2009-11-24 62 views
8

我正在编写一个使用SSH命令的GUI。我试图使用subprocess模块​​来调用ssh并设置SSH_ASKPASS环境变量,以便我的应用程序可以弹出一个窗口询问SSH密码。但是我无法使用给定的SSH_ASKPASS命令使ssh读取密码:无论我如何设置DISPLAY,SSH_ASKPASS,TERM环境变量或如何管理标准输入/输出,它总是在终端窗口中提示它。我如何确保ssh与当前的TTY分离并使用给定的程序来读取密码?如何通过subprocess模块​​调用ssh,以便它使用SSH_ASKPASS变量

我的测试代码为:

#!/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', '[email protected]'], 
    stdin=subprocess.PIPE, 
    stdout=subprocess.PIPE, 
    stderr=subprocess.PIPE, 
    env=env 
) 
p.communicate() 
+0

SSH将始终在调用它的子进程中提示你,所以你需要做的是告诉'Popen()'通过使用'p.stdin来发送密码。write()'或者简化整个该死的事情,并使用已经为你写的SSH交互的东西,比如Paramiko。 – jathanism 2009-11-24 02:06:33

+0

感谢您的回答。我尝试使用p.stdin.write()和p.communicate()发送密码,但仍然使用实际的TTY读取密码......另一个问题是,在SSH询问密码时不容易检测(可以使用不同的字符串,这可以在SSH会话中稍后出现,等等) – gyim 2009-11-24 09:31:44

+0

看到您愿意将密码自己写入SSH进程,我已经使用选项更新了我的答案'pexcpet',这将为此工作。 – abyx 2009-11-24 10:03:15

回答

15

只有当进程真的与TTY分开时,SSH才会使用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', '[email protected]'], 
    stdin=subprocess.PIPE, 
    stdout=subprocess.PIPE, 
    stderr=subprocess.PIPE, 
    env=env, 
    preexec_fn=os.setsid 
) 
+0

这正是我所追求的,你是我的英雄,先生。 – Ishpeck 2011-08-04 20:41:34

4

您的问题是SSH检测到您的TTY并直接将其谈判(如手册页明确规定)。你可以尝试在没有终端的情况下运行ssh - 该手册页建议可能需要将stdin重定向到/dev/null以使ssh认为它没有终端。

您也可以使用pexcept进行此操作,它已知与SSH协同工作 - 示例usage

正确的方式(TM)做你想做什么或者是:

  1. 使用一个专门为在python使用SSH(例如twisted conchparamiko
  2. 使用库公钥和私钥,这样就不需要密码
+0

正如你在代码中看到的那样,我_am_重定向标准输入,并且我没有发送任何数据,所以它与我将stdin重定向到/ dev/null非常相似。或者我错了? 我很确定,如果不使用完整的ssh实现就可以解决这个问题:例如,可以从终端运行git-gui(用tcl/tk编写)。它在后台运行ssh,并用自己的程序(git-gui-askpass)请求密码。 – gyim 2009-11-24 09:19:33

+0

我也尝试在调用Popen后立即关闭stdin,以确保代码模拟/ dev/null行为。没有成功:( – gyim 2009-11-24 09:33:27

+0

+1为PKI,它更容易编程处理 – JimB 2009-11-24 16:53:43

1

如果你想这样做是为了自己的个人使用的快速和肮脏的方式,你可以在你的终端这样使这两台机器之间的密码登录:

ssh-keygen -t rsa # generate a keypair (if you haven't done this already) 
ssh-copy-id [email protected]_machine # copy your public key to the other machine 

然后你就可以得到SSH命令要经过(子似乎无法接受SSH命令直接)通过创建脚本(记住,以纪念它的可执行文件,例如chmod 755 my_script.sh)你想要的东西,比如作为:

#!/bin/bash 
ssh [email protected]_machine ls 

,并从你的程序中调用它:

import subprocess 
response = subprocess.call("./my_script.sh") 
print(response) 

对于生产使用需要被部署在别人的机器我会与使用SSH文库的abyx的方式去应用的。比搞乱一些环境变量简单得多。

相关问题