Session SSH imbriquée avec Paramiko

je réécris un script Bash que j'ai écrit en Python. Le cœur de ce script était

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

j'ai un problème avec l'authentification imbriquée avec paramiko. Je n'ai pu trouver aucun exemple traitant de Ma situation précise, mais j'ai pu trouver des exemples avec sudo sur un hôte distant.

La première méthode écrit à stdin

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

deuxième crée un canal et utilise le socket-comme envoyer et recv.

j'ai été en mesure d'obtenir stdin.Ecrire sudo, mais il ne fonctionne pas avec ssh sur l'hôte distant.

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

...imprimer...

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

l'erreur de pseudo-terminal m'a rappelé le drapeau-t dans ma commande originale, donc je suis passé à la seconde méthode, en utilisant un canal. Au lieu de ssh.exec_command et plus tard, j'ai:

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)

...mais il imprime "- - - - envoyer SSH cmd - - - - - " et pend juste jusqu'à ce que je tue le processus.

Je suis nouveau en Python et je ne connais rien aux réseaux. Dans le premier cas, pourquoi l'envoi du mot de passe fonctionne-t-il avec sudo mais pas ssh? Sont les invites de différent? Est-ce que paramiko est la bonne bibliothèque pour ça?

29
demandé sur mqsoh 2009-12-16 04:28:06

5 réponses

j'ai réussi à trouver une solution, mais il nécessite un peu de travail manuel. Si quelqu'un a une meilleure solution, dites-le-moi.

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

la chose à noter est qu'au lieu de cela

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

...vous voulez que ce

chan = ssh.invoke_shell()

cela me rappelle quand j'ai essayé d'écrire un script TradeWars quand j'étais enfant et que j'ai abandonné le codage pendant dix ans. :)

25
répondu mqsoh 2009-12-16 19:05:25

voici un petit exemple utilisant paramiko seulement (et Port forwarding):

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
répondu Sinas 2012-05-04 13:00:48

vous pouvez créer une connexion ssh en utilisant le canal d'une autre connexion ssh. Voir ici pour plus de détails.

7
répondu David Lim 2017-05-23 11:46:51

pour une solution prête à l'emploi, consultez pxssh du projet pxpect. Regardez la sshls.py et ssh_tunnel.py exemples.

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

1
répondu snies 2010-03-24 19:54:18

la réponse de Sinas fonctionne bien mais n'a pas fourni toute la sortie des commandes très longues pour moi. Cependant, en utilisant chan.makefile () me permet de récupérer toute la sortie.

ci-dessous fonctionne sur un système qui nécessite tty et aussi des instructions pour sudo password

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
répondu user2945126 2013-11-01 14:28:21