2009-11-24 7 views
8

Sto scrivendo una GUI che utilizza i comandi SSH. Ho provato a utilizzare il modulo subprocess per chiamare ssh e impostare la variabile di ambiente SSH_ASKPASS in modo che la mia applicazione possa apparire in una finestra per chiedere la password SSH. Tuttavia non riesco a fare ssh a leggere la password usando il comando SSH_ASKPASS dato: lo richiede sempre nella finestra del terminale, indipendentemente da come imposto le variabili d'ambiente DISPLAY, SSH_ASKPASS, TERM o il modo in cui canalizzo l'input/output standard. Come posso assicurarmi che ssh sia staccato dal TTY corrente e usi il programma dato per leggere la password?Come chiamare ssh dal modulo di sottoprocesso in modo che utilizzi la variabile SSH_ASKPASS

Il mio codice di prova era:

#!/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 ti chiederà sempre nel sottoprocesso che lo ha chiamato, quindi quello che devi fare è dire 'Popen()' per inviare la password usando 'p.stdin.scrivi() 'o semplicemente semplifica l'intera dannata cosa e usa qualcosa per l'interazione SSH che è già stata scritta per te come Paramiko. – jathanism

+0

Grazie per la risposta. Ho provato a inviare la password usando p.stdin.write() e p.communicate(), ma ssh usa ancora il TTY effettivo per leggere la password ... E un altro problema è che non è facile da rilevare quando SSH chiede la password (può farlo con stringhe diverse, che possono apparire in seguito nella sessione SSH, ecc.) – gyim

+0

Visto che sei disposto a "scrivere" la password tu stesso per il processo SSH, ho aggiornato la mia risposta con l'opzione da usare 'pexcpet', che funzionerà per quello. – abyx

risposta

15

SSH utilizza la variabile SSH_ASKPASS solo se il processo è davvero staccato da TTY (reindirizzamento stdin e impostare variabili di ambiente non è sufficiente). Per staccare un processo dalla console, si dovrebbe fork e chiamare os.setsid(). Così la prima soluzione che ho trovato è stato:

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

C'è anche un elegante modo per farlo utilizzando il modulo sottoprocesso: nell'argomento preexec_fn possiamo passare una funzione Python che viene chiamato nel sottoprocesso prima di eseguire il comando esterno . Quindi la soluzione per la domanda è una linea in più:

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

Questo è esattamente quello che sto cercando. Sei il mio eroe, signore. – Ishpeck

4

tuo problema è che SSH rileva il TTY e parla direttamente ad esso (come è chiaramente indicato nella man-page). Puoi provare a eseguire ssh senza un terminale: la pagina man suggerisce che potrebbe essere necessario reindirizzare stdin a /dev/null affinché ssh pensi che non abbia un terminale.

È anche possibile utilizzare pexcept per questo, è noto per funzionare con SSH - esempio usage.

The Right Way (TM) per fare ciò che si sta cercando di fare è o:

  1. utilizzare una libreria appositamente per l'utilizzo di SSH in python (per esempio twisted conch o paramiko)
  2. Usa chiavi pubbliche e private in modo che le password non saranno necessarie
+0

Come si può vedere nel codice, io _am_ reindirizza lo standard input, e non invio alcun dato ad esso, quindi è proprio come ho reindirizzato a/dev/null. O mi sbaglio? Sono sicuro che questo problema può essere risolto senza utilizzare un'implementazione completa di ssh: ad esempio, git-gui (scritto in tcl/tk) può essere eseguito dal terminale. Esegue ssh in background e richiede la password con il proprio programma (git-gui - askpass). – gyim

+0

Ho anche provato a chiudere lo stdin subito dopo aver chiamato Popen per assicurarmi che il codice emuli il comportamento di/dev/null. Senza successo :( – gyim

+0

+1 per PKI, è molto più facile da gestire a livello di programmazione – JimB

1

Se volete un modo rapido e sporco di farlo per il proprio uso personale, si potrebbe abilitare l'accesso senza password tra queste due macchine facendo questo nel terminale :

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 

allora si può ottenere ssh comandi passare attraverso (sottoprocesso non riesco ad accettare SSH comandi direttamente) con la creazione di uno script (ricordatevi di segnare eseguibile, ad esempio chmod 755 my_script.sh) con le cose che vuoi, come ad come:

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

e chiamare dal programma:

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

Per la produzione-uso di applicazioni che hanno bisogno di essere implementato su macchine di altre persone mi piacerebbe andare con l'approccio di abyx di utilizzare una libreria SSH. Molto più semplice che scherzare con alcune variabili d'ambiente.

Problemi correlati