2009-12-16 31 views
28

Estoy reescribiendo un guión Bash que escribí en Python. El quid de esa secuencia de comandos eraSesión anidada SSH con Paramiko

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

Tengo un problema con la autenticación anidada con paramiko. No pude encontrar ningún ejemplo relacionado con mi situación exacta, pero pude encontrar ejemplos con sudo en un host remoto.

The first method escribe en la entrada estándar

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 crea un canal y utiliza la forma de casquillo enviar y recv .

que era capaz de conseguir stdin.write trabajar con sudo, pero no funciona con ssh en el host remoto.

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

... impresiones ...

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

El error pseudo-terminal me recordó de la bandera -t en mi comando original, por lo que me pasa a segundo método, utilizando un canal. En lugar de ssh.exec_command y más tarde, tengo:

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) 

... pero se imprime '---- ---- ssh enviar cmd' y sólo se bloquea hasta que matar el proceso.

Soy nuevo en Python y no tengo conocimientos sobre redes. En el primer caso, ¿por qué el envío de la contraseña funciona con sudo pero no con ssh? ¿Son las indicaciones diferentes? ¿Paramiko es incluso la biblioteca adecuada para esto?

Respuesta

24

Logré encontrar una solución, pero requiere un poco de trabajo manual. Si alguien tiene una mejor solución, por favor dígame.

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

Lo que hay que tener en cuenta es que en lugar de esto

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

... desea que esta

chan = ssh.invoke_shell() 

Me recuerda cuando traté de escribir un guión cuando TradeWars era un niño y dejó de codificar durante diez años. :)

+0

estoy tratando de usar su solución para una necesidad similar ... Sin embargo, ('algún-prompt $') no siempre se soluciona ... siempre tiene un '#' pero algún otro contenido que varía ... ¿Cómo lo hago? cuenta para ello? – Amistad

+0

Si termina con '#', entonces cambie 'some-prompt $' a '#'. Sin embargo, asegúrese de verificar el espacio en blanco al final, es decir, '... endswith ('#')' no es lo mismo que '... endswith ('#')'. – mqsoh

+0

¿la utilidad esperada (algo de TCL) te ayudaría? El equivalente de python es pexpect, creo ... – Jimbo

14

Aquí hay un pequeño ejemplo utilizando sólo paramiko (y el reenvío de puertos):

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

¿Podría explicar en qué puerto está reenviando y qué host está haciendo el reenvío en su ejemplo? –

+0

¿cómo está el reenvío de puertos? –

+0

Agregó un comentario en el código e hizo un pequeño cambio re. reenvío. – Sinas

0

respuesta de Sina funciona bien, pero no proporcionó toda la salida de los comandos muy largos para mí. Sin embargo, usando chan.makefile() me permite recuperar todo el resultado.

Los trabajos siguientes en un sistema que requiere TTY y también le pide la contraseña 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

Puede crear conexión SSH utilizando el canal de otra conexión ssh. Ver here para más detalles.

+0

Gracias. Es el mejor método que he encontrado. – Herman

+0

Este es el mejor y debe marcarse como la respuesta correcta. –

+0

De hecho, debería marcarse como la respuesta correcta. – Palisand

Cuestiones relacionadas