如何使用pexpect自动输入ssh私钥密码短语

3

我之前为一个Linux环境编写了一个程序,可以自动以用户身份运行SSHFS二进制文件,并输入存储的ssh私钥密码(公钥已在远程服务器上)。我使用简单的pexpect命令在一台服务器上实现了该功能。 (Ubuntu服务器14.04,ssh版本6.6,sshfs版本2.5)但是,当应用程序移动到Redhat机器(RHEL6.5,ssh版本5.3,sshfs版本2.4)时,该程序的这个单独部分成为了问题。这个简单步骤让我疯狂了一整天,所以我现在转向这个社区寻求支持。 我原本的代码(简化版)如下:

proc = pexpect.spawn('sshfs %s@%s:%s...') #many options, unrelated
proc.expect([pexpect.EOF, 'Enter passphrase for key.*', pexpect.TIMEOUT], timeout=30)
if proc.match_index == 1:
    proc.sendline('thepassphrase')

这段代码在ubuntu上可以正常运行,但在rhel上却无法正常工作。我已经尝试了将其传输到子进程的回退方法,但也没有取得太大的成功。

proc = subprocess.Popen('sshfs %s@%s:%s...', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)    
proc.stdin.write('thepassphrase'+'\n')
proc.stdin.flush()

当然,我已经尝试了许多微小的变化,但都没有成功,当我手动运行该命令时,命令可以正常运行。
更新3/3: 今天我也手动编译并安装了ssh 6.6在RHEL中,以查看是否导致了问题,但即使使用新的ssh二进制文件,问题仍然存在。
更新3/9: 今天,我找到了一个特定的解决方案,它能够正常工作,但我对其他许多不同的解决方案都不起作用感到不满意,并且我仍在寻找为什么的答案。这是我迄今为止做得最好的:
proc = subprocess.check_call("sudo -H -u %s ssh-keygen -p -P %s -N '' -f %s" % (user, userKey['passphrase'], userKey['path']), shell=True)
time.sleep(2)

proc = subprocess.Popen(cmd, shell=True)

proc.communicate()

time.sleep(1)
proc = subprocess.check_call("sudo -H -u %s ssh-keygen -p -P '' -N %s -f %s" % (user, userKey['passphrase'], userKey['path']), shell=True)

移除密钥的密码,挂载驱动器,再重新添加密钥。显然我不喜欢这个解决方案,但在我找到问题根源之前,它将不得不使用。

更新3/23:

由于我的愚蠢,直到现在我才看到这种方法的直接问题,现在我又回到了起点。虽然这个解决方法可以在第一次建立连接时工作,但“-o reconnect”显然会失败,因为sshfs不知道重新连接的密钥。这意味着这个解决方案不再可行,如果有人知道如何让pexpect版本工作,我会非常感激。


嗯...如果你将主键的加密密钥以明文形式存储,那为什么还要对密钥进行加密呢? - SingleNegationElimination
这是一个网络应用程序的一部分,用户在在线密码字段中输入他们的密码。该密码仅在RAM中存储不到一分钟,我猜这并不是最佳方案,但似乎不是主要的安全问题。 - Rboreal_Frippery
1
ssh-agent 不可行的原因是什么? - twalberg
无论是输入一次还是多次密码短语,我认为(尽管我没有测试过)会出现类似的问题。 - Rboreal_Frippery
1个回答

1

在个人与开发者的交流后,我确定这是一个已知的 bug,并且没有一种适当的方法以与问题中指定的方式运行命令。然而,开发者很快提出了一个同样有用的方法,涉及生成另一个终端进程。

passphrase = 'some passphrase'
cmd = "sudo -H -u somebody sshfs somebody@somewhere:/somewhere-else /home/somebody/testmount -o StrictHostKeychecking=no -o nonempty -o reconnect -o workaround=all -o IdentityFile=/somekey -o follow_symlinks -o cache=no"

bash = pexpect.spawn('bash', echo=False)
bash.sendline('echo READY')
bash.expect_exact('READY')
bash.sendline(cmd)
bash.expect_exact('Enter passphrase for key')
bash.sendline(passphrase)
bash.sendline('echo COMPLETE')
bash.expect_exact('COMPLETE')
bash.sendline('exit')
bash.expect_exact(pexpect.EOF)

我已经测试了这个解决方案,它是一个很好的解决方法,而且没有太多额外的开销。你可以在这里查看他的完整回复。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接