it-swarm.com.de

Verschachtelte SSH-Sitzung mit Paramiko

Ich schreibe ein Bash-Skript um, das ich in Python geschrieben habe. Der Kern dieses 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 auf meine genaue Situation bezogen, aber ich konnte mit Sudo auf einem entfernten Host Beispiele finden.

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

Das zweite erstellt einen Kanal und verwendet das socketartige send und recv .

Ich konnte stdin.write bekommen, um mit Sudo zu arbeiten, aber es funktioniert nicht mit ssh auf dem fernen 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 [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']

Der Pseudo-Terminal-Fehler erinnerte mich an das Flag -t in meinem ursprünglichen Befehl. Daher wechselte ich zur zweiten Methode mit einem Channel. 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 [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 '---- send ssh cmd ----' und hängt so lange, bis ich den Prozess abschalte.

Ich bin neu in Python und keiner kennt sich mit Netzwerken aus. Warum funktioniert das Senden des Passworts im ersten Fall mit Sudo , jedoch nicht mit ssh ? Unterscheiden sich die Aufforderungen? Ist paramiko dafür sogar die richtige Bibliothek?

29
mqsoh

Es ist mir gelungen, eine Lösung zu finden, die jedoch ein wenig Handarbeit erfordert. Wenn jemand eine bessere Lösung hat, sag 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()

Die Sache, die zu beachten ist, ist das stattdessen

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

...du willst das

chan = ssh.invoke_Shell()

Es erinnert mich daran, als ich als Kind versucht hatte, ein TradeWars-Skript zu schreiben, und habe das Codieren zehn Jahre lang aufgegeben. :)

25
mqsoh

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

Sie können eine SSH-Verbindung mithilfe eines Kanals aus einer anderen SSH-Verbindung erstellen. Siehe hier für weitere Details.

7
David Lim

Für eine fertige Lösung checken Sie pxssh aus dem pxpect-Projekt aus. Schauen Sie sich die Beispiele sshls.py und ssh_tunnel.py an.

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

1
snies

Sinas 'Antwort funktioniert gut, hat aber nicht die gesamte Ausgabe sehr langer Befehle für mich geliefert. Mit chan.makefile () kann ich jedoch die gesamte Ausgabe abrufen.

Das Folgende funktioniert auf einem System, das tty erfordert, und fordert auch zur Eingabe des Sudo-Passworts auf

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