Python - pysftp/paramiko - 使用其指纹验证主机密钥 [英] Python - pysftp / paramiko - Verify host key using its fingerprint

查看:51
本文介绍了Python - pysftp/paramiko - 使用其指纹验证主机密钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

导入操作系统进口木材导入pysftp进口paramiko连接信息 = {'服务器':example.com",'用户':用户",'passwd': "密码",'target_dir': "输出/生产",'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(服务器,本地路径):target_dir = 服务器['target_dir']keydata = "d8:4e:f1:f1:f1:f1:f1:f1:21:31:41:14:13:12:11:aa"密钥 = paramiko.RSAKey(data=decodebytes(keydata))选项 = pysftp.CnOpts()options.hostkeys.add('example.com', 'ssh-rsa', key)与 pysftp.Connection(服务器['服务器'],用户名=服务器['用户'],密码=服务器['passwd'],cnopts=options) 作为连接:conn.get_d(target_dir, localpath)delete_files_from_dir(conn, target_dir)move_files_from_server_to_local(connection_info, "/")

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

解决方案

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

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

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

ssh-keyscan example.com >tmp.pub

tmp.pub 看起来像(known_hosts 文件格式):

<预类= 朗 - 无prettyprint-越权"> <代码> example.com SSH-RSA AAAAB3NzaC1yc2EAAAABIwAAAQEA0hVqZOvZ7yWgie9OHdTORJVI5fJJoH1yEGamAd5G3werH0z7e9ybtq1mGUeRkJtea7bzru0ISR0EZ9HIONoGYrDmI7S + BiwpDBUKjva4mAsvzzvsy6Ogy/apkxm6Kbcml8u4wjxaOw3NKzKqeBvR3pc + nQVA + SJUZq8D2XBRd4EDUFXeLzwqwen9G7gSLGB1hJkSuRtGRfOHbLUuCKNR8RV82i3JvlSnAwb3MwN0m3WGdlJA8J + 5YAg4e6JgSKrsCObZK7W1R6iuyuH1zA + dtAHyDyYVHB4FnYZPL0hgz2PSb9c + iDEiFcT/LT4/dQ的+ kRW6DYn66lS8peS8zCJ9CSQ ==

现在,您可以使用 ssh-keygen:

ssh-keygen -l -f tmp.pub -E md5

(仅将 -E md5 用于支持多种指纹算法并默认为 SHA256 的较新版本的 OpenSSH)

你会得到类似的东西:

2048 MD5:c4:26:18:cf:a0:15:9a:5f:f3:bf:96:d8:3b:19:ef:7bexample.com (RSA)

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

 from base64 导入解码字节# ...keydata = b"""AAAAB3NzaC1yc2EAAAABIwAAAQEA0hV..."""密钥 = paramiko.RSAKey(data=decodebytes(keydata))cnopts = pysftp.CnOpts()cnopts.hostkeys.add('example.com', 'ssh-rsa', key)使用 pysftp.Connection(host, username, password, cnopts=cnopts) 作为 sftp:

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

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

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

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

使用Paramiko,你可以巧妙地实现MissingHostKeyPolicy 接口.

从如何AutoAddPolicy已实现:

class AutoAddPolicy (MissingHostKeyPolicy):"自动将主机名和新主机密钥添加到本地`.HostKeys` 对象,并保存它.这由`.SSHClient` 使用."def missing_host_key(self, client, hostname, key):client._host_keys.add(hostname, key.get_name(), key)如果 client._host_keys_filename 不是 None:client.save_host_keys(client._host_keys_filename)client._log(DEBUG, '为 %s 添加 %s 主机密钥:%s' %(key.get_name(), 主机名, hexlify(key.get_fingerprint())))

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


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

OP 在他的回答中发布了这种方法的实现.据称对于 Python 3,需要更复杂的实现,如使用 pysftp 和 Python 3 连接到 SFTP 服务器,仅使用服务器指纹.

This code throws an exception. How can I verify an SSH fingerprint without storing it in a file? I believe the code below is designed for a public key. But the client with the SFTP server validated the fingerprint and did not get me the public key.

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, "/")

The code is based on Verify host key with pysftp.

解决方案

Depending on your needs you can use either of these two methods:

In case you need to verify only one specific host key

Use ssh-keyscan (or similar) to retrieve the host public key:

ssh-keyscan example.com > tmp.pub

The tmp.pub will look like (known_hosts file format):

example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0hVqZOvZ7yWgie9OHdTORJVI5fJJoH1yEGamAd5G3werH0z7e9ybtq1mGUeRkJtea7bzru0ISR0EZ9HIONoGYrDmI7S+BiwpDBUKjva4mAsvzzvsy6Ogy/apkxm6Kbcml8u4wjxaOw3NKzKqeBvR3pc+nQVA+SJUZq8D2XBRd4EDUFXeLzwqwen9G7gSLGB1hJkSuRtGRfOHbLUuCKNR8RV82i3JvlSnAwb3MwN0m3WGdlJA8J+5YAg4e6JgSKrsCObZK7W1R6iuyuH1zA+dtAHyDyYVHB4FnYZPL0hgz2PSb9c+iDEiFcT/lT4/dQ+kRW6DYn66lS8peS8zCJ9CSQ==

Now, you can calculate a fingerprint of that public key with ssh-keygen:

ssh-keygen -l -f tmp.pub -E md5

(use the -E md5 only with newer versions of OpenSSH that support multiple fingerprint algorithms and default to SHA256)

You will get something like:

2048 MD5:c4:26:18:cf:a0:15:9a:5f:f3:bf:96:d8:3b:19:ef:7b example.com (RSA)

If the fingerprint matches with the one you have, you can now safely assume that the tmp.pub is a legitimate public key and use it in the code:

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:

(based on Verify host key with pysftp)

In case you need to automate verification of a host key based on its fingerprint

E.g. because the fingerprint comes from an external configuration.

I'm not sure if a limited API of pysftp allows that. You probably would have to skip pysftp and use Paramiko library directly (pysftp uses Paramiko internally).

With Paramiko, you can cleverly implement MissingHostKeyPolicy interface.

Start with how AutoAddPolicy is implemented:

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

Note that in the code you have the fingerprint available in hexlify(key.get_fingerprint()). Just compare that value against the fingerprint you have. If it matches, just return. Otherwise raise an exception, like the RejectPolicy does.


Another solution (which would work even with pysftp) is to implement PKey in a way that it holds only the fingerprint. And implement its __cmp__ method to compare the fingerprint only. Such an instance of PKey can then be added to cnopts.hostkeys.add.

OP posted an implementation of this approach in his answer. Allegedly for Python 3, more complex implementation is needed, as seen in Connecting to an SFTP server using pysftp and Python 3 with just the server fingerprint.

这篇关于Python - pysftp/paramiko - 使用其指纹验证主机密钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆