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.
- 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
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
ssh.connect(server, username=username, allow_agent=True) #Connects to the local SSH agent and tries to obtain the private key
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:
server, port, username, password = (‘host’, 22, ‘username’, ‘password’)
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.
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.