The Terminal Service

The terminal service is implemented by the class txwerewolves.service.SSHService. The service is initialized by the txwerewolves.service.SSHService.startService() method which overrides twisted.application.service.startService(). A number of components need to be created to start and configure this service. Because this service uses SSH private-key / public-key authentication, the secret and public materials for the service must be read in from the file system. An instance of twisted.conch.ssh.factory.SSHFactory is created and configured with the key material. The factory will be responsible for creating an SSH protocol instance when a client connects to the service. We’ll see how this is configured later on when the endpoint for the service is created.

Because the service is authenticated, the Twisted Cred framework is used to provide authentication services. This means the service will require a portal, a realm, and at least one credential checker. The portal is a simple instance of twisted.cred.portal.Portal. The realm is an instance of txwerewolves.auth.SSHRealm. The portal is initialized with this realm.

The final component need for the authentication system is a credential checker. Fortunately, Twisted provides twisted.conch.checkers.SSHPublicKeyChecker. This checker needs to be initialized with a public key database. The game uses twisted.conch.checkers.InMemorySSHKeyDB to fill this need. The keys are read in from a simple JSON file of usernames and their public keys and stored in the database. The credential checker is contructed from the key database and it is registered with the portal. The portal is assigned to a property of the SSHFactory instance.

Finally, a Twisted endpoint is constructed from a string description and instructed to listen and use the factory to produce a protocol that will communicate with the connected client. Once a connection is made, the SSHFactory creates an instance of twisted.conch.ssh.transport.SSHServerTransport to communicate with the connected client.

The Terminal Realm and Avatar

When a client connects to the terminal service and successfully authenticates with the registered credential checker, the resulting avatar ID is passed to the SSHRealm instance that the portal was initialized with. The SSHServerTransport also requests the twisted.conch.interfaces.IConchUser interface [1], and it expects the realm to return an avatar that supports this interface.

The SSHRealm instance takes the avatar ID and registers it as in the game’s user database using txwerewolves.users.register_user(). If a current avatar exists for the user, it is shut down and a new txwerewolves.auth.SSHAvatar is created to replace it. The active avatar for the user is stored in the user database as a property of the user entry.

The new avatar is a subclass of twisted.conch.avatar.ConchUser, so it inherits much of the code required to communicate with a client terminal. Namely, it’s openShell() method will be called when the client requests a shell. The avatar will use a slightly modified twisted.conch.insults.insults.ServerProtcol instance to connect a txwerewolves.term.TerminalAdapterProtocol to the SSH protocol connected to the client terminal. The TerminalAdapterProtocol is a subclass of twisted.conch.insults.insults.TerminalProtocol so basic curses-style abstractions are available to the application code.

The terminal avatar delegates many of its functions to its terminal adapter. Initially, the avatar installs the default terminal application as a property of the user entry in the user database. The terminal adapter is also responsible for translating user input from the client into events that can be handled by the application protocol.

[1]Actually, this is rather simplified. SSHServerTransport actually calls on twisted.conch.ssh.userauth.SSHUserAuthServer to perform the user authentication.