In my previous article about Paramiko I promised a follow-up tutorial about Paramiko. A lot of people have requested it, so here it finally is! In this article I’m going to show you how to set up a shell-like connection over SSH with Paramiko and Python 3.
For more blog posts regarding Python, have a look at this page.
Before you get started, make sure that you’ve installed the necessary packages. For instructions on how to install Paramiko, have a look at the Paramiko installation page. To download Python 3, have a look at the Python download page.
When you’ve installed both Python and Paramiko, create a new Python file and import the paramiko module. We’re also going to use multi-threading, so import threading as well. To quickly exit the application when exit is typed, import sys too.
1 |
import threading, paramiko |
The next step would be to create a ssh class we can use later on to set up connections.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class ssh: shell = None client = None transport = None def __init__(self, address, username, password): print("Connecting to server on ip", str(address) + ".") self.client = paramiko.client.SSHClient() self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy()) self.client.connect(address, username=username, password=password, look_for_keys=False) self.transport = paramiko.Transport((address, 22)) self.transport.connect(username=username, password=password) thread = threading.Thread(target=self.process) thread.daemon = True thread.start() def closeConnection(self): if(self.client != None): self.client.close() self.transport.close() def openShell(self): self.shell = self.client.invoke_shell() def sendShell(self, command): if(self.shell): self.shell.send(command + "\n") else: print("Shell not opened.") def process(self): global connection while True: # Print data when available if self.shell != None and self.shell.recv_ready(): alldata = self.shell.recv(1024) while self.shell.recv_ready(): alldata += self.shell.recv(1024) strdata = str(alldata, "utf8") strdata.replace('\r', '') print(strdata, end = "") if(strdata.endswith("$ ")): print("\n$ ", end = "") |
The code is pretty self-explanatory. The __init__ method is used when creating a new instance of the ssh class. The closeConnection method is used to close the ssh connection, self-explanatory huh?
The openShell method, on the other hand, should be used to invoke the ssh shell with the host and credentials defined on initialization. Lastly the method sendShell should be used to send a command to the shell.
As you’ve probably already noticed, the process method will output the results from the commands to our local terminal window. The process method will run constantly (hence while True ) in a separate thread to retrieve the output. This is not an ideal approach for every type of output data, or for extremely reliable connections, but it easily shows how the paramiko shell can be used.
The output checking thread is started with the following three lines. Which you can find in the __init__ method inside the ssh class we just defined.
1 2 3 |
thread = threading.Thread(target=process) thread.daemon = True thread.start() |
The only thing left is to open the connection and start sending commands to our ssh server. To do so, add the following lines. Make sure to replace “SSH USERNAME”, “SSH PASSWORD” and “SSH SERVER ADDRESS” with your own credentials and server address for the device you want to connect to.
1 2 3 4 5 6 7 8 9 10 11 12 |
sshUsername = "SSH USERNAME" sshPassword = "SSH PASSWORD" sshServer = "SSH SERVER ADDRESS" connection = ssh(sshServer, sshUsername, sshPassword) connection.openShell() while True: command = input('$ ') if command.startswith(" "): command = command[1:] connection.sendShell(command) |
The final code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import threading, paramiko class ssh: shell = None client = None transport = None def __init__(self, address, username, password): print("Connecting to server on ip", str(address) + ".") self.client = paramiko.client.SSHClient() self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy()) self.client.connect(address, username=username, password=password, look_for_keys=False) self.transport = paramiko.Transport((address, 22)) self.transport.connect(username=username, password=password) thread = threading.Thread(target=self.process) thread.daemon = True thread.start() def closeConnection(self): if(self.client != None): self.client.close() self.transport.close() def openShell(self): self.shell = self.client.invoke_shell() def sendShell(self, command): if(self.shell): self.shell.send(command + "\n") else: print("Shell not opened.") def process(self): global connection while True: # Print data when available if self.shell != None and self.shell.recv_ready(): alldata = self.shell.recv(1024) while self.shell.recv_ready(): alldata += self.shell.recv(1024) strdata = str(alldata, "utf8") strdata.replace('\r', '') print(strdata, end = "") if(strdata.endswith("$ ")): print("\n$ ", end = "") sshUsername = "SSH USERNAME" sshPassword = "SSH PASSWORD" sshServer = "SSH SERVER ADDRESS" connection = ssh(sshServer, sshUsername, sshPassword) connection.openShell() while True: command = input('$ ') if command.startswith(" "): command = command[1:] connection.sendShell(command) |
Pingback: Python and SSH: sending commands over SSH using Paramiko | Daan Lenaerts
Hi,
Thank you for this code. Can you please add this implementation:
“To quickly exit the application when exit is typed, import sys too.”
THanks,
Andrew
Thanks for your comment. Still looking for this?
Hi,
Do you mention password as a text? isnt that security threat. what if i want to send encrypted password to function? how is that possible?
Hi, thanks for your comment. Passwords are passed to the SSH client in text here, as is expected.