2009-12-16 10 views
28

Ich überschreibe ein Bash-Skript, das ich in Python geschrieben habe. Der Kern dieses Skripts warVerschachtelte SSH-Sitzung mit Paramiko

ssh -t first.com "ssh second.com very_remote_command" 

Ich habe ein Problem mit der verschachtelten Authentifizierung mit Paramiko. Ich konnte keine Beispiele finden, die sich mit meiner genauen Situation befassten, aber ich konnte Beispiele mit sudo auf einem entfernten Host finden.

The first method schreibt stdin

ssh.connect('127.0.0.1', username='jesse', password='lol') 
stdin, stdout, stderr = ssh.exec_command("sudo dmesg") 
stdin.write('lol\n') 
stdin.flush() 

The second einen Kanal erstellt und verwendet die pfannenartige senden und recv.

konnte ich stdin.write arbeiten mit sudo, erhalten, aber es mit ssh nicht auf dem entfernten Rechner arbeiten.

import paramiko 

ssh = paramiko.SSHClient() 
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect('first.com', username='luser', password='secret') 
stdin, stdout, stderr = ssh.exec_command('ssh [email protected]') 
stdin.write('secret') 
stdin.flush() 
print '---- out ----' 
print stdout.readlines() 
print '---- error ----' 
print stderr.readlines() 

ssh.close() 

... druckt ...

---- out ---- 
[] 
---- error ---- 
['Pseudo-terminal will not be allocated because stdin is not a terminal.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied (publickey,password,keyboard-interactive).\r\n'] 

Die Pseudo-Terminal Fehler erinnerten mich an der -t Flagge in meinem ursprünglichen Befehl, so wechselte ich zu der zweiten Methode, einen Kanal. Statt ssh.exec_command und später, ich habe:

t = ssh.get_transport() 
chan = t.open_session() 
chan.get_pty() 
print '---- send ssh cmd ----' 
print chan.send('ssh [email protected]') 
print '---- recv ----' 
print chan.recv(9999) 
chan = t.open_session() 
print '---- send password ----' 
print chan.send('secret') 
print '---- recv ----' 
print chan.recv(9999) 

... aber es druckt ‚---- ssh cmd senden ----‘ und hängt nur, bis ich den Prozess zu beenden.

Ich bin neu in Python und nicht zu gut über Netzwerke. Im ersten Fall, warum funktioniert das Senden des Passworts mit sudo aber nicht mit ssh? Sind die Eingabeaufforderungen unterschiedlich? Ist Paramiko dafür sogar die richtige Bibliothek?

Antwort

24

Ich habe es geschafft, eine Lösung zu finden, aber es erfordert ein wenig manuelle Arbeit. Wenn jemand eine bessere Lösung hat, bitte sag es mir.

ssh = paramiko.SSHClient() 
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect('first.com', username='luser', password='secret') 

chan = ssh.invoke_shell() 

# Ssh and wait for the password prompt. 
chan.send('ssh second.com\n') 
buff = '' 
while not buff.endswith('\'s password: '): 
    resp = chan.recv(9999) 
    buff += resp 

# Send the password and wait for a prompt. 
chan.send('secret\n') 
buff = '' 
while not buff.endswith('some-prompt$ '): 
    resp = chan.recv(9999) 
    buff += resp 

# Execute whatever command and wait for a prompt again. 
chan.send('ls\n') 
buff = '' 
while not buff.endswith('some-prompt$ '): 
    resp = chan.recv(9999) 
    buff += resp 

# Now buff has the data I need. 
print 'buff', buff 

ssh.close() 

Die Sache ist zu beachten, dass anstelle dieses

t = ssh.get_transport() 
chan = t.open_session() 
chan.get_pty() 

... Sie diese

chan = ssh.invoke_shell() 
wollen

Es erinnert mich an, als ich versuchte, ein TradeWars Skript zu schreiben, wenn ich war ein Kind und gab die Programmierung für zehn Jahre auf. :)

+0

Ich versuche, Ihre Lösung für eine ähnliche Notwendigkeit zu verwenden .. Jedoch ('einige-prompt $') ist nicht immer behoben .. es hat immer ein '#', aber einige andere Inhalte, die variiert..wie mache ich Konto dafür? – Amistad

+0

Wenn es mit '#' endet, ändern Sie 'some-prompt $' in '#'. Stellen Sie jedoch sicher, dass Sie nach weißem Leerzeichen suchen, d. H. '... endswith ('#')' ist nicht dasselbe wie '... endswith (' # ') '. – mqsoh

+0

würde das Expect Utility (eine TCL-Sache) Ihnen helfen? Das Python-Äquivalent ist pexpect, glaube ich ... – Jimbo

14

Hier ist ein kleines Beispiel nur mit paramiko (und Port-Weiterleitung):

import paramiko as ssh 

class SSHTool(): 
    def __init__(self, host, user, auth, 
       via=None, via_user=None, via_auth=None): 
     if via: 
      t0 = ssh.Transport(via) 
      t0.start_client() 
      t0.auth_password(via_user, via_auth) 
      # setup forwarding from 127.0.0.1:<free_random_port> to |host| 
      channel = t0.open_channel('direct-tcpip', host, ('127.0.0.1', 0)) 
      self.transport = ssh.Transport(channel) 
     else: 
      self.transport = ssh.Transport(host) 
     self.transport.start_client() 
     self.transport.auth_password(user, auth) 

    def run(self, cmd): 
     ch = self.transport.open_session() 
     ch.set_combine_stderr(True) 
     ch.exec_command(cmd) 
     retcode = ch.recv_exit_status() 
     buf = '' 
     while ch.recv_ready(): 
      buf += ch.recv(1024) 
     return (buf, retcode) 

# The example below is equivalent to 
# $ ssh 10.10.10.10 ssh 192.168.1.1 uname -a 
# The code above works as if these 2 commands were executed: 
# $ ssh -L <free_random_port>:192.168.1.1:22 10.10.10.10 
# $ ssh 127.0.0.1:<free_random_port> uname -a 
host = ('192.168.1.1', 22) 
via_host = ('10.10.10.10', 22) 

ssht = SSHTool(host, 'user1', 'pass1', 
    via=via_host, via_user='user2', via_auth='pass2') 

print ssht.run('uname -a') 
+0

Können Sie näher erläutern, welchen Port Sie weiterleiten und welcher Host die Weiterleitung in Ihrem Beispiel durchführt? –

+0

Wie geht es Portweiterleitung? –

+0

Ein Kommentar im Code hinzugefügt und eine kleine Änderung re. Weiterleitung – Sinas

0

Sinas Antwort funktioniert gut, aber haben von sehr langen Befehle für mich die ganze Ausgabe nicht zur Verfügung stellen. Verwenden Sie jedoch chan.Makefile() erlaubt mir, alle Ausgaben abzurufen.

Die nachfolgenden Arbeiten an einem System, das tty erfordert und fordert auch für sudo Passwort

ssh = paramiko.SSHClient() 
ssh.load_system_host_keys() 
ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) 
ssh.connect("10.10.10.1", 22, "user", "password") 
chan=ssh.get_transport().open_session() 
chan.get_pty() 
f = chan.makefile() 
chan.exec_command("sudo dmesg") 
chan.send("password\n") 
print f.read() 
ssh.close() 
7

Sie können SSH-Verbindung mit Kanal von einer anderen SSH-Verbindung erstellen. Weitere Informationen finden Sie unter here.

+0

Danke. Es ist die beste Methode, die ich gefunden habe. – Herman

+0

Dies ist das beste und sollte als die richtige Antwort markiert werden. –

+0

Es sollte in der Tat als die richtige Antwort markiert werden. – Palisand