2009-11-24 16 views
8

Estoy escribiendo una GUI que usa comandos SSH. Traté de usar el módulo de subproceso para llamar a ssh y establecer la variable de entorno SSH_ASKPASS para que mi aplicación pueda mostrar una ventana que solicita la contraseña de SSH. Sin embargo, no puedo hacer que ssh lea la contraseña usando el comando SSH_ASKPASS dado: siempre lo solicita en la ventana del terminal, independientemente de cómo configuro las variables de entorno DISPLAY, SSH_ASKPASS, TERM o cómo canalizo la entrada/salida estándar. ¿Cómo puedo asegurarme de que ssh está separado del TTY actual y usar el programa dado para leer la contraseña?Cómo llamar a ssh por módulo de subproceso para que use la variable SSH_ASKPASS

Mi código de prueba fue:

#!/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 siempre te preguntará en el subproceso que lo llamó, entonces lo que tienes que hacer es decirle a 'Popen()' que envíe la contraseña usando 'p.stdin.write() 'o simplemente simplifique toda la maldita cosa y use algo para la interacción SSH que ya ha sido escrito para usted, como Paramiko. – jathanism

+0

Gracias por la respuesta. Intenté enviar la contraseña usando p.stdin.write() y p.communicate(), buth ssh todavía usaba el TTY real para leer la contraseña ... Y otro problema es que no es fácil de detectar cuando SSH solicita una contraseña (puede hacerlo con diferentes cadenas, que pueden aparecer más adelante en la sesión SSH, etc.) – gyim

+0

Al ver que está dispuesto a 'escribir' la contraseña usted mismo para el proceso SSH, he actualizado mi respuesta con la opción de usar 'pexcpet', que funcionará para eso. – abyx

Respuesta

15

SSH usa la variable SSH_ASKPASS solo si el proceso está realmente separado de TTY (redireccionamiento stdin y conjunto ting variables de entorno no es suficiente). Para separar un proceso de la consola, debe bifurcar y llamar a os.setsid(). Así que la primera solución que encontré fue:

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

También hay una manera elegante de hacer esto utilizando el módulo de subproceso: en el argumento preexec_fn podemos pasar una función de Python que se llama en el subproceso antes de ejecutar el comando externo . Así que la solución de la cuestión es una línea adicional:

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

Esto es exactamente lo que estoy buscando. Eres mi héroe, señor. – Ishpeck

4

Su problema es que SSH detecta su TTY y habla con él directamente (como se dice claramente en la página de manual). Puede probar y ejecutar ssh sin un terminal: la página de manual sugiere que podría ser necesario redireccionar stdin a /dev/null para que ssh piense que no tiene terminal.

También puede usar pexcept para esto, se sabe que funciona con SSH - ejemplo usage.

El manera correcta (TM) para hacer lo que estamos tratando de hacer es o bien:

  1. uso de una biblioteca específicamente para el uso de SSH en Python (por ejemplo twisted conch o paramiko)
  2. Uso claves públicas y privadas para que las contraseñas no sean necesarias
+0

Como puede ver en el código, yo _am_ redirijo la entrada estándar y no le envío datos, por lo que es bastante similar a como redirigí stdin a/dev/null. ¿O estoy equivocado? Estoy seguro de que este problema se puede resolver sin utilizar una implementación completa de ssh: por ejemplo, git-gui (escrito en tcl/tk) se puede ejecutar desde la terminal. Ejecuta ssh en segundo plano y solicita una contraseña con su propio programa (git-gui - askpass). – gyim

+0

También traté de cerrar stdin inmediatamente después de llamar a Popen para asegurarme de que el código emula el comportamiento/dev/null. Sin éxito :( – gyim

+0

+1 para PKI, es mucho más fácil de manejar mediante programación – JimB

1

Si desea una forma rápida y sucia de hacerlo para su propio uso personal, usted podría habilitar la entrada de sin contraseña entre estas dos máquinas al hacer esto en su terminal :

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 

entonces se puede obtener comandos ssh para ir a (subproceso parece que no puede aceptar comandos directamente ssh) mediante la creación de un guión (recuerde marcar ejecutable, por ejemplo chmod 755 my_script.sh) con las cosas que desea, tales como:

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

y llamarlo desde su programa:

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

Para la producción-uso de aplicaciones que necesitan ser desplegados en las máquinas de otras personas me gustaría ir con el enfoque de abyx de utilización de biblioteca SSH. Mucho más simple que jugar con algunas variables de entorno.

Cuestiones relacionadas