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?
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. :)
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')
pour une solution prête à l'emploi, consultez pxssh du projet pxpect. Regardez la sshls.py et ssh_tunnel.py exemples.
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()