2009-12-16 11 views
28

Je suis en train de réécrire un script Bash que j'ai écrit en Python. Le coeur de ce script étaitSession SSH imbriquée avec Paramiko

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

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

The first method écrit à 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 crée un canal et utilise la prise comme envoyer et recv.

j'ai pu obtenir stdin.write travailler avec 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 [email protected]') 
stdin.write('secret') 
stdin.flush() 
print '---- out ----' 
print stdout.readlines() 
print '---- error ----' 
print stderr.readlines() 

ssh.close() 

... impressions ...

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

L'erreur pseudo-terminal me rappelle du drapeau -t dans ma commande d'origine, donc je suis passé à la deuxième 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 [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) 

... mais il imprime « ---- envoyer cmd ssh ---- » et se planter jusqu'à ce que je tue le processus. Je ne connais pas Python et je ne connais pas très bien les réseaux. Dans le premier cas, pourquoi l'envoi du mot de passe fonctionne-t-il avec sudo mais pas avec ssh? Les invites sont-elles différentes? Paramiko est-il même la bonne bibliothèque pour cela?

Répondre

24

J'ai réussi à trouver une solution, mais cela demande un petit travail manuel. Si quelqu'un a une meilleure solution, s'il vous plaît dites-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 que, au lieu de cette

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 je était un enfant et a abandonné le codage pendant dix ans. :)

+0

J'essaie d'utiliser votre solution pour un besoin similaire .. Cependant ('un-invite $') n'est pas toujours fixe .. il a toujours un '#' mais un autre contenu qui continue à varier..comment je compte pour cela? – Amistad

+0

Si elle se termine par '#', alors changez 'une-invite $ 'en' # '. Cependant, assurez-vous de vérifier l'espace blanc de fin, c'est-à-dire '... endswith ('#')' n'est pas la même chose que '... endswith ('#')'. – mqsoh

+0

serait l'utilité attendue (une chose TCL) vous aider? L'équivalent python est pexpect je crois ... – Jimbo

14

Voici un petit exemple en utilisant paramiko uniquement (et redirection de port):

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

Pourriez-vous préciser le port que vous transférez et l'hôte qui effectue le transfert dans votre exemple? –

+0

comment faites-vous le transfert de port? –

+0

Ajout d'un commentaire dans le code et fait un petit changement re. expéditeur. – Sinas

0

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

Les travaux ci-dessous sur un système qui nécessite TTY et invite également le mot de passe sudo

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

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

+0

Merci. C'est la meilleure méthode que j'ai trouvée. – Herman

+0

Ceci est le meilleur et devrait être marqué comme la bonne réponse. –

+0

Il devrait en effet être marqué comme la bonne réponse. – Palisand