- Python SSH Tutorial
- Installation
- Connect to SSH
- Connect using password
- Connect using SSH key
- Run commands over SSH
- Conclusion
- References
- Remote command execution over SSH using Python
- Remote execution
- Paramiko
- Asyncssh
- Issue with long running commands
- Solution/workaround
- SSH Using Python
- Use the paramiko Library in Python to Create SSH Connections and Run Commands
- Use the subprocess Module to Create SSH Connections and Run Commands
Python SSH Tutorial
SSH (secure shell) is good for remotely managing machines using a secure connection. Typically you will log in to a server using the command-line ssh tool, or something like PuTTy or MobaXTerm. This guide will show you how to use Python to connect and run commands over SSH using the Paramiko package.
Installation
The easiest way to install paramiko is using pip .
python -m pip install paramiko
To install from source, clone from GitHub and then install using setup.py .
git clone https://github.com/paramiko/paramiko cd paramiko python setup.py install
Connect to SSH
Connect to an SSH server using paramiko.client.SSHClient.connect(). The hostname is the only required parameter.
connect(hostname, port=22, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None, auth_timeout=None, gss_trust_dns=True, passphrase=None, disabled_algorithms=None)
One thing to consider is what trusted known host keys you have. You can use paramiko.client.SSHClient.load_system_host_keys() . You can also explicitly load a specific known hosts file with load_host_keys() and set the client to automatically accept and add unknown hosts with set_missing_host_key_policy() to paramiko.AutoAddPolicy . Use these options as needed. The AutoAddPolicy is not very secure since it will trust any remote host.
Connect using password
from paramiko import SSHClient client = SSHClient() #client.load_system_host_keys() #client.load_host_keys('~/.ssh/known_hosts') #client.set_missing_host_key_policy(AutoAddPolicy()) client.connect('example.com', username='user', password='secret') client.close()
Connect using SSH key
Using an SSH key is more secure than using a password. Call connect() just like using the password, but instead of providing the user password, we will provide a key_filename and maybe a passphrase for the key. The passphrase may not be needed if your private key file does not have a passphrase.
You can also omit the key file and let Paramiko try to find the right key automatically in your ~/.ssh/ directory. You can turn that on by calling client.look_for_keys(True) . The first example will show how to explicitly provide the key and passphrase and the second one will show how to look for keys.
# Explicitly provide key and passphrase from paramiko import SSHClient, AutoAddPolicy client = SSHClient() #client.load_system_host_keys() #client.load_host_keys('~/.ssh/known_hosts') #client.set_missing_host_key_policy(AutoAddPolicy()) client.connect('example.com', username='user', key_filename='mykey.pem', passphrase='mysshkeypassphrase') client.close()
This example show hows to look for keys in ~/.ssh/ automatically.
from paramiko import SSHClient client = SSHClient() #client.load_system_host_keys() #client.load_host_keys('~/.ssh/known_hosts') #client.set_missing_host_key_policy(AutoAddPolicy()) client.look_for_keys(True) client.connect('example.com', username='user') client.close()
Run commands over SSH
Once you have a connection open, you can execute any command just like you would if you were in a regular interactive SSH session.
This example shows how to:
- Run a command on the server
- Provide standard input data to command
- Read standard output and error from command
- Get the return code of the command
The command run in this example is the PHP interpreter with the code provided via standard input. The output is then printed out and the return code is checked.
from paramiko import SSHClient # Connect client = SSHClient() client.load_system_host_keys() client.connect('example.com', username='user', password='secret') # Run a command (execute PHP interpreter) stdin, stdout, stderr = client.exec_command('php') print(type(stdin)) # print(type(stdout)) # print(type(stderr)) # # Optionally, send data via STDIN, and shutdown when done stdin.write('') stdin.channel.shutdown_write() # Print output of command. Will wait for command to finish. print(f'STDOUT: ') print(f'STDERR: ') # Get return code from command (0 is default for success) print(f'Return code: ') # Because they are file objects, they need to be closed stdin.close() stdout.close() stderr.close() # Close the client itself client.close()
Conclusion
After reading this guide you should know how to connect to an SSH server using a password or SSH key. You should also know how to run a command, pass data to standard input, and read the output from standard output and error.
References
Remote command execution over SSH using Python
SSH or Secure Shell is a cryptographic network protocol for operating network services securely over an unsecured network.[1] Typical applications include remote command-line, login, and remote command execution, but any network service can be secured with SSH.
Remote execution
Python has many libraries for using SSH. I will discuss about the two libraries which i have used in my projects.
Paramiko
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 us look at example of paramiko.
import paramiko hostname = 'localhost' username = 'root' password = 'password' script_path = "/root" command = "ls" pem = None # ssh client client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # connect to ssh server client.connect(hostname =hostname, username=username, password=password, pkey=paramiko.RSAKey.from_private_key_file(pem) if pem else None) # run command _, stdout, stderr = client.exec_command('cd <> && <>'.format(script_path, command)) out = stdout.readlines() err = stderr.readlines() exit_code = stdout.channel.recv_exit_status() client.close()
Asyncssh
AsyncSSH is a Python package which provides an asynchronous client and server implementation of the SSHv2 protocol on top of the Python 3.6+ asyncio framework. Lets look into one simple example to run command using asynssh.
import asyncio, asyncssh, sys hosts = 'localhost' username = 'username' password = 'password' # create connection async def run_client(): conn = await asyncio.wait_for(asyncssh.connect(hosts, username=username, password=password, known_hosts = None),10,) return conn # run command async def run_command(): try: conn = await run_client() result = await conn.run('ls') if result.exit_status == 0: print(result.stdout, end='') else: print(result.stderr, end='', file=sys.stderr) print('Program exited with status %d' % result.exit_status, file=sys.stderr) except Exception as ex: print(ex) async def program(): await asyncio.gather(run_command()) # Run our program until it is complete try: asyncio.get_event_loop().run_until_complete(program()) except (OSError, asyncssh.Error) as exc: sys.exit('SSH connection failed: ' + str(exc))
Issue with long running commands
Above examples works well in most cases and it worked for me also, but in coporate network where everything is behind firewall, these firewalls causes issue in case of long running commands/scripts. Here firewall means the firewall software which are deployed on corporate networks. These firewall monitors network connections and if there is a long running idle sessions, then it disconnects automatically. It is a security measure but it can cause problems for programs which takes longer and there is communication between client and server. It looks simple but debugging this issue is bit tidious. For me it tooks weeks to come to this conclusion and then i started looking for solution. SSh has option to keep pinging after certain time but unfortunately, i could not find it anywhere.
Solution/workaround
Solution to this problem is very simple, We need to keep session alive by doing something during the long running idle session. Either you return something or pass something but bit tedius to implement. But ssh has very nice option to send ping after given interval, this helps in keeping the session active and alive. We see an example using paramiko where using transport, keep alive interval can be set.
import paramiko # Remote server details hostname = '172.*.*.*' port = 22 username = 'root' password = 'password' # script and path script_path = "/root/raj/" command = "sh sleeptest.sh" # set 60 sec interval to ping remote client = paramiko.Transport((hostname, port)) client.set_keepalive(60) client.connect(username=username, password=password) stdout_data = [] stderr_data = [] session = client.open_channel(kind='session') session.exec_command('cd <> && <>'.format(script_path, command)) stdout = session.makefile('rb', -1) stderr = session.makefile_stderr('rb', -1) print('exit status: ', session.recv_exit_status()) out = stdout.readlines() err = stderr.readlines() session.close() client.close()
import asyncio, asyncssh, sys async def run_client(): conn = await asyncio.wait_for(asyncssh.connect('172.*.*.*', username='root', password='password', known_hosts = None, keepalive_interval=600, keepalive_count_max=10000),10,) return conn async def run_command(): try: conn = await run_client() result = await conn.run('cd /root/raj && sh sleeptest.sh') if result.exit_status == 0: print(result.stdout, end='') else: print(result.stderr, end='', file=sys.stderr) print('Program exited with status %d' % result.exit_status, file=sys.stderr) except Exception as ex: print(ex) async def program(): # Run both print method and wait for them to complete (passing in asyncState) await asyncio.gather(run_command()) # Run our program until it is complete loop = asyncio.get_event_loop() loop.run_until_complete(program()) loop.close()
Hope it will be helpful to anyone new to these library. Do let me know if you find this article useful and feel free to reach me using any medium to discuss on anything.
Happy coding!
Updated: March 22, 2021
SSH Using Python
- Use the paramiko Library in Python to Create SSH Connections and Run Commands
- Use the subprocess Module to Create SSH Connections and Run Commands
SSH (stands for Secure Socket Shell ) is a highly used network protocol for secure, encrypted communication services over an unknown and insecure network. It allows us to send commands between two devices over such a network remotely and has many applications.
Python has modules available that allow such SSH execution. This tutorial will discuss how to run shell commands to a remote device after securing the SSH connection.
Use the paramiko Library in Python to Create SSH Connections and Run Commands
The paramiko module is frequently used for implementing SSH protocols in Python. It allows both server and client-side communication.
Check the code below for running shell commands on a remote device using this module.
ssh = paramiko.SSHClient() ssh.connect(server, username=username, password=password) ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)
The SSHClient() function specifies an SSH server session. We connect to the device using the proper authentication details in the connect function. The required commands can be executed on this device using the exec_command() function.
Another module named spur is derived from this module but has a slightly better interface.
Use the subprocess Module to Create SSH Connections and Run Commands
The check_output() function from the subprocess module can be used to run commands passed to it, and the function returns the output of this command. We can create passwordless SSH connections with this function to execute the required commands.
The following code shows an example of this.
subprocess.check_output(['ssh', 'my_server', 'echo /*/'])
Another method that can be used from this module is the Popen() . It is similar to the os.Popen() function from the os module to run commands and external programs. In this module, the Popen is a class and not a method. It is a little complicated to use this due to various parameters that need to be specified along with the required command. It combines all the 4 os.popen() functions into one. It also executes the command as a separate process. We can use it to create SSH connections and run commands, as shown below.
subprocess.Popen("ssh @ ".format(user=user, host=host, cmd='ls -l'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
Manav is a IT Professional who has a lot of experience as a core developer in many live projects. He is an avid learner who enjoys learning new things and sharing his findings whenever possible.