Python 如何通过子流程模块调用ssh,使其使用ssh_ASKPASS变量
我正在编写一个使用SSH命令的GUI。我尝试使用subprocess模块调用ssh并设置ssh_ASKPASS环境变量,以便我的应用程序可以弹出一个窗口询问ssh密码。但是,我无法使用给定的ssh_ASKPASS命令让ssh读取密码:无论我如何设置显示、ssh_ASKPASS、TERM环境变量或如何通过管道传输标准输入/输出,它总是在终端窗口中提示密码。如何确保ssh与当前TTY分离,并使用给定的程序读取密码 我的测试代码是:Python 如何通过子流程模块调用ssh,使其使用ssh_ASKPASS变量,python,ssh,subprocess,Python,Ssh,Subprocess,我正在编写一个使用SSH命令的GUI。我尝试使用subprocess模块调用ssh并设置ssh_ASKPASS环境变量,以便我的应用程序可以弹出一个窗口询问ssh密码。但是,我无法使用给定的ssh_ASKPASS命令让ssh读取密码:无论我如何设置显示、ssh_ASKPASS、TERM环境变量或如何通过管道传输标准输入/输出,它总是在终端窗口中提示密码。如何确保ssh与当前TTY分离,并使用给定的程序读取密码 我的测试代码是: #!/usr/bin/env python import os i
#!/usr/bin/env python
import os
import subprocess
env = dict(os.environ)
env['DISPLAY'] = ':9999' # Fake value (trying in OS X and Windows)
del env['TERM']
env['SSH_ASKPASS'] = '/opt/local/libexec/git-core/git-gui--askpass'
p = subprocess.Popen(['ssh', '-T', '-v', 'user@myhost.com'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env
)
p.communicate()
您的问题是SSH检测到您的TTY并直接与之对话(如手册页中明确说明的)。您可以尝试在没有终端的情况下运行ssh—手册页建议可能需要将
stdin
重定向到/dev/null
,让ssh认为它没有终端
您也可以使用它,这是已知的使用SSH的例子
做你想做的事的正确方法是:
只有当进程真正从TTY分离时,SSH才使用SSH_ASKPASS变量(stdin重定向和设置环境变量是不够的)。要从控制台分离进程,它应该分叉并调用os.setsid()。所以我找到的第一个解决方案是:
# Detach process
pid = os.fork()
if pid == 0:
# Ensure that process is detached from TTY
os.setsid()
# call ssh from here
else:
print "Waiting for ssh (pid %d)" % pid
os.waitpid(pid, 0)
print "Done"
使用subprocess模块也有一种优雅的方法来实现这一点:在preexec_fn参数中,我们可以在执行外部命令之前传递在子流程中调用的Python函数。因此,这个问题的解决方案是增加一行:
env = {'SSH_ASKPASS':'/path/to/myprog', 'DISPLAY':':9999'}
p = subprocess.Popen(['ssh', '-T', '-v', 'user@myhost.com'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
preexec_fn=os.setsid
)
如果您想以一种快速、肮脏的方式进行个人使用,您可以通过在终端中执行以下操作来启用这两台机器之间的无密码登录:
ssh-keygen -t rsa # generate a keypair (if you haven't done this already)
ssh-copy-id user@other_machine # copy your public key to the other machine
然后,您可以通过创建脚本(记住将其标记为可执行,例如,chmod 755 my_script.sh
)来执行ssh命令(子流程似乎无法直接接受ssh命令),其中包含您想要的内容,例如:
#!/bin/bash
ssh user@other_machine ls
并从您的程序中调用它:
import subprocess
response = subprocess.call("./my_script.sh")
print(response)
对于需要在其他人的机器上部署的应用程序的生产使用,我将使用abyx使用SSH库的方法。比处理一些环境变量简单得多。SSH总是在调用它的子进程中提示您,因此您需要做的是告诉
Popen()
使用p.stdin.write()发送密码
或者简化整个该死的事情,使用已经为您编写的SSH交互工具,例如Paramiko。谢谢您的回答。我尝试使用p.stdin.write()和p.communicate()发送密码,但是ssh仍然使用实际的TTY来读取密码。。。另一个问题是,当SSH请求密码时很难检测到(它可以使用不同的字符串进行检测,这些字符串可能会在SSH会话中出现,等等)。看到您愿意自己将密码“写入”SSH进程,我已经使用pexcpet
选项更新了我的答案,正如您在代码中看到的,我正在重定向标准输入,并且没有向它发送任何数据,因此它与我将stdin重定向到/dev/null的情况完全相同。还是我错了?我非常确信这个问题可以在不使用完整ssh实现的情况下得到解决:例如,GitGUI(用tcl/tk编写)可以从终端运行。它在后台运行ssh,并用自己的程序(gitgui--askpass)请求密码。我还尝试在调用Popen后立即关闭stdin,以确保代码模拟/dev/null行为。没有成功:(我同意PKI会简单得多,但是:1.我不能强制所有用户使用基于公钥的身份验证,2.如果私钥受密码保护,我仍然需要一些密码对话框支持…(不,不确定每个用户在启动我的程序之前是否已经将密钥添加到其ssh代理中…)这正是我想要的。你是我的英雄,先生。