How to ssh in python using Paramiko?

If you have ever agonized over connecting and communicating with a remote machine in python, give Paramiko a go.  Paramiko is most helpful for cases where one needs to securely communicate and exchange data,  execute commands on remote machines, handle connect requests from remove machines or access ssh services like sftp. As described in the paramiko’s homepage

“Paramiko is a module for python 2.2 (or higher) that implements the SSH2 protocol for secure (encrypted and authenticated) connections to remote machines.”

Paramiko: Dependencies & Installation

The version I’m using is 1.7.5.  paramiko is written purely in python and the only dependency for it is pycrypto 1.9+.
I installed using easy_install


sudo easy_install paramiko

On ubuntu, paramiko can be installed by


sudo apt-get paramiko

The rpm equivalent is also available.

Another option is to download the source code/module from the parent site and install using ‘


wget http://www.lag.net/paramiko/download/paramiko-1.7.5.zip unzip paramiko-1.7.5.zip cd paramiko-1.7.5 python setup.py install

To use paramiko effectively, We will need to understand the basics of ssh.

SSH and Security

SSH, or Secure SHell, as a network protocol provides mechanism for authenticated and encrypted connections between remote machines over unsecure network.  Before ssh was employed, telnet and rcp protocols exchanged authentication information as plaintext over unsecure network, while establishing the connection.   SSH solved this by using encrypted communication even while establishing the connection, effectively replacing other network protocols.

SSH provides

  • Encryption of all communication between the two entities with a several cipher algorithms to choose from
  • Authentication using password or a public key or using both options as a two-factor authentication
  • Ensuing integrity of data is by creating a digital signature of the data transferred from one entity to another.  There are multiple message authentication algorithms  to choose from the signature creation.

Connecting Using Paramiko

Paramiko supports both password based authentication and public key based authentication. Paramiko’s API facilitate both high level and low level control over the ssh connection.

The simplest and probably the easiest way to connect to a remote machine using Paramiko is the SSHClient object.  It is a high-level representation of a session with an SSH server. This class incorporates Transport, Channel, and SFTPClient to provide simpler authentication and connection apis.

The ssh server will be verified by the host keys loaded from the user’s local ssh’s known_hosts file.  In case of failure to verify, the default policy is to reject the server’s keys and raise an SSHException.  Here I’m overriding it with the AutoAddPolicy wherein the new server will be automatically added to the list of known hosts. Its also possible to define our own policies on how to handle unidentified servers.

Authentication of the client is attempted in the following order of priority:

  • The pkey or key_filename passed in (if any)
  • Any key we can find through an SSH agent
  • Any “id_rsa” or “id_dsa” key discoverable in ~/.ssh/
  • Plain username/password auth, if a password was given

Using Paramiko’s SSHClient

In the below example I’m using the password to authenticate.


import os import paramiko server, username, password = ('host', 'username', 'password') ssh = paramiko.SSHClient() parmiko.util.log_to_file(log_filename) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #In case the server's key is unknown, #we will be adding it automatically to the list of known hosts ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts"))) #Loads the user's local known host file. ssh.connect(server, username=username, password=password) ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command('ls /tmp') print "output", ssh_stdout.read() #Reading output of the executed command error = ssh_stderr.read() #Reading the error stream of the executed command print "err", error, len(error) #Transfering files to and from the remote machine sftp = ssh.open_sftp() sftp.get(remote_path, local_path) sftp.put(local_path, remote_path) sftp.close() ssh.close()

The ‘connect’ method also allows you to provide your own private key, or connect to the SSH agent on the local machine or read from the user’s local key files. Click here for a more detailed description of  the ‘connect‘ method’s signature.


privkey = paramiko.RSAKey.from_private_key_file (path_to_priv_key_file) ssh.connect(server, username=username,pkey=privkey ) #private key to use for authentication

or


ssh.connect(server, username=username, key_filename=path_to_priv_key_file) #the filename, or list of filenames, of optional private key(s) #to try for authentication

or


ssh.connect(server, username=username, allow_agent=True) #Connects to the local SSH agent and tries to obtain the private key

or


ssh.connect(server, username=username, look_for_keys=True) #Searches for discoverable private key files in ~/.ssh/

The Transport Object

It provides more direct control over how the connections are formed and authentication is carried over.  It provides for various  options such as logging debugging information like hex dump, manipulating the algorithms used and their priorities, controlling the authentication methods and their sequence, the type of channels to open, forcing renegotiating keys etc.  It is also possible to start an SSH Server or SFTP Server using this object.

The below example uses the Transport Object to connect:

import os
import paramiko
server, port, username, password = (‘host’, 22, ‘username’, ‘password’)
parmiko.util.log_to_file(log_filename)
nbytes = 100

An SSH Transport attaches to a stream (usually a socket), negotiates an encrypted session, authenticates, and then creates stream tunnels, called Channels, across the session. Multiple channels can be multiplexed across a single session (and often are, in the case of port forwardings).


trans = paramiko.Transport((host, port)) trans.connect(username = username, password = password)

Next, after authentication, we create a channel of type “session”


session = trans.open_channel("session") #Once the channel is established, we can execute only one command.  To execute another command, we need to create another channel session.exec_command('ls /tmp')

We need to wait till the command is executed or the channel is closed.  recv_exit_status return the result of the exit status of the command executed or -1 if no exit status was given.


exit_status = session.recv_exit_status()

Now the command execution is over and stdout and stderr streams will be linked to the channel and can be read.


stdout_data = [] stderr_data = [] while session.recv_ready(): stdout_data.append(session.recv(nbytes)) stdout_data = "".join(stdout_data) while session.recv_stderr_ready(): stderr_data.append(session.recv_stderr(nbytes)) stderr_data = "".join(stderr_data) print "exit status", exit_status print "output" print stdout_data print "error" print stderr_data

We can also create SFTP client from the Transport Object as below


sftp = paramiko.SFTPClient.from_transport(trans) sftp.get('remote_path', 'local_path') sftp.put('local_path', 'remote_path') sftp.close()

SFTP client object. SFTPClient is used to open an sftp session across an open ssh Transport and do remote file operations.

Personally I prefer to write a wrapper module over the SSHClient api and use that in my day to day needs.

Caveats

Paramiko’s SFTPClient is significantly slower compared to sftp or scp, some times by an order of magnitude, especially for huge files.  The scp implementation listed in the ‘Interesting Read’ below assures to be faster though I’ve not tested it yet.

Paramiko’s SSHClient does not allow for setting a timeout for exec_command.  This means that, in case of the remote_machine not returning the exec_command call, the process would freeze and need to be killed.

Interesting Read

SSH Programming with Paramiko | Completely Different Recipe 576810: Copy files over SSH using paramiko Paramiko Home Implementing SCP in Paramiko

4 comments

  1. Thanks so much for this write-up! The lack of use-cases in the documentation makes it a bit difficult to figure out.

    Reply

  2. There is an error in Using Paramiko’s SSHClient on the line:

    parmiko.util.log_to_file

    Reply

  3. parmiko.util.log_to_file should be paramiko.util.log_to_file

    minor typo. Thanks for the blogpost, very useful.

    Reply

  4. Hi,
    I am trying to to ssh into server using python paramiko package.
    when I tried to ssh into server using “pem” key then it worked but when I tried it by taking private key content in a string it shows error.
    below are the code and output :

    import paramiko
    import StringIO
    content=”—–BEGIN RSA PRIVATE KEY—– MIIEowIBAAKCAQE —-whatever content”
    private_key = StringIO.StringIO(content)
    k = paramiko.RSAKey.from_private_key(private_key)
    c = paramiko.SSHClient()
    c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    host=”XX.XX.XX.XXX”
    c.connect( hostname = host,username=”ec2-user”, pkey = k )
    print “Connected to ” + host`

    output:
    Traceback (most recent call last):
    File “one.py”, line 6, in
    k = paramiko.RSAKey.from_private_key(private_key)
    File “/home/ec2-user/abc/local/lib/python2.7/site-packages/paramiko/pkey.py”, line 217, in from_private_key
    key = cls(file_obj=file_obj, password=password)
    File “/home/ec2-user/abc/local/lib/python2.7/site-packages/paramiko/rsakey.py”, line 42, in __init__
    self._from_private_key(file_obj, password)
    File “/home/ec2-user/abc/local/lib/python2.7/site-packages/paramiko/rsakey.py”, line 167, in _from_private_key
    data = self._read_private_key(‘RSA’, file_obj, password)
    File “/home/ec2-user/abc/local/lib/python2.7/site-packages/paramiko/pkey.py”, line 277, in _read_private_key
    raise SSHException(‘not a valid ‘ + tag + ‘ private key file’)
    paramiko.ssh_exception.SSHException: not a valid RSA private key file

    Can anyone suggest what could be the problem?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *