SSH with Twisted - Running Commands on a Remote Server (Page 5 of 5 )
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.
How Do I Do That?
There are several classes that work together to make up a twisted.conch.ssh SSH client. The transport.SSHClientTransportclass sets up the connection and verifies the identity of the server. Theuserauth.SSHUserAuthClientlogs in using your authentication credentials. Theconnection.SSHConnectionclass takes over once you’ve logged in, and creates one or morechannel.SSHChannelobjects, 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. sshclient.py
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):
self.requestService(
PasswordAuth(self.username, self.password,
ClientConnection(self.command)))
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.conn.sendRequest(
self, 'exec', common.NS(self.command), wantReply=True).addCallback(
self._gotResponse)
def _gotResponse(self, _):
self.conn.sendEOF(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)
reactor.run()
Run sshclient.py 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 sshclient.py myserver.example.com who
Username: abe
Password: password
root pts/0 Jun 11 21:35 (192.168.0.13)
phil pts/2 Jun 22 13:58 (192.168.0.1)
phil pts/3 Jun 22 13:58 (192.168.0.1)
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.
ThePasswordAuthinherits fromuserauth.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 methodsgetPublicKeyandgetPrivateKeyinstead, returning the appropriate key as a string in each case.
TheClientConnectionclass in Example 10-4 will have itsserviceStartedmethod called as soon as the client has successfully logged in. It callsself.openChannelwith aCommandChannel object, which is a subclass ofchannel.SSHChannel. This object is used to work with an authenticated channel to the SSH server. ItschannelOpenmethod is called when the channel is ready. At this point, you can callself.conn.sendRequestto 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 thetwisted.conch.common.NSfunction. Set the keyword argumentwantReplytoTrueif you’re interested in getting a response from the command; this setting will causesendRequestto return aDeferredthat will be called back when the command is completed. (If you don’t setwantReplytoTrue,sendRequestwill returnNone.) As data is received from the server, it will be passed todataReceived. Once you’re done using the channel, close it by callingself.conn.sendEOF. Theclosed method will be called to let you know when the channel has been successfully closed.
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |
|
This article is excerpted from chapter 10 of the book Twisted Network Programming Essentials, written by Abe Fettig (O'Reilly, 2007; ISBN: 0596100329). Check it out today at your favorite bookstore. Buy this book now.
|
|