Paramiko's SSHClient with SFTP

Question:

How I can make SFTP transport through SSHClient on the remote server? I have a local host and two remote hosts. Remote hosts are backup server and web server. I need to find on backup server necessary backup file and put it on web server over SFTP. How can I make Paramiko’s SFTP transport work with Paramiko’s SSHClient?

Asked By: Denis

||

Answers:

paramiko.SFTPClient

Sample Usage:

import paramiko
paramiko.util.log_to_file("paramiko.log")

# Open a transport
host,port = "example.com",22
transport = paramiko.Transport((host,port))

# Auth    
username,password = "bar","foo"
transport.connect(None,username,password)

# Go!    
sftp = paramiko.SFTPClient.from_transport(transport)

# Download
filepath = "/etc/passwd"
localpath = "/home/remotepasswd"
sftp.get(filepath,localpath)

# Upload
filepath = "/home/foo.jpg"
localpath = "/home/pony.jpg"
sftp.put(localpath,filepath)

# Close
if sftp: sftp.close()
if transport: transport.close()
Answered By: leoluk

If you have a SSHClient, you can also use open_sftp():

import paramiko


# lets say you have SSH client...
client = paramiko.SSHClient()

sftp = client.open_sftp()

# then you can use upload & download as shown above
...
Answered By: Alon Gouldman

In addition to the first answer which is great but depends on username/password, the following shows how to use an ssh key:

from paramiko import Transport, SFTPClient, RSAKey
key = RSAKey(filename='path_to_my_rsakey')
con = Transport('remote_host_name_or_ip', 22)
con.connect(None,username='my_username', pkey=key)
sftp = SFTPClient.from_transport(con)
sftp.listdir(path='.')
Answered By: David W.

The accepted answer "works". But with its use of the low-level Transport class, it bypasses a host key verification, what is a security flaw, as it makes the code susceptible to Man-in-the-middle attacks.

Better is to use the right Paramiko SSH API, the SSHClient, which does verify the host key:

import paramiko
paramiko.util.log_to_file("paramiko.log")

ssh = paramiko.SSHClient()
ssh.connect(host, username='user', password='password')
# or 
# key = paramiko.RSAKey.from_private_key_file('id_rsa')
# ssh.connect(host, username='user', pkey=key)

sftp = ssh.open_sftp()

sftp.get(remotepath, localpath)
# or
sftp.put(localpath, remotepath)

For details about verifying the host key, see:
Paramiko "Unknown Server"

Answered By: Martin Prikryl

For those anyone need to integrate with an ssh/sftp server that requires a private key and want to perform host key verification for the known host by using a specific public key, here is a snippet code with paramiko:

import paramiko

sftp_hostname = "target.hostname.com"
sftp_username = "tartgetHostUsername"
sftp_private_key = "/path/to/private_key_file.pvt"
sftp_private_key_password = "private_key_file_passphrase_if_it_encrypted"
sftp_public_key = "/path/to/public_certified_file.pub"
sftp_port = 22
remote_path = "."
target_local_path = "/path/to/target/folder"

ssh = paramiko.SSHClient()
    
# Load target host public cert for host key verification
ssh.load_host_keys(sftp_public_key)

# Load encrypted private key and ssh connect
key = paramiko.RSAKey.from_private_key_file(sftp_private_key, sftp_private_key_password)
ssh.connect(host=sftp_hostname, port=sftp_port, username=sftp_username, pkey=key)

# Get the sftp connection
sftp_connection = ssh.open_sftp()

directory_list = sftp_connection.listdir(remote_path)

# ...

if sftp_connection: sftp_connection.close()
if ssh: ssh.close()

Notice that only certificates in classic Openssh format are supported, otherwise needs to be converted with the following commands (also for the latest Openssh formats):

$chmod 400 /path/to/private_key_file.pvt
$ssh-keygen -p -f /path/to/private_key_file.pvt -m pem -P <currentPassphrase> -N <newPassphrase>

In order to avoid man in the middle attack, it is important to do not use paramiko.AutoAddPolicy() and load the public host key programmatically as above or load it from ~/.ssh/known_hosts
The file must be in the format "<host_name> ssh-rsa AAAAB3NzaC1yc2EAAAA..."
In case you don’t have the public key and you trust the target host (take care to mitm), you can download it using $ssh-keyscan target.hostname.com command.

The above code is the only way I found to avoid the following error during connection:

paramiko.ssh_exception.SSHException: Server 'x.y.z' not found in known_hosts

This error was prompted also with the following way to load the public certificates:

key = paramiko.RSAKey(data=decodebytes(sftp_public_key))
ssh_client.get_host_keys().add(sftp_hostname, 'ssh-rsa', key)

Also the following code was not able for me to load the certificate (tried also by encoding the certificate in base64):

ssh_client=paramiko.SSHClient()

rsa_key = paramiko.RSAKey.from_private_key_file(sftp_private_key, sftp_private_key_password)
rsa_key.load_certificate(sftp_public_key)

It always ends with:

  File "/usr/local/lib/python3.9/site-packages/paramiko/pkey.py", line 720, in from_string
    key_blob = decodebytes(b(fields[1]))
  File "/usr/lib64/python3.9/base64.py", line 538, in decodebytes
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding

The above code above worked for the SFTP integration with GoAnywhere.

I hope this is helpful, I’ve not found any working example and spent many hours in searches and tests.
The implementations using pysftp wrapper it is now to be considered as discontinued from 2016.

Answered By: fl4l
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.