sshserver.py will run an SSH server on port 2222. Connect to this server with an SSH client using the username admin and password aaa, and try typing some commands: $ ssh admin@localhost -p 2222 >>> Welcome to my test SSH server. Connection to localhost closed. How Does That Work? The SSHDemoProtocol class in Example 10-1 inherits fromtwisted.conch.recvline.HistoricRecvline.HistoricRecvLineis a protocol with built-in features for building command-line shells. It gives your shell features that most people take for granted in a modern shell, including backspacing, the ability to use the arrow keys to move the cursor forwards and backwards on the current line, and a command history that can be accessed using the up and down arrows.twisted.conch.recvlinealso provides a plainRecvLineclass that works the same way, but without the command history. ThelineReceivedmethod inHistoricRecvLineis called whenever a user enters a line. Example 10-1 shows how you might override this method to parse and execute commands. There are a couple of differences betweenHistoricRecvLineand a regularProtocol, which come from the fact that withHistoricRecvLineyou’re actually manipulating the current contents of a user’s terminal window, rather than just printing out text. To print a line of output, useself.terminal.write; to go to the next line, useself.nextLine. Thetwisted.conch.avatar.ConchUserclass represents the actions available to an authenticated SSH user. By default,ConchUser doesn’t allow the client to do anything. To make it possible for the user to get a shell, make his avatar implementtwisted.conch.interfaces.ISession. TheSSHDemoAvatarclass in Example 10-1 doesn’t actually implement all ofISession; it only implements enough for the user to get a shell. TheopenShellmethod will be called with atwisted.conch.ssh.session. SSHSessionProcessProtocolobject that represents the encrypted client’s end of the encrypted channel. You have to perform a few steps to connect the client’s protocol to your shell protocol so they can communicate with each other. First, wrap your protocol class in atwisted.conch.insults.insults.ServerProtocolobject. You can pass extra arguments toinsults.ServerProtocol, and it will use them to initialize your protocol object. This sets up your protocol to use a virtual terminal. Then usemakeConnectionto connect the two protocols to each other. The client’s protocol actually expectsmakeConnectionto be called with a an object implementing the lower-leveltwisted.internet.interfaces.ITransportinterface, not aProtocol; thetwisted.conch.session.wrapProtocolfunction wraps aProtocolin a minimalITransportinterface.
To make a realm for your SSH server, write a class that has arequestAvatar method. The SSH server will callrequestAvatarwith the username asavatarIdandtwisted.conch.interfaces.IAvataras one of the interfaces. Return your subclass oftwisted.conch. avatar.ConchUser. There’s only one more thing you’ll need to have a complete SSH server: a unique set of public and private keys. Example 10-1 demonstrates how you can use theCrypto.PublicKey.RSAmodule to generate these keys.RSA.generatetakes a key length as the first argument and an entropy-generating function as the second argument; thetwisted.conch.ssh.commonmodule provides theentropy.get_bytesfunction for this purpose.RSA.generatereturns aCrypto.PublicKey.RSA.RSAobjobject. You extract public and private key strings from theRSAobj by passing it to thegetPublicKeyStringandgetPrivateKeyStringfunctions from thetwisted.conch.ssh.keysmodule. Example 10-1 saves its keys to disk after generating them the first time it runs: you need to keep these keys preserved between clients so clients can identify and trust your sever.
To run the SSH server, create a twisted.conch.ssh.factory.SSHFactory object. Set itsportalattribute to a portal using your realm, and register a credentials checker that can handletwisted.cred.credentials.IUsernamePasswordcredentials. Set theSSHFactory’spublicKeysattribute to a dictionary that matches encryption algorithms to key string objects. To get the RSA key string object, pass your public key as thedatakeyword tokeys.getPublicKeyString. Then set theprivateKeysattribute to a dictionary that matches protocols to key objects. To get the RSA private key object, pass your private key as thedata keyword tokeys.getPrivateKey. BothgetPublicKeyString andgetPrivateKeycan take a filename keyword instead, to load a key directly from a file. Once theSSHFactoryhas the keys, it’s ready to go. Callreactor.listenTCPto have it start listening on a port and you’ve got an SSH server.
blog comments powered by Disqus |