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. |