如何弥补Paramiko rekey失败?

时间:2015-08-04 21:48:47

标签: python ssh paramiko

简短版:

  • Paramiko在高容量数据期间有known issue rekeying 传输
  • 引用的补丁(现在是dist的一部分)通过摆弄时间来缓解它,但是如果在重新生成密钥完成之前有太多的数据流,那么在高容量传输时仍然可以中止连接
  • 我看到的最简单的解决方法是引用paramiko.Packetizer.need_rekey(),然后在我的数据传输线程中阻塞或睡眠(如果它返回True)

我的问题:

  1. 当我拥有的paramiko.Packetizer.need_rekey()对象时,如何访问paramiko.SSHClient()的任何参考或示例
  2. 有什么更好的解决问题的建议吗?
  3. 长版:

    现代SSH会话需要在经过一定时间或数据后重新生成密钥。如果对重新加密的请求没有足够快地采取行动 - 那么"足够快"自请求重定密钥以来意味着太多时间或太多数据已经过去 - 连接被中止为安全保护机制。

    Paramiko曾经在重定密钥请求和重新加密之间有非常窄的20个数据包限制。 2012年This patch

      

    增加(d)重新密钥请求和&之间接收的数据包的限制。   完成从20包到2 ** 29包。

    在我的应用程序中,我使用paramiko.transport.open_channel通过转发端口传输大型(10-30 GB)数据流。如果我在同一局域网上的两台主机之间执行此操作,则它会100%成功完成。如果主机位于不同的局域网上,延迟越来越高,它就开始出现故障 - 让我们说50%的时间,但可能会超过这个。

    毋庸置疑,将30 GB的SSH连接丢失到30 GB的数据传输可能会令人沮丧。

    使用OpenSSH作为客户端,我没有这个问题,这与我已经读过的报告一致,即重新加密互操作性是有问题的。据报道,OpenSSH与本身无缝地重新密钥;与其他人 ......不那么重要。但我已经使用Paramiko登录远程主机并运行必要的命令来启动数据传输;不得不打开一个单独的OpenSSH会话只是为了转发数据的端口是混乱的。

    正确的解决方案似乎是Paramiko应该阻止或延迟非重新密钥数据包,同时它会重新打开 - 这似乎是OpenSSH处理它的方式,尽管我还没有看到权威的确认那。但这可能是一个雄心勃勃的变化,会影响很多人并花时间。

    作为一项临时措施,简单的解决办法是让我的脚本自我调节。我很乐意在重新加密处理时插入sleep个电话或完全阻止数据传输。如果没有在后台进行大规模数据传输,重新加密应该很容易在截止日期内完成。

    Paramiko提供了一种查看是否已请求重定密钥的方法 - paramiko.Packetizer.need_rekey()。但我不知道如何在我现有的SSHClient()对象的上下文中调用它。对于paramiko.transport.*方法,我可以使用paramiko.SSHClient().get_transport()来创建用于调用它们的对象。对于获取对Packetizer方法的访问权限,似乎没有paramiko.SSHClient().get_packetizer()等效项。 demo/代码中没有Packetizer的示例,test_packetizer.py下的tests/文件表明它被设计为用作原始界面,而不是{{1} }。

    所以 - 重申上面简短版本的问题 -

    1. 当我看到SSHClient为True时,我认为我可以通过sleep()解决此问题,但我不知道如何从我的上下文中访问paramiko.Packetizer.need_rekey()方法拥有need_rekey()个对象。有什么想法吗?
    2. 我应该考虑另一个解决这个问题的方法吗?
    3. 感谢任何帮助!

      版本信息FYI:

      SSHClient

1 个答案:

答案 0 :(得分:1)

我仍然希望听到#2的好答案......

我通过黑客攻击和削减找到了#1的答案:transport返回的get_transport对象包含一个packetizer对象,可用于解决need_rekey()方法:

$ python
Python 2.7.5 (default, Apr  9 2015, 11:03:32)
[GCC 4.8.3 20140911 (Red Hat 4.8.3-9)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import paramiko
>>> ssh = paramiko.SSHClient()
>>> omkey = paramiko.RSAKey.from_private_key_file('./privkey.rsa')
>>> ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
>>> ssh.connect('gowenfawr.example.com', username='gowenfawr', pkey=omkey)
>>> transport = ssh.get_transport()
>>> transport.packetizer
<paramiko.packet.Packetizer object at 0x7fb121aa5650>
>>> transport.packetizer.need_rekey()
False
>>>

我更新了我的&#34;数据读取&#34;循环以检查是否设置了transport.packetizer.need_rekey(),如果是,则使用time.sleep(1)进行休眠。以下代码打印出来&#39;。&#39;每1/80数据传输进度和一个&#39; Z&#39;因为已经请求了重新密钥,它会在暂停时打印出来:

track = 0
hash = int(size/80)
while True:
    r, w, x = select.select([chan], [], [])
    if chan in r:
        data = chan.recv(1024)
        if len(data) == 0:
            break
        os.write(fp[0], data)
        track = track + len(data)
        if transport.packetizer.need_rekey():
            sys.stdout.write('Z')
            sys.stdout.flush()
            time.sleep(1)
        if track > hash:
            sys.stdout.write('.')
            sys.stdout.flush()
            track = 0
print ''

这导致了这种输出

$ grabdata.py gowenfawr.example.com
target is gowenfawr.example.com
Found appropriate kernel version
Setting up data transfer
.....Z.....Z.....Z.....Z......Z.....Z.....Z.....Z.....Z......Z.....Z.....Z.....Z.....Z......Z.....Z...
Transfer complete!
$ 

这告诉我,重复密钥正在定期发生(好的,考虑到这里传输的数据量很大),自从更改以来,我无法重现超过10次运行的会话中止失败。