Paramiko:读取SSH协议横幅时出错
最近,我写了一段代码,可以用不同的用户名连接到工作站(多亏了一个私钥),这个代码是基于paramiko库的。
之前我从来没有遇到过问题,但今天却出现了这个错误:SSHException: Error reading SSH protocol banner
。
这很奇怪,因为这个问题是随机发生在任何连接上。有没有什么办法可以解决这个问题呢?
8 个回答
我遇到了一个问题,就是通过一个跳板机同时建立了12个连接(也就是12个线程)。
因为我需要快速解决这个问题,所以我加了一段等待时间。
for target in targets:
deployer.deploy_target(target, asynchronous=True)
然后我改成了:
for target in targets:
deployer.deploy_target(target, asynchronous=True)
time.sleep(5)
这样对我来说就能正常工作了。
另外,我还根据上面的建议加了一个banner_timeout,这样可以让它更可靠。
client.connect(bastion_address, banner_timeout=60)
当我把一个不存在的文件名传给 kwargs
>key_filename
时,paramiko 似乎会报这个错。我相信还有其他情况也会莫名其妙地出现这个异常。
在transport.py文件中,将超时时间(正如TinBane提到的)从15改为更高的值后,问题部分解决了。这个改动是在第484行:
self.banner_timeout = 200 # It was 15
不过,为了彻底解决这个问题,我在transport.py中添加了一行静态代码,以在_check_banner(self):
函数里声明新的更高的值。
具体的改动如下:
- 原来的代码是这样的:
def _check_banner(self):
for i in range(100):
if i == 0:
timeout = self.banner_timeout
else:
timeout = 2
- 经过永久性修改后变成了这样:
def _check_banner(self):
for i in range(100):
if i == 0:
timeout = self.banner_timeout
timeout = 200 # <<<< Here is the explicit declaration
else:
timeout = 2
补充一下TinBane的回答,关于编辑transport.py
的建议:现在你不需要再这么做了。
自从2015年发布的Paramiko v. 1.15.0版本开始,(具体来说是这个PR),你可以在创建Paramiko连接的时候直接配置这个值,像这样:
client = SSHClient()
client.connect('ssh.example.com', banner_timeout=200)
在写这段话时,Paramiko的当前版本是v. 2.7.1,你在调用connect
方法时可以配置另外两个超时时间,总共是这三个(来源):
banner_timeout
- 可选的超时时间(单位是秒),用于等待SSH横幅的显示。timeout
- 可选的超时时间(单位是秒),用于TCP连接。auth_timeout
- 可选的超时时间(单位是秒),用于等待身份验证的响应。
这要看你说的“修复”是什么意思。评论里提到的根本原因是网络拥堵或资源不足。从这个角度看,它和一些HTTP状态码有点像。这是正常的原因,也可能是SSH服务器返回了错误的头部数据。
比如说,429状态码表示“请求过多”,这告诉客户端要限制请求频率;有时候,如果你超出了配额,API也会返回503状态码,意思是要稍后再试。这个意思就是让你等一会儿再试。
你可以在代码中处理这个异常,等一会儿再试一次。你也可以编辑你的transport.py文件,把banner超时时间设置得更长。如果你的应用不在乎服务器响应的速度,可以把这个时间设置为60秒。
编辑: 根据Greg的回答,编辑你的transport文件已经不再需要了。当你调用连接时,可以传递一个banner_timeout(这可以解决这个问题)、一个timeout(用于底层的TCP连接),还有一个auth_timeout(等待认证响应)。Greg的回答里有一个带有banner_timeout的代码示例,你可以直接使用。