Paramiko的SSHClient与SFTP

103

我如何通过远程服务器上的SSHClient实现SFTP传输? 我有一个本地主机和两个远程主机。 远程主机是备份服务器和Web服务器。 我需要在备份服务器上找到必要的备份文件,并通过SFTP将其放在Web服务器上。 如何使用Paramiko的SFTP传输与Paramiko的SSHClient一起工作?

5个回答

223

paramiko.SFTPClient

示例用法:

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()

6
非常好的回答。但我想补充一点,TransportSFTPClient 均实现了 __enter__/__exit__ 接口,因此可以在上下文管理器中使用,例如:with Transport((host, port)) as transport: - ayorgo
这个实现可以工作,但是它没有清理进程。sftp-server 进程与之分叉,如果你运行多次,你会发现在代码完成后有很多进程存在。 - Saurabh Nemade
3
虽然这样可以工作,它使用低级的Transport类绕过了主机密钥验证,这是一种安全漏洞,因为它使代码容易受到中间人攻击。更好的方法是使用正确的Paramiko SSH API,即SSHClient。请参见我的回答 - Martin Prikryl

48
接受的答案“有效”。但是由于使用底层的Transport类,它绕过了主机密钥验证,这是一个安全漏洞,因为它使代码容易受到中间人攻击
更好的方法是使用正确的Paramiko SSH API,即SSHClient,它会验证主机密钥。
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)

有关验证主机密钥的详细信息,请参阅:
Paramiko "未知服务器"

7

如果您拥有一个 SSHClient,您也可以使用 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
...

3
首先,这不是一个独立的答案,而只是对@leoluk答案的评论。其次,如果您有SSHClient,您可以简单地执行sftp = client.open_sftp() - Martin Prikryl

5

除了第一个回答(很好,但需要用户名/密码)之外,以下内容展示了如何使用SSH密钥:

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='.')

4

如果您需要与需要私钥的ssh/sftp服务器集成,并希望使用特定的公钥执行已知主机的主机密钥验证,请使用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()

请注意,仅支持经典的Openssh格式证书,否则需要使用以下命令进行转换(也适用于最新的Openssh格式):
$chmod 400 /path/to/private_key_file.pvt
$ssh-keygen -p -f /path/to/private_key_file.pvt -m pem -P <currentPassphrase> -N <newPassphrase>

为了避免中间人攻击,重要的是不要使用paramiko.AutoAddPolicy(),并像上面那样以编程方式加载公共主机密钥或从~/.ssh/known_hosts中加载它。
文件必须采用以下格式:"<host_name> ssh-rsa AAAAB3NzaC1yc2EAAAA..."
如果您没有公钥并且信任目标主机(请注意mitm),则可以使用$ssh-keyscan target.hostname.com命令下载它。
上述代码是我找到的唯一避免连接期间出现以下错误的方法:
paramiko.ssh_exception.SSHException: Server 'x.y.z' not found in known_hosts

这个错误也会在以下方式加载公共证书时出现:
key = paramiko.RSAKey(data=decodebytes(sftp_public_key))
ssh_client.get_host_keys().add(sftp_hostname, 'ssh-rsa', key)

以下代码对我来说也无法加载证书(尝试过将证书编码为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)

这总是以以下方式结束:

  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

上面的代码可以用于GoAnywhere与SFTP的集成。

希望这对你有所帮助,我没有找到任何可行的示例,花了很多时间进行搜索和测试。 使用pysftp包装器实现现在应该被视为从2016年起停止使用。

我确认了你的提示,我编辑了代码并删除了三行,谢谢。 - fl4l

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接