Paramiko“未知服务器”

40
我正在尝试使用Paramiko库,但是当我尝试使用以下简单程序连接时,该库会立即抛出异常:
import paramiko
ssh = paramiko.SSHClient()
ssh.connect('127.0.0.1', username='boatzart', password='mypassword')

我遇到的错误是:

Traceback (most recent call last):
File "test.py", line 6, in <module>
ssh.connect('127.0.0.1')
File "build/bdist.macosx-10.7-intel/egg/paramiko/client.py", line 316, in connect
File "build/bdist.macosx-10.7-intel/egg/paramiko/client.py", line 85, in missing_host_key
paramiko.SSHException: Unknown server 127.0.0.1
无论我尝试哪个服务器,都会出现这个问题。
6个回答

76

我也遇到了同样的问题,以下是解决方案:

import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('127.0.0.1', username=username, password=password)
stdin, stdout, stderr = client.exec_command('ls -l')

这是设置在连接到没有主机密钥存储在系统或本地 HostKeys 对象中的服务器时要使用的策略。默认策略是拒绝所有未知服务器(使用 RejectPolicy)。您可以替换为 AutoAddPolicy 或编写自己的策略类。

更多详细信息请参见 paramiko api 文档。希望这有所帮助。

之后,您可以将其保存到另一个密钥文件中以供下次使用,如下所示。

ssh.get_host_keys().save('/some/file/path')

您可以按照以下方式始终从文件加载。

ssh.load_host_keys('/some/file/path')

8
它是有效的,我也这样做,但值得一提的是,您正在自动信任目标机器,并在技术上容易受到中间人攻击。只是提一下注意事项! - F1Rumors
1
AutoAddPolicy()会将host_key添加到known_hosts吗?那么下次运行时,我可以删除set_missing_host_key_policy()并只使用load_system_host_keys()吗? - nidHi
1
只有在提前调用SSHClient.load_host_keys来告诉它从哪里加载密钥时,才能使用@nidHi。请参见我的答案 - Martin Prikryl

35

正确的方法可以是:

  • 在调用connect之前,在由SSHClient.get_host_keys返回的实例上调用HostKeys.add,并传递受信任的密钥。

    from base64 import decodebytes
    # ...
    
    keydata = b"""AAAAB3NzaC1yc2EAAAADAQAB..."""
    key = paramiko.RSAKey(data=decodebytes(keydata))
    client.get_host_keys().add('example.com', 'ssh-rsa', key) 
    

    根据实际的密钥类型,您可能需要使用ECDSAKeyEd25519Key代替RSAKey

    要查看如何获取用于代码的密钥,请参阅我的回答:
    使用pysftp验证主机密钥

    如果您只知道指纹,请参阅:
    Python - 使用指纹验证主机密钥的pysftp / paramiko

  • 或者使用client.load_system_host_keys()加载已缓存的主机密钥(例如通过OpenSSH命令行ssh)。

  • 或者,您至少可以缓存第一次尝试的主机密钥,以确保它在将来不会更改。

    为此,在connect之前使用SSHClient.load_host_keys。与AutoAddPolicy结合使用时,Paramiko会自动将新的主机密钥添加到文件中。


  • 1
    这应该放得更高!特别是最后关于缓存第一次尝试的主机密钥的注释:调用client.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))client.set_missing_host_key_policy(paramiko.AutoAddPolicy()),然后connect到服务器以保存密钥。随后的连接可以使用client.load_system_host_keys()加载主机密钥。 - karan.dodia
    虽然这是一条明智且重要的安全建议,但更重要的建议可能是“不要使用密码”。通过公钥客户端身份验证,这就没那么重要了 - Caesar

    32

    异常是由于缺少主机密钥引起的,而“未知服务器”这个比较难懂的提示就是线索——因为异常是从 missing_host_key 触发的。

    请尝试使用以下方法:

    import paramiko
    
    paramiko.util.log_to_file('ssh.log') # sets up logging
    
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.connect('127.0.0.1', username=username, password=password)
    stdin, stdout, stderr = client.exec_command('ls -l')
    

    7
    我遇到了这个问题,想在这里发布一个解决方法。问题确实是ssh服务器发送了ecdsa密钥,而paramiko目前不支持它们。在我的Debian Wheezy系统上,我通过注释掉/etc/ssh/sshd_config文件中的一行来禁用ecdsa:
    # HostKey /etc/ssh/ssh_host_ecdsa_key
    重启sshd服务后,它就回到使用RSA密钥了。我的known_hosts文件中有一些ecdsa密钥,所以我只需将其删除以进行重置,并手动登录以重新生成密钥。从那时起,paramiko完美地按预期工作,具有RSA主机密钥检查功能。

    6
    我遇到了这个错误:我可以从shell连接,但paramiko显示“Unknown server workdevel114”。
    known_hosts中有两个类似的条目:
    user@host> grep workdevel114 ~/.ssh/known_hosts
    workdevel114 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC8qGbuI1BaBodi7sKWLfV8Eh+De80Th7HFLD4WiJWo57THl0Q+QcopUaU3pF....
    user@host> grep I1BaBodi7sKWLfV8Eh+De80Th7HFLD4WiJWo57THl0Q+QcopUaU3pF ~/.ssh/known_hosts
    workdevel114 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC8qGbuI1BaBodi7sK...
    |1|f/auQ9nY5dFbVtOdY3ocjtVO9dM=|esvazUDTT3VIcLk9DxmPI6FZt1s= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC8qGbuI1BaBodi7sKWLfV8Eh+De80Th7HFLD4...
    

    第二个条目(|1|....)似乎会让paramiko混淆。我猜这与这个问题有关:https://github.com/paramiko/paramiko/issues/67 我通过添加以下行解决了这个问题:
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    

    但是,在这种情况下,这将禁用ssh协议的主机检查:Paramiko认为主机密钥未知,但实际上是已知的。已知的密钥被忽略了。我不在意,因为在我的环境中很少发生中间人攻击。

    Paraiko版本:1.7.7.1-1ubuntu1


    我知道这是一个旧的线程,但添加“set_missing_host_key_policy”解决了我的问题,因为使用“load_system_host_keys”不够。 - ton

    0
    我尝试使用上Martin Prikryl的回答,将其部分内容整合进一个带有上下文管理器的类中。

    或者你至少可以缓存主机密钥,以确保它在未来不会更改。为此,请在连接之前使用SSHClient.load_host_keys。结合AutoAddPolicy使用,Paramiko会自动将新的主机密钥添加到文件中。

    
    import paramiko
    from paramiko import client, sftp_client
    
    
    class SFTP:
        sftp: sftp_client.SFTPClient
        ssh: client.SSHClient
        host: str
        user: str
        password: str
    
        def __init__(self, host: str, user: str, password: str):
            self.host = host
            self.user = user
            self.password = password
    
        def __enter__(self) -> sftp_client.SFTPClient:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.load_system_host_keys()
            ssh.connect(self.host, username=self.user, password=self.password)
            self.ssh = ssh
    
            sftp = ssh.open_sftp()
            self.sftp = sftp
            return sftp
    
        def __exit__(self, exc, _, __) -> bool:
            if exc is None:
                self.sftp.close()
                self.ssh.close()
                return True
    
            raise exc
    

    例子:

    
    with SFTP('host', 'user', 'password') as sftp:
        ...
    

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