11 Aug 2022 - tsp
Last update 21 Sep 2022
6 mins
Disclaimer: This blog entry is more of a note of some simple recipes used over and over again.
Sometimes one has some Python scripts running and wants to transfer data to or
fetch data from a remote machine - or simply execute commands. The best way to
run commands on remote machines usually is SSH, the best ways for file transfer
SFTP, SCP or simply copying to an NFS share (or of course using WebDAV / DeltaV).
A simple method to realize this is the usage of the excellent paramiko
library - this is a Python implementation of the SSHv2 protocol that builds on
top of the cryptography
package for native cryptography primitives.
This library is pretty flexible - this short blog entry will only show the most basic usage and also assumes that one wants to use public key based authentication for the remote user. As soon as one has grasped the basic ideas one can figure out everything necessary from the excellent documentation.
One can directly install paramiko
from PyPi:
pip install paramiko
First as usual one has to import the library:
import paramiko
Then one can use the RSAKey.from_private_key_file
method to load the
private key part for authentication:
try:
sshkey = paramiko.RSAKey.from_private_key_file("/path/to/keyfile")
except IOError:
# The file has not been found or any other kind of I/O error has happened
pass
except PasswordRequiredException:
# No password has been supplied by the keyfile is password protected
pass
except SSHException
# Keyfile is invalid
pass
To supply a password one can add the password
argument to the from_private_key_file
method. Please keep in mind that handling passwords usually should be done
with care - using separate memory locations that are not pageable, etc. - this is
pretty hard to do correct from Pythons environment.
To execute remote commands one just has to:
stdin
, stdout
and stderr
pipes
from the remote shellHost key handling tells the client how to react when one sees an unknown or changed remote host key. The typical policies are:
RejectPolicy
(the default policy) that simply rejects connections to
any unknown host. One has to connect using the same user first manually to
the remote machine to get a program running using this policy.AutoAddPolicy
is one of the more popular approaches - when a host
first sees a new host key it accepts it when no other hostkey is known for
the given destination. In case the key changes the connection is rejected though.WarningPolicy
always connects and only warns in case of unknown or
changed host keys. This is rather unsafe and allows for man in the middle
attacks …# First load the SSHKey - see above for details:
sshkey = paramiko.RSAKey.from_private_key_file("/path/to/keyfile")
# Create the SSH client and setup handling of new host keys
sshClient = paramiko.SSHClient()
sshClient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the remote host
sshClient.connect("203.0.113.1", username = "example", pkey = sshkey)
To execute commands one uses the exec_command
method that returns a tuple
of the standard pipes:
stdin, stdout, stderr = sshClient.exec_command(command)
To dump all information sent by a client and simply terminate afterwards one
can dump information from stdout
and invoke all destructors on the pipes.
Note that this is required!:
stdin, stdout, stderr = sshClient.exec_command("uname -a")
print(stdout.read())
del stdin, stdout, stderr
In the end one has to close the client:
sshClient.close()
To transfer files one can use the sftp
subsystem of SSH. Paramiko provides
a full blown implementation of the SFTP protocol including directory traversal,
permission management, reading and writing files without storing them locally, etc.
The following example will limit itself to uploading and downloading of files
as well as deleting and listing.
Bootstrapping of the public key, client instantiating and connecting works the same as before:
# First load the SSHKey - see above for details:
sshkey = paramiko.RSAKey.from_private_key_file("/path/to/keyfile")
# Create the SSH client and setup handling of new host keys
sshClient = paramiko.SSHClient()
sshClient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the remote host
sshClient.connect("203.0.113.1", username = "example", pkey = sshkey)
Then one has to open the SFTP client:
sftpClient = sshClient.open_sftp()
put
operation: sftpClient.put("/local/source", "/remote/destination")
get
operation: sftpClient.get("/remote/source", "/local/destination")
Both methods allow one to set a callback =
callable that receives two
integer parameters callback(bytesTransferred, bytesTotal)
. One could use
this to provide status updates for example:
def showTransferStatus(bytesTransferred, bytesTotal):
print(f"{bytesTransferred} out of {bytesTotal} bytes transferred")
sftpClient.put("/local/source", "/remote/destination", callback = showTransferStatus)
To delete a file one can simply call the remove
function:
sftpClient.remove("/path/remote")
There are a variety of methods to list files on the remote machine using the SFTP
client. The easiest for small directories is listdir(path = '.')
. This method
returns a simple Python list containing all filenames and directory names on the remote
side. It does not contain entries for .
and ..
though.
print(sftpClient.listdir())
In case one also wants to receive the attributes one might use listdir_attr(path = '.')
.
This returns an wrapper containing additional information per entry:
filename
longname
stat(2)
:
st_atime
- the last accessed time (not updated on all filesystems)st_gid
as the owning group IDst_uid
is the owning user IDst_mode
contains the Unix permission flagsst_mtime
specifies the last modified timest_size
, the size in bytesA more advanced method to list files uses an iterator / generator pattern.
This method is called listdir_iter(path = '.', read_aheads = 50)
. This
method can be used in a typical iterator pattern like a for each loop:
for f in sftpClient.listdir_iter():
print(f.filename)
The objects iterated over have the same structure as the result of listdir_attr
.
In the end the SFTP client as well as the SSH client has to be shut down:
sftpClient.close()
sshClient.close()
This article is tagged: Python, Basics, Programming
Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)
This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/