Running Commands on a Remote Server

This lab demonstrates how to write an SSH client. You can use twisted.conch to communicate with a server using SSH: logging in, executing commands, and capturing the output.

10.4.1. How Do I Do That?

There are several classes that work together to make up a twisted.conch.ssh SSH client. The TRansport.SSHClientTransport class sets up the connection and verifies the identity of the server. The userauth.SSHUserAuthClient logs in using your authentication credentials. The connection.SSHConnection class takes over once you've logged in, and creates one or more channel.SSHChannel objects, which you then use to communicate with the server over a secure channel. Example 10-4 shows how you can use these classes to make an SSH client that logs into a server, runs a command, and prints the output.

Example 10-4.

from twisted.conch import error

from twisted.conch.ssh import transport, connection, keys, userauth, channel, common

from twisted.internet import defer, protocol, reactor

class ClientCommandTransport(transport.SSHClientTransport):

 def _ _init_ _(self, username, password, command):

 self.username = username

 self.password = password

 self.command = command

 def verifyHostKey(self, pubKey, fingerprint):

 # in a real app, you should verify that the fingerprint matches

 # the one you expected to get from this server

 return defer.succeed(True)

 def connectionSecure(self):


 PasswordAuth(self.username, self.password,


class PasswordAuth(userauth.SSHUserAuthClient):

 def _ _init_ _(self, user, password, connection):

 userauth.SSHUserAuthClient._ _init_ _(self, user, connection)

 self.password = password

 def getPassword(self, prompt=None):

 return defer.succeed(self.password)

class ClientConnection(connection.SSHConnection):

 def _ _init_ _(self, cmd, *args, **kwargs):

 connection.SSHConnection._ _init_ _(self)

 self.command = cmd

 def serviceStarted(self):

 self.openChannel(CommandChannel(self.command, conn=self))

class CommandChannel(channel.SSHChannel):

 name = 'session'

 def _ _init_ _(self, command, *args, **kwargs):

 channel.SSHChannel._ _init_ _(self, *args, **kwargs)

 self.command = command

 def channelOpen(self, data):


 self, 'exec', common.NS(self.command), wantReply=True).addCallback(


 def _gotResponse(self, _):


 def dataReceived(self, data):

 print data

 def closed(self):

 reactor.stop( )

class ClientCommandFactory(protocol.ClientFactory):

 def _ _init_ _(self, username, password, command):

 self.username = username

 self.password = password

 self.command = command

 def buildProtocol(self, addr):

 protocol = ClientCommandTransport(

 self.username, self.password, self.command)

 return protocol

if __name__ == "_ _main_ _":

 import sys, getpass

 server = sys.argv[1]

 command = sys.argv[2]

 username = raw_input("Username: ")

 password = getpass.getpass("Password: ")

 factory = ClientCommandFactory(username, password, command)

 reactor.connectTCP(server, 22, factory) )

Run with two arguments: a hostname and a command. It will ask for your username and password, log into the server, execute the command, and print the output. For example, you could run the who command to get a list of who's currently logged in to the server:

 $ python who

 Username: abe

 Password: password

 root pts/0 Jun 11 21:35 (

 phil pts/2 Jun 22 13:58 (

 phil pts/3 Jun 22 13:58 (


10.4.2. How Does That Work?

The ClientCommandTransport in Example 10-4 handles the initial connection to the SSH server. Its verifyHostKey method checks to make sure the server's public key matches your expectations. Typically, you'd remember each server the first time you connected, and then check on subsequent connections to make sure that another server wasn't maliciously trying to pass itself off as the server you expected. Here, it just returns a TRue value without bothering to check the key. The connectionSecure method is called as soon as the initial encrypted connection has been established. This is the appropriate time to send your login credentials, by passing a userauth.SSHUserAuthClient to self.requestService, along with a connection.SSHConnection object that should manage the connection after authentication succeeds.

The PasswordAuth inherits from userauth.SSHUserAuthClient. It has to implement only a single method, getPassword, which returns the password it will use to log in. If you wanted to use public key authentication, you'd implement the methods getPublicKey and getPrivateKey instead, returning the appropriate key as a string in each case.

The ClientConnection class in Example 10-4 will have its serviceStarted method called as soon as the client has successfully logged in. It calls self.openChannel with a CommandChannel object, which is a subclass of channel.SSHChannel. This object is used to work with an authenticated channel to the SSH server. Its channelOpen method is called when the channel is ready. At this point, you can call self.conn.sendRequest to send a command to the server. You have to encode data sent over SSH as a specially formatted network string; to get a string in this format, pass it to the twisted.conch.common.NS function. Set the keyword argument wantReply to TRue if you're interested in getting a response from the command; this setting will cause sendRequest to return a Deferred that will be called back when the command is completed. (If you don't set wantReply to true, sendRequest will return None.) As data is received from the server, it will be passed to dataReceived. Once you're done using the channel, close it by calling self.conn.sendEOF. The closed method will be called to let you know when the channel has been successfully closed.

Getting Started

Building Simple Clients and Servers

Web Clients

Web Servers

Web Services and RPC


Mail Clients

Mail Servers

NNTP Clients and Servers


Services, Processes, and Logging

show all menu

Twisted Network Programming Essentials
Twisted Network Programming Essentials
ISBN: 0596100329
EAN: 2147483647
Year: 2004
Pages: 107
Authors: Abe Fettig
Similar book on Amazon © 2008-2017.
If you may any questions please contact us: