【问题标题】:How to compensate for Paramiko rekey failures?如何弥补 Paramiko 密钥更新失败?
【发布时间】:2015-08-07 02:51:41
【问题描述】:

短版:

  • Paramiko 在大容量数据期间有一个known issue rekeying 转让
  • 引用的补丁(现在是 dist 的一部分)通过调整时间来缓解它,但是对于大容量传输,如果在更新密钥完成之前有太多数据流,仍然有可能中止连接
  • 我看到的最简单的解决方法是引用 paramiko.Packetizer.need_rekey(),如果它返回 True,则在我的数据传输线程中阻塞或休眠

我的问题:

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

加长版:

现代 SSH 会话需要在一定时间或数据通过后重新加密。如果没有足够快地响应重新生成密钥的请求 - 其中“足够快”意味着自请求重新生成密钥以来经过了太多时间或太多数据 - 连接将作为安全保护机制中止。

Paramiko 过去在重新生成密钥请求和重新生成密钥之间有 20 个数据包的非常狭窄的限制。 This patch 2012 年

增加(d) re-key request & 之间接收数据包的限制 完成从 20 包到 2**29 包。

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

不用说,将 25 GB 的 SSH 连接丢失为 30 GB 的数据传输可能会令人沮丧。

使用 OpenSSH 作为客户端,我没有这个问题,这与我读过的报告一致,即重新加密互操作性存在问题。据报道,OpenSSH 可以与 自身 无缝地重新生成密钥;与其他人...不太一样。但是我已经在使用 Paramiko 登录远程主机并运行必要的命令来启动数据传输;必须打开一个单独的 OpenSSH 会话来转发数据的端口是很麻烦的。

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

作为临时措施,简单的解决方法是让我的脚本自行调节。我很乐意插入sleep 呼叫或完全阻止数据传输,同时处理重新输入密钥。如果没有在后台进行大量数据传输,密钥更新应该很容易在最后期限内完成。

Paramiko 提供了一种查看是否已请求更新密钥的方法 - paramiko.Packetizer.need_rekey()。但我不知道如何在我现有的 SSHClient() 对象的上下文中调用它。对于paramiko.transport.* 方法,我可以使用paramiko.SSHClient().get_transport() 创建一个用于调用它们的对象。似乎没有 paramiko.SSHClient().get_packetizer() 等效项用于访问 Packetizer 方法。 demo/ 代码中没有 Packetizer 的示例,tests/ 下的 test_packetizer.py 文件表明它旨在用作原始接口而不是 SSHClient

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

  1. 当我看到paramiko.Packetizer.need_rekey() 为True 时,我想我可以通过sleep()ing 来解决这个问题,但是我看不到如何从拥有SSHClient 对象的上下文中访问need_rekey() 方法。有什么想法吗?
  2. 对于这个问题,我应该考虑其他解决方案吗?

感谢任何帮助!

版本信息仅供参考:

$ 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
>>> paramiko.__version__
'1.15.2'
>>>

【问题讨论】:

    标签: python ssh paramiko


    【解决方案1】:

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

    我通过 hacking 和 slashing 找到了 #1 的答案:get_transport 返回的 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
    >>>
    

    我更新了我的“数据读取”循环以检查是否设置了transport.packetizer.need_rekey(),如果设置了,则使用time.sleep(1) 进入睡眠状态。以下代码打印出 '.'每 1/80 的数据传输进度和一个“Z”将在暂停时打印出来,因为已请求重新生成密钥:

    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 次运行中重现会话中止失败。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多