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:['[email protected]', 'ecdh-sha2-nistp521', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp256', 'diffie-hellman-group14-sha1', 'diffie-hellman-group1-sha1', '[email protected]'] 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: [email protected]
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可以尝试一下下面两种方法:
- 使用 allow_agent=False, look_for_keys=False
- 尝试延长连接的超时时间timeout