Enviar una contraseña a través de SSH o SCP con subprocess.Popen

Estoy intentando ejecutar un comando scp (copia segura) usando subprocess.Popen . El inicio de sesión requiere que envíe una contraseña:

 from subprocess import Popen, PIPE proc = Popen(['scp', "user@10.0.1.12:/foo/bar/somefile.txt", "."], stdin = PIPE) proc.stdin.write(b'mypassword') proc.stdin.flush() 

Esto devuelve inmediatamente un error:

 user@10.0.1.12's password: Permission denied, please try again. 

Estoy seguro de que la contraseña es correcta. Lo verifico fácilmente invocando manualmente scp en el shell. Entonces, ¿por qué no funciona esto?

Tenga en cuenta que hay muchas preguntas similares a esto, que se subprocess.Popen al subprocess.Popen y enviar una contraseña para el inicio de sesión automático mediante SSH o FTP:

¿Cómo puedo configurar una contraseña de usuario en linux desde un script de python?
Usa subproceso para enviar una contraseña

La (s) respuesta (s) a estas preguntas no funcionan y / o no se aplican porque estoy usando Python 3.

La segunda respuesta que vinculó sugiere que use Pexpect (que suele ser la forma correcta de interactuar con los progtwigs de línea de comandos que esperan entrada). Hay una bifurcación que funciona para python3 y que puedes usar.

Aquí hay una función para ssh con una contraseña usando pexpect :

 def ssh(host, cmd, user, password, timeout=30, bg_run=False): """SSH'es to a host using the supplied credentials and executes a command. Throws an exception if the command doesn't return 0. bgrun: run command in the background""" fname = tempfile.mktemp() fout = open(fname, 'w') options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no' if bg_run: options += ' -f' ssh_cmd = 'ssh %s@%s %s "%s"' % (user, host, options, cmd) child = pexpect.spawn(ssh_cmd, timeout=timeout) child.expect(['password: ']) child.sendline(password) child.logfile = fout child.expect(pexpect.EOF) child.close() fout.close() fin = open(fname, 'r') stdout = fin.read() fin.close() if 0 != child.exitstatus: raise Exception(stdout) return stdout 

Algo similar debería ser posible utilizando scp .

La utilidad scp OpenSSH invoca el progtwig ssh para realizar la conexión SSH con el host remoto, y el proceso ssh maneja la autenticación. La utilidad ssh no acepta una contraseña en la línea de comandos o en su entrada estándar. Creo que esta es una decisión deliberada por parte de los desarrolladores de OpenSSH, porque creen que la gente debería usar mecanismos más seguros como la autenticación basada en claves. Cualquier solución para invocar ssh va a seguir uno de estos enfoques:

  1. Use una clave SSH para la autenticación, en lugar de una contraseña.
  2. Use sshpass , expect o una herramienta similar para automatizar la respuesta a la solicitud de contraseña.
  3. Use (use abusivamente) la función SSH_ASKPASS para que ssh obtenga la contraseña invocando otro comando, que se describe aquí o aquí , o en algunas de las respuestas aquí .
  4. Obtenga el administrador del servidor SSH para habilitar la autenticación basada en host y usar eso. Tenga en cuenta que la autenticación basada en host solo es adecuada para ciertos entornos de red. Ver notas adicionales aquí y aquí .
  5. Escriba su propio cliente ssh utilizando perl, python, java o su idioma favorito. Hay bibliotecas de cliente ssh disponibles para la mayoría de los lenguajes de progtwigción modernos, y tendría un control total sobre cómo el cliente obtiene la contraseña.
  6. Descargue el código fuente de ssh y compile una versión modificada de ssh que funcione de la manera que desee.
  7. Utilice un cliente ssh diferente. Hay otros clientes ssh disponibles, tanto gratuitos como comerciales. Uno de ellos podría satisfacer sus necesidades mejor que el cliente OpenSSH.

En este caso particular, dado que ya está invocando scp desde un script de python, parece que uno de estos sería el enfoque más razonable:

  1. Use pexpect , el módulo de python expect, para invocar a scp y darle la contraseña.
  2. Use paramiko , la implementación de python ssh, para realizar esta tarea ssh en lugar de invocar un progtwig externo.

Pexpect tiene una biblioteca para exactamente esto: pxssh

http://pexpect.readthedocs.org/en/stable/api/pxssh.html

 import pxssh import getpass try: s = pxssh.pxssh() hostname = raw_input('hostname: ') username = raw_input('username: ') password = getpass.getpass('password: ') s.login(hostname, username, password) s.sendline('uptime') # run a command s.prompt() # match the prompt print(s.before) # print everything before the prompt. s.logout() except pxssh.ExceptionPxssh as e: print("pxssh failed on login.") print(e) 

Supongo que algunas aplicaciones interactúan con el usuario usando stdin y algunas aplicaciones interactúan con terminal. En este caso, cuando escribimos la contraseña usando PIPE, estamos escribiendo a stdin. Pero la aplicación SCP lee la contraseña del terminal. Como el subproceso no puede interactuar con el usuario usando el terminal, pero solo puede interactuar con la entrada estándar, no podemos usar el módulo de subproceso y debemos usar pexpect para copiar el archivo usando scp.

Siéntase libre de correcciones.