Python - pysftp / paramiko - 使用其指纹验证主机密钥

Tam*_*yce 4 python ssh paramiko pysftp

此代码引发异常。如何在不将其存储在文件中的情况下验证 SSH 指纹?我相信下面的代码是为公钥设计的。但是带有 SFTP 服务器的客户端验证了指纹并且没有给我公钥。

import os
import shutil

import pysftp
import paramiko

connection_info = {
    'server': "example.com",
    'user': "user",
    'passwd': "password",
    'target_dir': "out/prod",
    'hostkey': "ssh-rsa 2048 d8:4e:f1:f1:f1:f1:f1:f1:21:31:41:14:13:12:11:aa",
}

def move_files_from_server_to_local(server, localpath):
    target_dir = server['target_dir']
    keydata = "d8:4e:f1:f1:f1:f1:f1:f1:21:31:41:14:13:12:11:aa"
    key = paramiko.RSAKey(data=decodebytes(keydata))
    options = pysftp.CnOpts()
    options.hostkeys.add('example.com', 'ssh-rsa', key)
    with pysftp.Connection(
                    server['server'],
                    username=server['user'],
                    password=server['passwd'],
                    cnopts=options) as conn:
        conn.get_d(target_dir, localpath)
        delete_files_from_dir(conn, target_dir)

move_files_from_server_to_local(connection_info, "/")
Run Code Online (Sandbox Code Playgroud)

该代码基于使用 pysftp 验证主机密钥

Mar*_*ryl 5

根据您的需要,您可以使用以下两种方法之一:

如果您只需要验证一个特定的主机密钥

使用ssh-keyscan(或类似方法)检索主机公钥:

ssh-keyscan example.com > tmp.pub
Run Code Online (Sandbox Code Playgroud)

tmp.pub会像(known_hosts文件格式):

ssh-keyscan example.com > tmp.pub
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用以下命令计算该公钥的指纹ssh-keygen

ssh-keygen -l -f tmp.pub -E md5
Run Code Online (Sandbox Code Playgroud)

-E md5仅与支持多种指纹算法并默认为 SHA256 的较新版本的 OpenSSH 一起使用)

你会得到类似的东西:

example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0hVqZOvZ7yWgie9OHdTORJVI5fJJoH1yEGamAd5G3werH0z7e9ybtq1mGUeRkJtea7bzru0ISR0EZ9HIONoGYrDmI7S+BiwpDBUKjva4mAsvzzvsy6Ogy/apkxm6Kbcml8u4wjxaOw3NKzKqeBvR3pc+nQVA+SJUZq8D2XBRd4EDUFXeLzwqwen9G7gSLGB1hJkSuRtGRfOHbLUuCKNR8RV82i3JvlSnAwb3MwN0m3WGdlJA8J+5YAg4e6JgSKrsCObZK7W1R6iuyuH1zA+dtAHyDyYVHB4FnYZPL0hgz2PSb9c+iDEiFcT/lT4/dQ+kRW6DYn66lS8peS8zCJ9CSQ==
Run Code Online (Sandbox Code Playgroud)

如果指纹与您拥有的指纹匹配,您现在可以安全地假设这tmp.pub是一个合法的公钥并在代码中使用它:

from base64 import decodebytes
# ...

keydata = b"""AAAAB3NzaC1yc2EAAAABIwAAAQEA0hV..."""
key = paramiko.RSAKey(data=decodebytes(keydata))
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add('example.com', 'ssh-rsa', key)

with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp:
Run Code Online (Sandbox Code Playgroud)

(基于使用 pysftp 验证主机密钥

如果您需要根据指纹自动验证主机密钥

例如,因为指纹来自外部配置。

我不确定 pysftp 的有限 API 是否允许这样做。您可能不得不跳过 pysftp 并直接使用 Paramiko(pysftp 在内部使用 Paramiko)。

使用 Paramiko,您可以巧妙地实现MissingHostKeyPolicyinterface

从如何AutoAddPolicy实现开始:

class AutoAddPolicy (MissingHostKeyPolicy):
    """
    Policy for automatically adding the hostname and new host key to the
    local `.HostKeys` object, and saving it.  This is used by `.SSHClient`.
    """

    def missing_host_key(self, client, hostname, key):
        client._host_keys.add(hostname, key.get_name(), key)
        if client._host_keys_filename is not None:
            client.save_host_keys(client._host_keys_filename)
        client._log(DEBUG, 'Adding %s host key for %s: %s' %
                    (key.get_name(), hostname, hexlify(key.get_fingerprint())))
Run Code Online (Sandbox Code Playgroud)

请注意,在代码中,您可以在hexlify(key.get_fingerprint()). 只需将该值与您拥有的指纹进行比较。如果匹配,就返回。否则引发异常,就像做的RejectPolicy那样。


另一个解决方案(即使使用 pysftp 也能工作)是以PKey只保存指纹的方式实现。并实现其仅比较指纹的__cmp__方法PKey然后可以将这样的实例添加到cnopts.hostkeys.add.

OP 在他的回答中发布了这种方法的实现。据称,对于 Python 3,需要更复杂的实现,如Connecting to an SFTP server using pysftp and Python 3 with only the server指纹 中所示