7 months, 3 weeks

Connecting to Network Devices via SSH Using Paramiko




 

When administrating IT devices from a remote location, be it network equipment or servers, SSH has become the standard. With its secure transport and various authentication methods, it's a safe choice that is widely used to this day to administer and configure servers or network devices. It is thus only natural when getting started with programmability and network automation, to find a way of issuing SSH commands not by hand but from a script. With this, you can take a sequence of commands you used to type into the device by hand and execute them programmatically on one or more devices. The last part is crucial. With a script that executes commands for you, you can easily apply the same sequence of commands to another device.

The Python community has already developed an SSH client library that is available for our use, called Paramiko. Paramiko is a pure-Python (2.7, 3.4+) implementation of the SSHv2 protocol, providing both client and server functionality. Direct use of Paramiko itself is only intended for users who need advanced/low-level primitives or want to run an in-Python sshd.

Install the Paramiko package:  python3 -m pip install paramiko

We will also specify the host, username, and password in variables and then initiate a connection to 
the specified host:
1. Import the Paramiko library:  from paramiko.client import SSHClient

2. Specify the host, username, and password. You can name these variables however you like.

SSH_USER = "<Insert your ssh user here>"
SSH_PASSWORD = "<Insert your ssh password here>"
SSH_HOST = "<Insert the IP/host of your device/server here>"
SSH_PORT = 22 # Change this if your SSH port is different

3. Create an SSHClient object, which we just imported from Paramiko:

client = SSHClient()                                                                                                      

4.Before actually connecting, we will need to make sure that our client knows the host keys: 
client.load_system_host_keys()
try:                                                                                     
   client.connect(SSH_HOST, port=SSH_PORT,  username=SSH_USER, password=SSH_PASSWORD,  look_for_keys=False)
   print("Connected successfully!")
except Exception:                                                                
   print("Failed to establish connection.")                            
finally:                                                                                  
 client.close()                                                                       

The way Paramiko handles unknown host keys can be specified using a policy. One of these policies, AutoAddPolicy, allows us to just add unknown host keys to our scripts set of host keys:

from paramiko.client import SSHClient, AutoAddPolicy
SSH_USER = "<Insert your ssh user here>"
SSH_PASSWORD = "<Insert your ssh password here>"
SSH_HOST = "<Insert the IP/host of your device/server here>"
SSH_PORT = 22 # Change this if your SSH port is different
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(SSH_HOST, port=SSH_PORT, username=SSH_USER, password=SSH_PASSWORD)

The preceding code will automatically add these host keys. Be aware that this might be a potential security risk since you are not verifying that the host you are connecting to is the one you connected to last time.

It's better to use a dedicated password prompt that hides what you have typed so that someone looking over your console history can't retrieve your password. For this purpose, Python has the built-in getpass module.

Follow these steps to retrieve the configuration variables necessary, not as static information in the script but rather interactively from the user using a combination of input and the getpass module:

import getpass                                                                                              
SSH_PASSWORD = getpass.getpass(prompt='Password: ', stream=None)
SSH_USER = input("Username: ")
SSH_HOST = input("Host: ")
SSH_PORT = int(input("Port: "))

client.load_system_host_keys()
client.connect(SSH_HOST, port=SSH_PORT, username=SSH_USER, password=SSH_PASSWORD)

Executing a command will return three different file-like objects to us representing stdin, stdout, and stderr:
CMD = "show ip interface brief" # You can issue any command you want
stdin, stdout, stderr = client.exec_command(CMD)
client.close()                                                                                

Reading the output of an executed command

We will use the stdout object to retrieve what the command has returned:

output = stdout.readlines()                                                            
we write the output back to a file:
with open("backup.txt", "w") as out_file
  for line in output:
      out_file.write(line)

Executing the same command against multiple devices
We will then read that file from our Python script, create clients for each of these devices, and finally execute a command while also saving the output back to our file:
1. Import the required libraries, Paramiko and json:
import json
from paramiko.client import SSHClient

2. Open up the credentials.json file and provide the credentials to your device(s) in the format shown in the following code. You can specify as many devices as you want:

 {
 "name": "<insert a unique name of your device>",
 "host": "<insert the host of your device>",
 "username": "<insert the username>",
 "password": "<insert the password",
 "port": 22
 },
 {
 "name": "<insert a unique name of your device>",
 "host": "<insert the host of your device>",
 "username": "<insert the username>",
 "password": "<insert the password",
 "port": 22
 },
{
 "name": "<insert a unique name of your device>",
 "host": "<insert the host of your device>",
 "username": "<insert the username>",
 "password": "<insert the password",
 "port": 22
 }
]

3. We will now open the JSON file in our Python script:
credentials = {}
with open("credentials.json") as fh:
 json.load(fh)

4. Create a variable holding the command you want to execute. We will then loop over all the devices specified in our credentials.json file and create an SSH client object. Additionally, we will create an individual output file for each of our devices based on the name we specified in the JSON file:

CMD = "show running-config"
for cred in credentials:
  out_file_name = str(cred['name']) + ".txt"
  client = SSHClient()
  client.load_system_host_keys()
  client.connect(SSH_HOST, port=cred['port'], username=cred['username'], password=cred['password'])
  stdin, stdout, stderr = client.exec_command(CMD)
  out_file = open(out_file_name, "w")
  output = stdout.readlines()
  for line in output:
     out_file.write(line)
  out_file.close()
  client.close()
  print("Executed command on " + cred['name'])

Executing a sequence of commands

1. Import the Paramiko library. We will also need the built-in time library:
from paramiko.client import SSHClient
import time                                                

2. Specify the host, username, and password. You can name these variables however you like. In the Python community, it has become a standard to uppercase these global variables: 
SSH_USER = "<Insert your ssh user here>"
SSH_PASSWORD = "<Insert your ssh password here>"
SSH_HOST = "<Insert the IP/host of your device/server here>"
SSH_PORT = 22 # Change this if your SSH port is different

3. Create an SSHClient object, which we just imported from Paramiko: 
client = SSHClient()                                                      

4. While we have created our client object, we have not yet connected to the device. 

client.load_system_host_keys()
client.connect(SSH_HOST, port=SSH_PORT, username=SSH_USER, password=SSH_PASSWORD)

5. Open up an interactive shell session and a channel that we can use to retrieve the output:
channel = client.get_transport().open_session()
shell = channel.invoke_shell()

6. Next, specify the list of commands we want to execute on the device:
commands = [
 "configure terminal",
 "hostname test"
]

7. Iterate over each of the commands, execute them,:
for cmd in commands:
  shell.send(cmd + "\n")
  out = shell.recv(1024)
  print(out)
  time.sleep(1)
  client.close()
we use Paramiko's concept of an interactive shell to send multiple commands to the remote device one after another. 


Using public/private keys for authentication

We have always used a username/password combination to connect to our device. This is not the most secure way, however, and many security policies advocate using public-private key pairs instead of a static password. We will see how to programmatically open an SSH connection using a password-protected private key.

1. Import the Paramiko library:
from paramiko.client import SSHClient

2. Specify the host and username. 
SSH_USER = "<Insert your ssh user here>"
SSH_HOST = "<Insert the IP/host of your device/server here>"
SSH_PORT = 22 # Change this if your SSH port is different
SSH_KEY = "<Insert the name of your private key here>"
SSH_KEY_PASSWORD = "<Insert the password here>"

3. Create an SSHClient object, which we just imported from Paramiko: 
client = SSHClient()                                                                  

4. While we have created our client object, we have not yet connected to the device. 
client.load_system_host_keys()
client.connect(SSH_HOST, port=SSH_PORT, username=SSH_USER,  look_for_keys=True, key_filename=SSH_KEY, passphrase=SSH_KEY_PASSWORD)

5. As seen before, we can now execute a command once the connection is established:
stdin, stdout, stderr = client.exec_command('<your command>')

6. Finally, we need to close the connection:
client.close()                                                                           

We use Paramiko's ability to load RSA keys for authentication to avoid using a username/password combination for authentication. 
The RSA algorithm is an asymmetric cryptography algorithm; this means that it uses a public key and a private key (i.e two different, mathematically linked keys). As their names suggest, a public key is shared publicly, while a private key is secret and must not be shared with anyone.

The RSA algorithm is named after those who invented it in 1978: Ron Rivest, Adi Shamir, and Leonard Adleman.

Loading local SSH configuration

1. Import the Paramiko library:
from paramiko.client import SSHClient
from paramiko import SSHConfig

2. Specify the path to your SSH config file and the name of your host as it appears in your SSH configuration.
SSH_CONFIG = "<insert path to ssh config here>"
SSH_HOST = "example"                                        

3. Create an SSHConfig object, which we just imported from Paramiko, and create a local file object with the path to our SSH configuration: 
config = SSHConfig()
config_file = open(SSH_CONFIG)

4. Next, we need to tell the SSHConfig object to load and parse the configuration file:
config.parse(config_file)                                

5. With the config parsed, we can now do a lookup on this configuration object to extract all information stored in the configuration itself. The lookup function will return a dictionary:
dev_config = config.lookup(SSH_HOST)

6. With our device configuration extracted from the SSH config we can go ahead and fill our connection details with what we have extracted from the SSH configuration file:
client.load_system_host_keys()
HOST = dev_config['hostname'],
client.connect(HOST, port=int(dev_config['port']), username=dev_config['user'], key_filename=dev_config['identityfile'])
client.close()                                       


 


Responses(0)







Related