我正在编写一个使用SSH命令的GUI。我尝试使用子进程模块来调用ssh并设置SSH_ASKPASS环境变量,以便我的应用程序可以弹出一个窗口,询问SSH密码。但是我无法使用给定的SSH_ASKPASS命令使ssh读取密码:它总是在终端窗口中提示它,无论我如何设置DISPLAY,SSH_ASKPASS,TERM环境变量或如何管道标准输入/输出。如何确保ssh与当前TTY分离并使用给定程序读取密码?
我的测试代码是:
#!/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()
答案 0 :(得分:15)
SSH仅在进程与TTY分离时才使用SSH_ASKPASS变量(stdin重定向和设置环境变量是不够的)。要从控制台分离进程,它应该fork并调用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"
使用子进程模块还有一种优雅的方法:在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
)
答案 1 :(得分:4)
您的问题是SSH检测到您的TTY并直接与它进行对话(如手册页中明确指出的那样)。您可以尝试在没有终端的情况下运行ssh - 手册页建议可能需要将stdin
重定向到/dev/null
以使ssh认为它没有终端。
您也可以使用pexcept,知道可以使用SSH - 例如usage。
正确的方式(TM)做你想做的事情是:
答案 2 :(得分:1)
如果您想快速而肮脏地将其用于个人用途,您可以在终端中启用无密码登录:
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库的方法。比搞乱一些环境变量要简单得多。