Paramiko:读取SSH协议横幅时出错

94 投票
8 回答
188810 浏览
提问于 2025-04-19 05:54

最近,我写了一段代码,可以用不同的用户名连接到工作站(多亏了一个私钥),这个代码是基于paramiko库的。

之前我从来没有遇到过问题,但今天却出现了这个错误:SSHException: Error reading SSH protocol banner

这很奇怪,因为这个问题是随机发生在任何连接上。有没有什么办法可以解决这个问题呢?

8 个回答

2

我遇到了一个问题,就是通过一个跳板机同时建立了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)
3

当我把一个不存在的文件名传给 kwargs>key_filename 时,paramiko 似乎会报这个错。我相信还有其他情况也会莫名其妙地出现这个异常。

4

在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

29

补充一下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 - 可选的超时时间(单位是秒),用于等待身份验证的响应。
46

这要看你说的“修复”是什么意思。评论里提到的根本原因是网络拥堵或资源不足。从这个角度看,它和一些HTTP状态码有点像。这是正常的原因,也可能是SSH服务器返回了错误的头部数据。

比如说,429状态码表示“请求过多”,这告诉客户端要限制请求频率;有时候,如果你超出了配额,API也会返回503状态码,意思是要稍后再试。这个意思就是让你等一会儿再试。

你可以在代码中处理这个异常,等一会儿再试一次。你也可以编辑你的transport.py文件,把banner超时时间设置得更长。如果你的应用不在乎服务器响应的速度,可以把这个时间设置为60秒。

编辑: 根据Greg的回答,编辑你的transport文件已经不再需要了。当你调用连接时,可以传递一个banner_timeout(这可以解决这个问题)、一个timeout(用于底层的TCP连接),还有一个auth_timeout(等待认证响应)。Greg的回答里有一个带有banner_timeout的代码示例,你可以直接使用。

撰写回答