Day 12–101 Days of DevOps — Python Paramiko module

Prashant Lakhera
5 min readJul 12, 2021

--

Welcome to Day 12 of 101 Days of DevOps. The topic for today is Python paramiko module.

To view the complete course, please check the below url.

For more info, register via the below link

YouTube Channel link

According to the Paramiko official documentation

Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol [1], providing both client and server functionality. While it leverages a Python C extension for low level cryptography (Cryptography), Paramiko itself is a pure Python interface around SSH networking concepts.

Let try to understand this in simple terms. Sometimes you need to access a remote server to execute some command or to perform file transfer without having to login into it, this is where we can use paramiko module.

Paramiko is a third-party module so you first need to install it which you can do simply with the help of pip.

pip3 install paramiko

Now in order to connect to the remote machine, you have two options

  • Using username and password
  • Using the username and cryptographic key

In this blog, we are going to explore both of these options.

In order to connect to a remote server, we need to follow the series of steps

Step1: Import paramiko module

import paramiko

Step2: Create ssh client and connect to the remote machine.

ssh = paramiko.SSHClient()
ssh.connect(hostname='<ip of remote server>',username='centos',password='<password>')

Step3: Next step is to execute the command. In order to execute the command, we are going to use exec_command to the ssh client. This command is going to return a tuple(stdin,stdout,stderr).

  • stdin: when your command requiring some input(e.g. sudo ls)
  • stdout: like in the below example when you are going to get the output of the executed command
  • stderr: When the executed command returns an error
stdin,stdout,stderr = ssh.exec_command('free -m')

Step4: As mentioned above, to get the output

print(stdout.readlines()

Step5: Finally close the connection

ssh.close()

At this stage, our code will look like this

import paramiko
ssh = paramiko.SSHClient()
ssh.connect(hostname='<ip address of remote machine',username='centos',password='<password of remote server>')
stdin,stdout,stderr = ssh.exec_command('free -m')
print(stdout.readlines()
ssh.close()

If you store this code, let say in paramiko_test.py and try to execute it, you will get the below error

python3 paramiko_test.py
Traceback (most recent call last):
File "paramiko_test.py", line 3, in <module>
ssh.connect(hostname='44.X.X.X',username='centos',password='password')
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/client.py", line 415, in connect
self._policy.missing_host_key(
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/client.py", line 823, in missing_host_key
raise SSHException(
paramiko.ssh_exception.SSHException: Server '44.X.X.X' not found in known_hosts

Now try to ssh to the remote machine for the first time

ssh centos@44.X.X.XThe authenticity of host '44.X.X.X (44.X.X.X)' can't be established.ECDSA key fingerprint is SHA256:ad+57OV3vNMVlixOo5JdzOH91E3/RX/laqZaSkF4a0w.Are you sure you want to continue connecting (yes/no/[fingerprint])?

The exception you are getting because from the machine where you are trying to access the remote host doesn’t trust the remote server. In the case of ssh, you need to enter yes in order to trust it, similarly in the case of paramiko you need to add ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()). By default, paramiko client rejects the unknown host or key.

class paramiko.client.RejectPolicyPolicy for automatically rejecting the unknown hostname & key. This is used by SSHClient.

In order for the new client to automatically adding the hostname and new host key to the local Hostkey object, you need to use paramiko.AutoAddPolicy().

class paramiko.client.AutoAddPolicyPolicy for automatically adding the hostname and new host key to the local HostKeys object, and saving it. This is used by SSHClient.

For more info please refer to

After updating that our code will look like this

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='<ip address of remote machine',username='centos',password='<password of remote server>')
stdin,stdout,stderr = ssh.exec_command('free -m')
print(stdout.readlines()
ssh.close()

If you try to execute the code again, I am getting desired output but with this exception.

python3 paramiko_test.py
[' total used free shared buff/cache available\n', 'Mem: 3741 556 2811 22 373 2948\n', 'Swap: 2047 0 2047\n']
Exception ignored in: <function BufferedFile.__del__ at 0x1104ff790>
Traceback (most recent call last):
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/file.py", line 66, in __del__
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/channel.py", line 1392, in close
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/channel.py", line 991, in shutdown_write
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/channel.py", line 967, in shutdown
File "/Users/plakhera/Documents/boto3/venv/lib/python3.8/site-packages/paramiko/transport.py", line 1846, in _send_user_message
AttributeError: 'NoneType' object has no attribute 'time'

and the solution as suggested in the StackOverflow post is to add more time to process the command. You need to import the time module and add the sleep of 5 sec or depending upon your requirement.

import paramiko
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='44.X.X.X',username='centos',password='password')
stdin,stdout,stderr = ssh.exec_command('free -m')
time.sleep(5)
print(stdout.readlines())
ssh.close()

If you execute your code again, this time it works perfectly fine after adding the sleep parameter.

python3 paramiko_test.py
[' total used free shared buff/cache available\n', 'Mem: 3741 556 2811 22 373 2949\n', 'Swap: 2047 0 2047\n']

Now hardcoding the password in a file is bad security practice, so we are going to use the os module and export the password as an environment variable.

export PASSWORD='<your password>'

In order to use it in your code

import os

password=os.environ.get('PASSWORD=')
ssh.connect(hostname='44.X.X.X',username='centos',password=password)

We already discussed in depth the use of the os module on Day 1 https://www.101daysofdevops.com/courses/101-days-of-devops/lessons/day-1-python-os-module/

Now our code will look like this

import paramiko
import time
import os

password=os.environ.get('PASSWORD')

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='44.X.X.X',username='centos',password=password)
stdin,stdout,stderr = ssh.exec_command('free -m')
time.sleep(5)
print(stdout.readlines())
ssh.close()

If you re-execute your code, there is no change in terms of output but now you are following the good security practice.

python3 paramiko_test.py
[' total used free shared buff/cache available\n', 'Mem: 3741 560 2807 22 374 2945\n', 'Swap: 2047 0 2047\n']

The above code works fine. But nowadays we are mostly dealing with cloud servers where we need to enter the username and key. In order to connect to a cloud server using a key, you need to make a minor modification to your code. Rather than specify the password, specify the location of the key, and the rest of the code remain the same.

ssh.connect(hostname='<ip of remote server>',username='centos',key_filename='<location of key>')
  • You can use paramiko to download and upload the file by calling open_sftp().

Downloading the file

ftp_client=ssh.open_sftp()
ftp_client.get('myls.py','/tmp/myls.py')
ftp_client.close()

Uploading a file

ftp_client=ssh.open_sftp()
ftp_client.put('myls.py','myls.py')
ftp_client.close()

Your complete code will look like this

import paramiko
import time
import os

password = os.environ.get('PASSWORD')

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='44.X.X.X',username='centos',password=password)

ftp_client=ssh.open_sftp()
ftp_client.put('myls.py','myls.py')
ftp_client.get('myls.py','/tmp/myls.py')

ftp_client.put('myls.py','myls.py')
ftp_client.close()

GitHub link: https://github.com/100daysofdevops/100daysofdevops/tree/master/paramiko

I am looking forward to you guys joining the amazing journey.

--

--

Prashant Lakhera
Prashant Lakhera

Written by Prashant Lakhera

AWS Community Builder, Ex-Redhat, Author, Blogger, YouTuber, RHCA, RHCDS, RHCE, Docker Certified,4XAWS, CCNA, MCP, Certified Jenkins, Terraform Certified, 1XGCP

No responses yet