Python Module for Remote SSH Connections:

To connect remote server or machine which is running with SSH service there is a Python Module called Paramiko. Find the details below.

About Paramiko

Paramiko is a pure-Python module and can be easy_install’ed as other typical python modules can. However, PyCrypto is written largely in C, so you may need a compiler to install both depending on your platform.

Paramiko itself has extensive API documentation and an active mailing list. As an added bonus, there’s a Java port of it as well (don’t get me started on controlling SSH within Java) if you need something to achieve the same thing in Java.

Paramiko also offers an implementation of the SSH and SFTP server protocols. It really is feature-rich and complete. I’ve used it in heavily threaded applications as well as in day-to-day maintenance scripts. There’s even an installation and deployment system, named Fabric, that further builds on Paramiko to provide application deployment utilities via SSH.

Getting started

The primary class of the Paramiko API is ”paramiko.SSHClient”. It provides the basic interface you are going to want to use to instantiate server connections and file transfers.

Here’s a simple example:

import paramiko
ssh = paramiko.SSHClient()
ssh.connect('', username='jesse', 

This creates a new SSHClient object, and then calls ”connect()” to connect us to the local SSH server. It can’t get much easier than that!

Host Keys

One of the complicating aspects of SSH authentication is host keys. Whenever you make an ssh connection to a remote machine, that host’s key is stored automatically in a file in your home directory called ”.ssh/known_hosts”. If you’ve ever connected to a new host via SSH and seen a message like this:

The authenticity of host 'localhost (::1)' can't be
RSA key fingerprint is 
Are you sure you want to continue connecting 

and typed “yes” — you’ve added an entry to the ”known_hosts” file. These keys are important because accepting them implies a level of trust of the host. If the key ever changes or is compromised in some way, your client will refuse to connect without notifying you.

Paramiko enforces this same rule. You must accept and authorize the use and storage of these keys on a per-host basis. Luckily, rather then having to be prompted for each one, or manage each one individually, you can set a magic policy.

The default behavior with an SSHClient object is to refuse to connect to a host (”paramiko.RejectPolicy”) who does not have a key stored in your local ”known_hosts” file. This can become annoying when working in a lab environment where machines come and go and have the operating system reinstalled constantly.

Setting the host key policy takes one method call to the ssh client object (”set_missing_host_key_policy()”), which sets the way you want to manage inbound host keys. If you’re lazy like me, you pass in the ”paramiko.AutoAddPolicy()” which will auto-accept unknown keys.

import paramiko
ssh = paramiko.SSHClient()
ssh.connect('', username='jesse', 

Of course, don’t do this if you’re working with machines you don’t know or trust! Tools built on Paramiko should make this overly liberal policy a configuration option.

Running Simple Commands

So, now that we’re connected, we should try running a command and getting some output.

SSH uses the same type of input, output, and error handles you should be familiar with from other Unix-like applications. Errors are sent to standard error, output goes to standard out, and if you want to send data back to the application, you write it to standard in.

So, the response data from client commands are going to come back in a tuple – (stdin, stdout, stderr) – which are file-like objects you can read from (or write to, in the case of stdin). For example:

>>> ssh.connect('', username='jesse', 
...    password='lol')
>>> stdin, stdout, stderr = \
...    ssh.exec_command("uptime")
>>> type(stdin)

>>> stdout.readlines()
['13:35  up 11 days,  3:13, 4 users, load averages: 0.14 0.18 0.16\n']

Under the covers, Paramiko has opened a new ”paramiko.Channel” object which represents the secure tunnel to the remote host. The Channel object acts like a normal python socket object. When we call ”exec_command()”, the Channel to the host is opened, and we are handed back ”paramiko.ChannelFile” “file-like” objects which represents the data sent to and from the remote host.

One of the documented nits with the ChannelFile objects paramiko passes back to you is that you need to constantly ”read()” off of the stderr and stdout handles given back to you. If the remote host sends back enough data to fill the buffer, the host will hang waiting for your program to read more. A way around this is to either call ”readlines()” as we did above, or ”read()”. If you need to internally buffer the data, you can also iterate over the object with ”readline()”.

This is the simplest form of connecting and running a command to get the output back. For many sysadmin tasks, this will be invaluable as you need to parse the output of a returned command to find exactly what you need. With Python’s rich string manipulation, this is an easy task. Let’s run something with a lot of output, that also requires a password:

ssh.connect('', username='jesse', 
stdin, stdout, stderr = ssh.exec_command(
   "sudo dmesg")

Uh oh. I just called the sudo command. It is going to require me to provide a password interactively with the remote host. No worries:

ssh.connect('', username='jesse', 
stdin, stdout, stderr = ssh.exec_command(
    "sudo dmesg")
data =
for line in data:
    if line.split(':')[0] == 'AirPort':
        print line

There! I logged in remotely and found all messages for my Airport card. The key thing to note here is that I wrote my password to the stdin “file” so that sudo allowed me in.

If you’re wondering, yes, this provides an easy base to create your own interactive shell. You might want to do something like this to make a little custom admin shell using the Python cmd module to administer machines inside of your lab.

File put and get

File manipulation within Paramiko is handled via the SFTP implementation, and, like the ssh client command execution, it’s easy as pie.

We start by instantiating a new paramiko.SSHClient just as before:

import paramiko
ssh = paramiko.SSHClient()
ssh.connect('', username='jesse', 

This time, we make a call into ”open_sftp()” after we perform the connect to the host. ”open_sftp()” returns a ”paramiko.SFTPClient” client object that supports all of the normal sftp operations (stat, put, get, etc.). In this example, we perform a “get” operation to download the file ”” from the remote system and write it to to the local file, ””.

ftp = ssh.open_sftp() ftp.get('', '') ftp.close()

Writing a file to the remote host (a “put” operation) works the exact same way. We just transpose the local and remote arguments:

ftp = ssh.open_sftp()
ftp.get('', '')

The nice thing about the sftp client implementation that Paramiko provides is that it support things like stat, chmod, chown, etc. Obviously these might act differently depending on the remote server because some servers do not implement all of the protocol, but even so they’re incredibly useful.

You could easily write functions like ”glob.glob()” to transverse a remote directory tree looking for a particular filename pattern. You could also search based on permissions, size, etc.

One thing to note, however, and this bit me a few times: sftp as a protocol is slightly more restrictive than something like normal secure copy (scp). SCP allows you to use Unix wild cards in the file name when grabbing a file from the remote machine. SFTP, on the other hand, expects the full explicit path to the file you want to download. An example of this is:

ftp.get('*.py', '.')

In most cases, this would mean “download all files with .py” to the local directory on my machine. SFTP is unhappy with this formulation, though (see Listing 2). I learned this the hard way, after I spent several hours pulling apart the sftp client implementation out of frustration.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s