如何补偿 Paramiko 密钥更新失败?
How to compensate for Paramiko rekey failures?
短版:
- Paramiko 在大容量数据期间有 known issue rekeying
转会
- 引用的补丁(现在是 dist 的一部分)通过摆弄时间来缓解它,但是对于大容量传输,如果在 rekey 完成之前有太多数据流,仍然有可能中止连接
- 我看到的最简单的解决方法是引用
paramiko.Packetizer.need_rekey()
然后在我的数据传输线程中阻塞或休眠,如果它 returns True
我的问题:
- 当我拥有的是
paramiko.SSHClient()
对象时如何访问 paramiko.Packetizer.need_rekey()
的任何参考或示例?
- 有更好的解决问题的建议吗?
长版:
现代 SSH 会话需要在一定时间或数据通过后重新生成密钥。如果密钥更新请求没有得到足够快的响应 - "quickly enough" 意味着自请求密钥更新以来经过的时间或数据过多 - 作为安全保护机制,连接将被中止。
Paramiko 以前在重新加密请求和重新加密之间有 20 个数据包的非常窄的限制。 This patch 从 2012 年开始
Increase(d) the limit of received packets between re-key request &
completion from 20 packets to 2**29 packets.
在我的应用程序中,我使用 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
。
所以 - 重申上面简短版本中的问题 -
- 当我看到
paramiko.Packetizer.need_rekey()
为 True 时,我想我可以通过 sleep()
ing 来解决这个问题,但是我看不到如何从我的上下文中访问 need_rekey()
方法有一个 SSHClient
对象。有什么想法吗?
- 对于这个问题,我应该考虑另一种解决方案吗?
感谢任何帮助!
版本信息仅供参考:
$ 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'
>>>
我仍然很想听到关于 #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
>>>
我更新了 "data read" 循环以检查 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 次运行。
短版:
- Paramiko 在大容量数据期间有 known issue rekeying 转会
- 引用的补丁(现在是 dist 的一部分)通过摆弄时间来缓解它,但是对于大容量传输,如果在 rekey 完成之前有太多数据流,仍然有可能中止连接
- 我看到的最简单的解决方法是引用
paramiko.Packetizer.need_rekey()
然后在我的数据传输线程中阻塞或休眠,如果它 returns True
我的问题:
- 当我拥有的是
paramiko.SSHClient()
对象时如何访问paramiko.Packetizer.need_rekey()
的任何参考或示例? - 有更好的解决问题的建议吗?
长版:
现代 SSH 会话需要在一定时间或数据通过后重新生成密钥。如果密钥更新请求没有得到足够快的响应 - "quickly enough" 意味着自请求密钥更新以来经过的时间或数据过多 - 作为安全保护机制,连接将被中止。
Paramiko 以前在重新加密请求和重新加密之间有 20 个数据包的非常窄的限制。 This patch 从 2012 年开始
Increase(d) the limit of received packets between re-key request & completion from 20 packets to 2**29 packets.
在我的应用程序中,我使用 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
。
所以 - 重申上面简短版本中的问题 -
- 当我看到
paramiko.Packetizer.need_rekey()
为 True 时,我想我可以通过sleep()
ing 来解决这个问题,但是我看不到如何从我的上下文中访问need_rekey()
方法有一个SSHClient
对象。有什么想法吗? - 对于这个问题,我应该考虑另一种解决方案吗?
感谢任何帮助!
版本信息仅供参考:
$ 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'
>>>
我仍然很想听到关于 #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
>>>
我更新了 "data read" 循环以检查 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 次运行。