def test_noRegeneration(self): """ L{keys._getPersistentRSAKey} will not regenerate the key if the key already exists. """ tempDir = FilePath(self.mktemp()) keyFile = tempDir.child("mykey.pem") key = keys._getPersistentRSAKey(keyFile, keySize=512) self.assertEqual(key.size(), 512) self.assertTrue(keyFile.exists()) keyContent = keyFile.getContent() # Set the key size to 1024 bits. Since it exists already, it will find # the 512 bit key, and not generate a 1024 bit key. key = keys._getPersistentRSAKey(keyFile, keySize=1024) self.assertEqual(key.size(), 512) self.assertEqual(keyFile.getContent(), keyContent)
def test_providedArguments(self): """ L{keys._getPersistentRSAKey} will put the key in C{directory}/C{filename}, with the key length of C{keySize}. """ tempDir = FilePath(self.mktemp()) keyFile = tempDir.child("mykey.pem") key = keys._getPersistentRSAKey(keyFile, keySize=512) self.assertEqual(key.size(), 512) self.assertTrue(keyFile.exists())
def test_keySizeZero(self): """ If the key generated by L{keys.getPersistentRSAKey} is set to None the key size should then become 0. """ tempDir = FilePath(self.mktemp()) keyFile = tempDir.child("mykey.pem") key = keys._getPersistentRSAKey(keyFile, keySize=512) key._keyObject = None self.assertEqual( key.size(), 0)
def setUp(self): if not ssh: raise unittest.SkipTest( "cryptography requirements missing, can't run historic " "recvline tests over ssh") u, p = b'testuser', b'testpass' rlm = TerminalRealm() rlm.userFactory = TestUser rlm.chainedProtocolFactory = lambda: insultsServer checker = checkers.InMemoryUsernamePasswordDatabaseDontUse() checker.addUser(u, p) ptl = portal.Portal(rlm) ptl.registerChecker(checker) sshFactory = ConchFactory(ptl) sshKey = keys._getPersistentRSAKey(filepath.FilePath(self.mktemp()), keySize=512) sshFactory.publicKeys[b"ssh-rsa"] = sshKey sshFactory.privateKeys[b"ssh-rsa"] = sshKey sshFactory.serverProtocol = self.serverProtocol sshFactory.startFactory() recvlineServer = self.serverProtocol() insultsServer = insults.ServerProtocol(lambda: recvlineServer) sshServer = sshFactory.buildProtocol(None) clientTransport = LoopbackRelay(sshServer) recvlineClient = NotifyingExpectableBuffer() insultsClient = insults.ClientProtocol(lambda: recvlineClient) sshClient = TestTransport(lambda: insultsClient, (), {}, u, p, self.WIDTH, self.HEIGHT) serverTransport = LoopbackRelay(sshClient) sshClient.makeConnection(clientTransport) sshServer.makeConnection(serverTransport) self.recvlineClient = recvlineClient self.sshClient = sshClient self.sshServer = sshServer self.clientTransport = clientTransport self.serverTransport = serverTransport return recvlineClient.onConnection
def conch_helper(endpoint, proto=None, namespace=dict(), keyDir=None, keySize=4096): """ Return a L{SSHKeyDirectory} based SSH service with the given parameters. Authorized keys are read as per L{SSHKeyDirectory} with ``baseDir`` being ``keyDir/users``. @param endpoint: endpoint for the SSH service @param namespace: the manhole namespace @param keyDir: directory that holds server/server.key file and users directory, which is used as ``baseDir`` in L{SSHKeyDirectory} @see: L{SSHKeyDirectory} """ if keyDir is None: from twisted.python._appdirs import getDataDirectory keyDir = getDataDirectory() keyDir = filepath.FilePath(keyDir) keyDir.child("server").makedirs(True) keyDir.child("users").makedirs(True) checker = SSHPublicKeyChecker(SSHKeyDirectory(keyDir.child("users"))) if proto is None: sshRealm = manhole_ssh.TerminalRealm() sshRealm.chainedProtocolFactory = chainedProtocolFactory(namespace) else: sshRealm = SSHSimpleRealm(proto) sshPortal = portal.Portal(sshRealm, [checker]) sshKeyPath = keyDir.child("server").child("server.key") sshKey = keys._getPersistentRSAKey(sshKeyPath, keySize) sshFactory = manhole_ssh.ConchFactory(sshPortal) sshFactory.publicKeys[b"ssh-rsa"] = sshKey sshFactory.privateKeys[b"ssh-rsa"] = sshKey sshService = strports.service(endpoint, sshFactory) return sshService
def makeService(options): """ Create a manhole server service. @type options: C{dict} @param options: A mapping describing the configuration of the desired service. Recognized key/value pairs are:: "telnetPort": strports description of the address on which to listen for telnet connections. If None, no telnet service will be started. "sshPort": strports description of the address on which to listen for ssh connections. If None, no ssh service will be started. "namespace": dictionary containing desired initial locals for manhole connections. If None, an empty dictionary will be used. "passwd": Name of a passwd(5)-format username/password file. "sshKeyDir": The folder that the SSH server key will be kept in. "sshKeyName": The filename of the key. "sshKeySize": The size of the key, in bits. Default is 4096. @rtype: L{twisted.application.service.IService} @return: A manhole service. """ svc = service.MultiService() namespace = options['namespace'] if namespace is None: namespace = {} checker = checkers.FilePasswordDB(options['passwd']) if options['telnetPort']: telnetRealm = _StupidRealm(telnet.TelnetBootstrapProtocol, insults.ServerProtocol, manhole.ColoredManhole, namespace) telnetPortal = portal.Portal(telnetRealm, [checker]) telnetFactory = protocol.ServerFactory() telnetFactory.protocol = makeTelnetProtocol(telnetPortal) telnetService = strports.service(options['telnetPort'], telnetFactory) telnetService.setServiceParent(svc) if options['sshPort']: sshRealm = manhole_ssh.TerminalRealm() sshRealm.chainedProtocolFactory = chainedProtocolFactory(namespace) sshPortal = portal.Portal(sshRealm, [checker]) sshFactory = manhole_ssh.ConchFactory(sshPortal) if options['sshKeyDir'] != "<USER DATA DIR>": keyDir = options['sshKeyDir'] else: from twisted.python._appdirs import getDataDirectory keyDir = getDataDirectory() keyLocation = filepath.FilePath(keyDir).child(options['sshKeyName']) sshKey = keys._getPersistentRSAKey(keyLocation, int(options['sshKeySize'])) sshFactory.publicKeys["ssh-rsa"] = sshKey sshFactory.privateKeys["ssh-rsa"] = sshKey sshService = strports.service(options['sshPort'], sshFactory) sshService.setServiceParent(svc) return svc
def getManholeFactory(namespace, user, secret, keydir=None): """ Get an administrative manhole into the application. :param namespace: the namespace to show in the manhole :type namespace: dict :param user: the user to authenticate into the administrative shell. :type user: str :param secret: pass for this manhole :type secret: str """ import string from twisted.cred import portal from twisted.conch import manhole, manhole_ssh from twisted.conch import recvline from twisted.conch.insults import insults from twisted.conch.ssh import keys from twisted.cred.checkers import (InMemoryUsernamePasswordDatabaseDontUse as MemoryDB) from twisted.python import filepath try: from IPython.core.completer import Completer except ImportError: from rlcompleter import Completer class EnhancedColoredManhole(manhole.ColoredManhole): """ A nicer Manhole with some autocomplete support. See the patch in https://twistedmatrix.com/trac/ticket/6863 Since you're reading this, it'd be good if *you* can help getting that patch into twisted :) """ completion = True def handle_TAB(self): """ If tab completion is available and enabled then perform some tab completion. """ if not self.completion: recvline.HistoricRecvLine.handle_TAB(self) return # If we only have whitespace characters on this line we pass # through the tab if set(self.lineBuffer).issubset(string.whitespace): recvline.HistoricRecvLine.handle_TAB(self) return cp = Completer(namespace=self.namespace) cp.limit_to__all__ = False lineLeft, lineRight = self.currentLineBuffer() # Extract all the matches matches = [] n = 0 while True: match = cp.complete(lineLeft, n) if match is None: break n += 1 matches.append(match) if not matches: return if len(matches) == 1: # Found the match so replace the line. This is apparently how # we replace a line self.handle_HOME() self.terminal.eraseToLineEnd() self.lineBuffer = [] self._deliverBuffer(matches[0] + lineRight) else: # Must have more than one match, display them matches.sort() self.terminal.write("\n") self.terminal.write(" ".join(matches)) self.terminal.write("\n\n") self.drawInputLine() def keystrokeReceived(self, keyID, modifier): """ Act upon any keystroke received. """ self.keyHandlers.update({'\b': self.handle_BACKSPACE}) m = self.keyHandlers.get(keyID) if m is not None: m() elif keyID in string.printable: self.characterReceived(keyID, False) class chainedProtocolFactory: def __init__(self, namespace): self.namespace = namespace def __call__(self): return insults.ServerProtocol(EnhancedColoredManhole, self.namespace) sshRealm = manhole_ssh.TerminalRealm() sshRealm.chainedProtocolFactory = chainedProtocolFactory(namespace) checker = MemoryDB(**{user: secret}) sshPortal = portal.Portal(sshRealm, [checker]) sshFactory = manhole_ssh.ConchFactory(sshPortal) if not keydir: from twisted.python._appdirs import getDataDirectory keydir = getDataDirectory() keyLocation = filepath.FilePath(keydir).child('id_rsa') sshKey = keys._getPersistentRSAKey(keyLocation, 4096) sshFactory.publicKeys[b"ssh-rsa"] = sshKey sshFactory.privateKeys[b"ssh-rsa"] = sshKey return sshFactory