python paramiko包 ssh报错No existing session 调试记录

python paramiko包 ssh报错No existing session 调试记录

问题描述

import paramiko
from scp import SCPClient 
class SSH(object):
    def __init__(self, ssh_ip, ssh_port, user, password, time_out=10, transport="sftp"):
        """
        :type transport: str
        """
        self.__host = ssh_ip
        self.__port = ssh_port
        self.__usr = user
        self.__pwd = password
        self.__timeout = time_out
        paramiko.util.log_to_file('paramiko.log')
        self.__ssh = paramiko.SSHClient()
        self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.__transport = transport
        self.__sftp = None
        self.__scp = None

        self.__login_status = 0

    def connect(self):
        try:
            self.__ssh.connect(hostname=self.__host,
                               port=self.__port,
                               username=self.__usr,
                               password=self.__pwd,
                               timeout=self.__timeout,
                               # allow_agent=False, look_for_keys=False
                               compress=True
                               )
            if self.__transport == "sftp":
                # self.__sftp = paramiko.SFTPClient.from_transport(self.__ssh.get_transport())
                self.__sftp = self.__ssh.open_sftp()
            elif self.__transport == "scp":
                self.__scp = SCPClient(self.__ssh.get_transport(), socket_timeout=15.0)
        except Exception as e:
            self.__login_status = 0
            return False, repr(e)
        self.__login_status = 1
        return True, ""
        
    def close(self):
        if self.__sftp:
            self.__sftp.close()
        if self.__ssh:
            self.__ssh.close()

        self.__login_status = 0
        
if __name__ == "__main__":
    host = "192.168.99.126"
    port = 22
    usr = "root"
    pwd = "root"
    timeout = 10
    T = SSH(host, port, usr, pwd, timeout, "scp")
    res, err = T.connect()
    T.close()

在使用ssh.connect()的时候总会弹出异常 ‘No existing session’, 其他文章 都使用allow_agent=False, look_for_keys=False 这行代码解决问题

            self.__ssh.connect(hostname=self.__host,
                               port=self.__port,
                               username=self.__usr,
                               password=self.__pwd,
                               timeout=self.__timeout,
                               allow_agent=False, look_for_keys=False,
                               compress=True
                               )

但是实际运行时依旧会弹出 ‘No existing session’
在这里插入图片描述
同样的代码连接其他设备可以成功

paramiko日志

paramiko中有一个模块可以将paramiko的调试日志导出到一个指定的文件,在实例化ssh之前先调用paramiko.util.log_to_file(),默认日志级别时DEBUG

        paramiko.util.log_to_file('paramiko.log')
        self.__ssh = paramiko.SSHClient()
        self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

再次调试, 可以观察到paramiko的日志:
日志1:

DEB [20201023-13:43:36.902] thr=1   paramiko.transport: starting thread (client mode): 0x20c73988
DEB [20201023-13:43:36.903] thr=1   paramiko.transport: Local version/idstring: SSH-2.0-paramiko_2.7.2
DEB [20201023-13:43:46.946] thr=1   paramiko.transport: Remote version/idstring: SSH-2.0-dropbear_2016.74
INF [20201023-13:43:46.946] thr=1   paramiko.transport: Connected (version 2.0, client dropbear_2016.74)
DEB [20201023-13:43:46.949] thr=1   paramiko.transport: kex algos:['curve25519-sha256@libssh.org', 'ecdh-sha2-nistp521', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp256', 'diffie-hellman-group14-sha1', 'diffie-hellman-group1-sha1', 'kexguess2@matt.ucc.asn.au'] server key:['ecdsa-sha2-nistp521', 'ssh-rsa', 'ssh-dss'] client encrypt:['aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'aes256-cbc', 'twofish256-cbc', 'twofish-cbc', 'twofish128-cbc', '3des-ctr', '3des-cbc'] server encrypt:['aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'aes256-cbc', 'twofish256-cbc', 'twofish-cbc', 'twofish128-cbc', '3des-ctr', '3des-cbc'] client mac:['hmac-sha1-96', 'hmac-sha1', 'hmac-sha2-256', 'hmac-sha2-512', 'hmac-md5'] server mac:['hmac-sha1-96', 'hmac-sha1', 'hmac-sha2-256', 'hmac-sha2-512', 'hmac-md5'] client compress:['none'] server compress:['none'] client lang:[''] server lang:[''] kex follows?False
DEB [20201023-13:43:46.956] thr=1   paramiko.transport: Kex agreed: curve25519-sha256@libssh.org
DEB [20201023-13:43:46.957] thr=1   paramiko.transport: HostKey agreed: ecdsa-sha2-nistp521
DEB [20201023-13:43:46.957] thr=1   paramiko.transport: Cipher agreed: aes128-ctr
DEB [20201023-13:43:46.957] thr=1   paramiko.transport: MAC agreed: hmac-sha2-256
DEB [20201023-13:43:46.957] thr=1   paramiko.transport: Compression agreed: none
DEB [20201023-13:43:47.139] thr=1   paramiko.transport: kex engine KexCurve25519 specified hash_algo <built-in function openssl_sha256>
DEB [20201023-13:43:47.140] thr=1   paramiko.transport: Switch to new keys ...
DEB [20201023-13:44:15.855] thr=1   paramiko.transport: EOF in transport thread

日志2:

.transport: starting thread (client mode): 0x8ab46350L
.transport: Local version/idstring: SSH-2.0-paramiko_2.1.2
.transport: Remote version/idstring: SSH-2.0-OpenSSH_5.3
.transport: Connected (version 2.0, client OpenSSH_5.3)
.transport: kex algos:[u'diffie-hellman-group-exchange-sha256', u'diffie-hellman-group-exchange-sha1', u'diffie-hellman-group14-sha1', u'diffie-hellman-group1-sha1'] server key:[
.transport: Kex agreed: diffie-hellman-group1-sha1
.transport: Cipher agreed: aes128-ctr
.transport: MAC agreed: hmac-sha2-256
.transport: Compression agreed: none
.transport: kex engine KexGroup1 specified hash_algo <built-in function openssl_sha1>
.transport: Switch to new keys ...
.transport: Adding ssh-rsa host key for [10.0.2.243]:36000: 2e3faf53075afccf09a3ac2q391dd5e8
.transport: Trying key 3a96e7e9e3f59963fbee693f44x8cf58 from /home/user/.ssh/id_rsa1
.transport: userauth is OK
.transport: Authentication (publickey) failed.
.transport: Trying key 8628c2021979e0e0302aac6bc8xcbcef from /home/user/.ssh/id_rsa2
.transport: userauth is OK
.transport: Authentication (publickey) failed.
.transport: Trying key 6e399bc162a737150e1bd643abx7737d from /home/user/.ssh/id_rsa3
.transport: userauth is OK
.transport: Authentication (publickey) failed.
.transport: Trying key 77d22314df154bfd5ca591bd16x19363 from /home/user/.ssh/id_rsa4
.transport: userauth is OK
.transport: Authentication (publickey) failed.
.transport: Trying key 77d22314df154bfd5ca591bd16x19363 from /home/user/.ssh/id_rsa5
.transport: userauth is OK
.transport: Authentication (publickey) failed.
.transport: Trying key c1909f00bf62c74eed5a3a9e5dxa73d1 from /home/user/.ssh/id_rsa6
.transport: userauth is OK
.transport: Disconnect (code 2): Too many authentication failures for user
.transport: Trying key c897a8e51a0d41facf7fa712a2xeace8 from /home/user/.ssh/id_rsa7
.transport: Trying discovered key 3a96e7e9e3f5996xfbee693f44b8cf58 in /home/user/.ssh/id_rsa1

出现日志2中这种情况,基本就可以使用allow_agent=False,look_for_keys=False来解决

日志1中在

paramiko.transport: Switch to new keys ...

之后就直接报异常了,没有进行RSA Host key认证

下面查看paramiko源码:

  File "C:\python37\lib\site-packages\paramiko\client.py", line 412, in connect
    server_key = t.get_remote_server_key()
  File "C:\python37\lib\site-packages\paramiko\transport.py", line 834, in get_remote_server_key
    raise SSHException("No existing session")
paramiko.ssh_exception.SSHException: No existing session

client.py

# If GSS-API Key Exchange is performed we are not required to check the
        # host key, because the host is authenticated via GSS-API / SSPI as
        # well as our client.
        if not self._transport.gss_kex_used:
            server_key = t.get_remote_server_key()
            if our_server_keys is None:
                # will raise exception if the key is rejected
                self._policy.missing_host_key(
                    self, server_hostkey_name, server_key
                )
            else:
                our_key = our_server_keys.get(server_key.get_name())
                if our_key != server_key:
                    if our_key is None:
                        our_key = list(our_server_keys.values())[0]
                    raise BadHostKeyException(hostname, server_key, our_key)

    def get_remote_server_key(self):
        """
        Return the host key of the server (in client mode).

        .. note::
            Previously this call returned a tuple of ``(key type, key
            string)``. You can get the same effect by calling `.PKey.get_name`
            for the key type, and ``str(key)`` for the key string.

        :raises: `.SSHException` -- if no session is currently active.

        :return: public key (`.PKey`) of the remote server
        """
        if (not self.active) or (not self.initial_kex_done):
            raise SSHException("No existing session")
        return self.host_key

可以看到server_key = t.get_remote_server_key()这里获取远程服务器key失败
在get_remote_server_key()函数可以找到self.active和self.initial_kex_done,再根据initial_kex_done查到到一个赋值的地方,前面的"Switch to new keys …"刚好是上面日志1中的异常抛出时的最后一条日志

    def _parse_newkeys(self, m):
        self._log(DEBUG, "Switch to new keys ...")
        self._activate_inbound()
        # can also free a bunch of stuff here
        self.local_kex_init = self.remote_kex_init = None
        self.K = None
        self.kex_engine = None
        if self.server_mode and (self.auth_handler is None):
            # create auth handler for server mode
            self.auth_handler = AuthHandler(self)
        if not self.initial_kex_done:
            # this was the first key exchange
            self.initial_kex_done = True

调试到这里的时候发现self.active 和 self.initial_kex_done都是True值, 但是为什么还会跳到 raise SSHException(“No existing session”)
在这里插入图片描述
调试的时候在server_key = t.get_remote_server_key()这一行打了一个断点,再次运行的时候,没有抛出异常, 顺利连接了,分析可得出t.start_client(timeout=timeout)未启动成功才导致抛出了异常。

t.start_client(timeout=timeout)

在这里插入图片描述
可以看到上面t.start_client(timeout=timeout)设置了一个超时时间,将这个超时时间延长,如将10秒延长至15秒, 运行:

if __name__ == "__main__":
    host = "192.168.101.126"
    port = 22
    usr = "root"
    pwd = "root"
    # timeout = 10
    timeout = 15
    T = SSH(host, port, usr, pwd, timeout, "scp")
    res, err = T.connect()
    T.close()

连接成功:
在这里插入图片描述

总结

paramiko 抛出 No existing session可以尝试一下下面两种方法:

  1. 使用 allow_agent=False, look_for_keys=False
  2. 尝试延长连接的超时时间timeout
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
你好!关于你提到的错误,ConnectionResetError: [Errno 104] Connection reset by peer,这通常是由于网络连接问题引起的。它表示远程主机突然关闭了连接。 有几个可能的原因和解决方法可以尝试: 1. 确保你的网络连接稳定。检查你的网络设置,确保没有任何网络故障或防火墙阻止了 SSH 连接。 2. 确认 SSH 服务器是否正常运行。确保目标服务器上的 SSH 服务正在运行,并且正确地监听 SSH 默认端口(默认是22)。 3. 检查目标服务器的身份验证设置。如果服务器配置了仅允许特定用户或密钥进行身份验证,确保你提供的身份验证信息正确。 4. 考虑通过增加超时选项来增加连接时间。在 paramikoSSHClient 对象上设置超时选项可以防止连接过早关闭。 下面是一个使用 paramiko 进行 SSH 连接的示例代码,你可以参考一下: ```python import paramiko # 创建 SSH 客户端 client = paramiko.SSHClient() client.load_system_host_keys() # 设置自动添加远程主机密钥 client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接远程主机 client.connect('hostname', port=22, username='username', password='password') # 执行命令 stdin, stdout, stderr = client.exec_command('your_command') # 输出结果 print(stdout.read().decode()) # 关闭连接 client.close() ``` 请注意,上述代码中的 'hostname'、'username' 和 'password' 分别需要替换为你要连接的远程主机的实际值。 希望以上信息对你有所帮助!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值