Recipe 15.10. Performing Remote Logins with SSHCredit: Peter Cogolo, Anna Martelli Ravenscroft ProblemYou need to send commands, using the SSH protocol, to one or more logins that can be on a local machine or a remote machine. SolutionSSH is a secure replacement for the old Telnet protocol. One way to use SSH from a Python program is with the third-party paramiko package: # auto_ssh.py - remote control via ssh import os, sys, paramiko from getpass import getpass paramiko.util.log_to_file('auto_ssh.log', 0) def parse_user(user, default_host, default_port): ''' given name[@host[:port]], returns name, host, int(port), applying defaults for hose and/or port if necessary ''' if '@' not in user: return user, default_host, default_port user, host = user.split('@', 1) if ':' in host: host, port = host.split(':', 1) else: port = default_port return user, host, int(port) def autoSsh(users, cmds, host='localhost', port=22, timeout=5.0, maxsize=2000, passwords=None): ''' run commands for given users, w/default host, port, and timeout, emitting to standard output all given commands and their responses (no more than 'maxsize' characters of each response). ''' if passwords is None: passwords = { } for user in users: if user not in passwords: passwords[user] = getpass("Enter user '%s' password: " % user) for user in users: user, host, port = parse_user(user, default_host, default_port) try: transport = paramiko.Transport((host, port)) transport.connect(username=user, password=passwords[user]) channel = transport.open_session( ) if timeout: channel.settimeout(timeout) for cmd in cmd_list: channel.exec_command(cmd) response = channel.recv(max_size) print 'CMD %r(%r) -> %s' % (cmd, user, response) except Exception, err: print "ERR: unable to process %r: %s" % (user, err) if _ _name_ _ == '_ _main_ _': logname = os.environ.get("LOGNAME", os.environ.get("USERNAME")) host = 'localhost' port = 22 usage = """ usage: %s [-h host] [-p port] [-f cmdfile] [-c "command"] user1 user2 ... -c command -f command file -h default host (default: localhost) -p default host (default: 22) Example: %s -c "echo $HOME" %s same as: %s -c "echo $HOME" %s@localhost:22 """ % (sys.argv[0], sys.argv[0], logname, sys.argv[0], logname) import getopt optlist, user_list = getopt.getopt(sys.argv[1:], 'c:f:h:p:') if not user_list: print usage sys.exit(1) cmd_list = [ ] for opt, optarg in optlist: if opt == '-f': for r in open(optarg, 'rU'): if r.rstrip( ): cmd_list.append(r) elif opt == '-c': command = optarg if command[0] == '"' and command[-1] == '"': command = command[1:-1] cmd_list.append(command) elif opt == '-h': host = optarg elif opt == '-p': port = optarg else: print 'unknown option %r' % opt print usage sys.exit(1) autoSsh(user_list, cmd_list, host=host, port=port) DiscussionThe third-party extension paramiko package lets you easily automate access to all sorts of SSH services, even from non-Unix machines. paramiko even lets you write your own SSH servers in Python. In this recipe, however, we use paramiko on the client side, as a more secure alternative to the similar use of telnetlib shown previously in Recipe 15.9. Production code generally has to be made more robust, but this recipe should be enough to get you started in the right direction. The recipe's autoSsh function first ensures it knows passwords for all the users (asking interactively for the passwords of users it doesn't know about). Then, it loops over all the users, parsing strings such as foo@bar:2222 to mean user foo at host bar, port 2222, and defaulting the host and port values, if necessary. The loop body relies on two types of objects supplied by paramiko, transport and Channel. The transport is constructed by giving it the (host, port) pair and then a connection is made with a username and password. (Alternatively, depending on the SSH server, one might connect using a private key, but this recipe uses just a password.) The channel is obtained from the transport, and the recipe then sets a timeout (by default, 6 seconds) to ensure that no long-term hanging occurs in case of problems with an SSH server or the network path to it. Finally, an inner loop over all commands sends each command, receives a response (up to a maximum length in bytes, 2000 by default), and prints the command and response.
See Alsoparamiko's home page at http://www.lag.net/~robey/paramiko/; paramiko requires another third-party extension to Python, the Python Cryptography Toolkit, whose home page is at http://www.amk.ca/python/code/crypto; docs on SSH at http://www.openssh.com/, http://www.ucolick.org/~sla/ssh/, http://kimmo.suominen.com/docs/ssh/; Richard Silverman and Daniel J. Barrett, SSH: The Secure Shell, The Definitive Guide (O'Reilly); Recipe 15.9. |