37 Stimmen

Verschachtelte SSH-Sitzung mit Paramiko

Ich schreibe gerade ein Bash-Skript, das ich geschrieben habe, in Python um. Der Kern des Skripts war

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 befassen, aber ich konnte Beispiele finden mit sudo auf einem entfernten Host.

Die erste Methode schreibt an 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()

Die zweite erstellt einen Kanal und verwendet die socket-ähnliche senden. y recv .

Ich konnte die stdin.write zur Arbeit mit sudo aber es funktioniert nicht mit ssh auf dem entfernten Host.

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 luser@second.com')
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']

Der Pseudoterminal-Fehler erinnerte mich an die -t-Flagge in meinem ursprünglichen Befehl, so dass ich zur zweiten Methode wechselte und einen Kanal verwendete. Anstelle von ssh.exec_command und später, habe ich:

t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()
print '---- send ssh cmd ----'
print chan.send('ssh luser@second.com')
print '---- recv ----'
print chan.recv(9999)
chan = t.open_session()
print '---- send password ----'
print chan.send('secret')
print '---- recv ----'
print chan.recv(9999)

...aber er gibt '---- send ssh cmd ----' aus und bleibt einfach hängen, bis ich den Prozess beende.

Ich bin neu in Python und kenne mich nicht allzu gut mit Netzwerken aus. Im ersten Fall, warum funktioniert das Senden des Passworts mit sudo aber nicht mit ssh ? Sind die Eingabeaufforderungen unterschiedlich? Ist paramiko überhaupt die richtige Bibliothek für diese Aufgabe?

33voto

mqsoh Punkte 3160

Es ist mir gelungen, eine Lösung zu finden, die allerdings ein wenig Handarbeit erfordert. Wenn jemand eine bessere Lösung hat, sagen Sie es mir bitte.

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()

Dabei ist zu beachten, dass anstelle dieses

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

...Sie wollen das

chan = ssh.invoke_shell()

Das erinnert mich daran, wie ich als Kind versucht habe, ein TradeWars-Skript zu schreiben und das Programmieren für zehn Jahre aufgegeben habe :)

17voto

Sinas Punkte 171

Hier ist ein kleines Beispiel, das nur paramiko (und Portweiterleitung) verwendet:

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')

7voto

David Lim Punkte 81

Sie können eine ssh-Verbindung über den Kanal einer anderen ssh-Verbindung herstellen. Siehe aquí für weitere Einzelheiten.

1voto

snies Punkte 3365

Eine fertige Lösung ist pxssh aus dem pxpect-Projekt. Sehen Sie sich die Beispiele sshls.py und ssh_tunnel.py an.

http://www.noah.org/wiki/Pexpect

0voto

user2945126 Punkte 1

Die Antwort von Sinas funktioniert gut, lieferte mir aber nicht alle Ausgaben von sehr langen Befehlen. Mit chan.makefile() kann ich jedoch die gesamte Ausgabe abrufen.

Das Folgende funktioniert auf einem System, das tty benötigt und auch nach dem sudo-Passwort fragt

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()

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X