开发者

How to call ssh by subprocess module so that it uses SSH_ASKPASS variable

I am writing a GUI which uses SSH commands. I tried to use the subprocess module to call ssh and set the SSH_ASKPASS environment variable so that my application can pop up a window asking for the SSH password. However I cannot make ssh read the password using the given SSH_ASKPASS command: it always prompts it in the terminal window, regardless how I set the DISPLAY, SSH_ASKPASS, TERM environment variables or how I pipe the standard input/output. How can I make sure that ssh is detached from the current TTY and use the given program to read password?

My test code was:

#!/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.P开发者_C百科IPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    env=env
)
p.communicate()


SSH uses the SSH_ASKPASS variable only if the process is really detached from TTY (stdin redirecting and setting environment variables is not enough). To detach a process from console it should fork and call os.setsid(). So the first solution I found was:

# 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"

There is also an elegant way to do this using the subprocess module: in the preexec_fn argument we can pass a Python function that is called in the subprocess before executing the external command. So the solution for the question is one extra line:

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
)


Your problem is that SSH detects your TTY and talks to it directly (as is clearly stated in the man-page). You can try and run ssh without a terminal - the man page suggests it might be necessary to redirect stdin to /dev/null for ssh to think it has no terminal.

You can also use pexcept for this, it's known to work with SSH - example usage.

The Right Way (TM) to do what you're trying to do is either:

  1. Use a library specifically for using SSH in python (for example twisted conch or paramiko)
  2. Use public and private keys so that passwords will not be necessary


If you want a quick and dirty way of doing it for your own personal usage, you could enable passwordless login between these two machines by doing this in your terminal:

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

Then you can get ssh commands to go through (subprocess can't seem to accept ssh commands directly) by creating a script (remember to mark it executable, e.g. chmod 755 my_script.sh ) with the things you want, such as:

#!/bin/bash
ssh user@other_machine ls

and call it from your program:

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

For production-use of apps that need to be deployed on other people's machines I'd go with abyx's approach of using an SSH library. Much simpler than messing with some environment variables.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜