Automatización de transferencias por SFTP
Ante la necesidad de transferir archivos entre servidores de forma automatizada (sin intervención humana) recordé la excelente opción que otorga openSSH en su suite de herramientas: autenticación mediante clave pública/privada.
Dado que en un script es bastante feo lograr la interacción requerida por SFTP para autenticar un usuario (comportamiento default), contar con esta opción es extremadamente útil. Sí, no es imposible realizar la autenticación de forma interactiva, algo que está muy bien explicado en varias páginas e hice en un script de backup hace un par de años, pero es mucho más elegante usar la otra opción.

La idea de este artículo es explicar en pocos pasos cómo configurar tanto el cliente como el servidor SSH para utilizar autenticación de clave pública/privada. Además les mostraré cómo escribir un script que, utilizando esta autenticación, sirve para enviar archivos de forma automática.
Como si esto fuera poco (?!), también les mostraré como escribir un script que utilice autenticación interactiva, a través de la herramienta expect. Todo claro?, entonces "a darle átomos".


Generación del par de claves pública/privada

Como la autenticación será a través de estas claves, necesitaremos generar un par por cada usuario que utilicemos. La generación es muy simple, gracias a la herramienta ssh-keygen (si suena a generador de serials para crackear aplicaciones). Las claves se pueden generar en cualquier máquina, no necesariamente el servidor o el cliente. El comando a ejecutar es:
$ ssh-keygen -f id_rsa -t rsa
donde:
-f especifica el nombre del archivo. Se generan dos archivos, la clave privada con el nombre que pasamos en el parámetro, y la pública con el nombre concatenado a .pub
-t especifica el algoritmo que deriva las claves (puede ser RSA o DSA, lo más común es usar RSA, aunque DSA es más seguro).
Ejemplo:
$ ssh-keygen -f id_rsa -t rsa
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
68:82:cb:67:8d:b1:4c:e1:3c:da:22:c0:c3:22:48:c2 demasiadovivo@192.168.1.1
The key's randomart image is:
+--[ RSA 2048]----+
| |
|. |
|.E . |
|* + . . |
|=+. B o S |
|+..* O |
|. = B . |
| . + |
| |
+-----------------+
Cuando intentemos generar las claves, el programa preguntará si deseamos asignarle un passphrase para proteger la clave privada. En este caso hay que dar enter porque si asignamos una passphrase, cada vez que deseemos autenticar un cliente, tendremos que poner dicha passphrase a mano, para desbloquer la clave privada... esto no serviría a nuestros propósitos de utilizar un script automático...

Del par generado, utilizaremos la clave pública en el servidor (id_rsa.pub en el ejemplo) y la privada en el cliente (id_rsa).


Configuración del servidor SSH/SFTP

En el servidor lo primero que tenemos que hacer es habilitar las opciones que permiten la autenticación con clave pública/privada, es decir, editando el archivo /etc/ssh/sshd_config (o /etc/sshd_config en otras distros):
RSAAuthentication yes
PubkeyAuthentication yes
Luego debemos copiar la clave pública del cliente en el archivo de claves autorizadas para ese usuario /home/usuario/.ssh/authorized_keys:
cat id_rsa.pub >> /home/usuario/.ssh/authorized_keys

Conexión desde el cliente

En el cliente tenemos dos alternativas: pasar la clave privada como parámetro de la llamada sftp, o copiar la clave privada en /home/usuario/.ssh/. En el segundo caso, al intentar loguearnos en el servidor remoto, sftp utilizará por default la clave que encuentre en el directorio ssh del home, es decir, podemos ejecutar:
sftp <usuario>@<hostname>
por ej: sftp demasiadovivo@192.168.1.2
Si necesitamos pasar la clave por parámetro (algo en muchos casos necesario porque el usuario que ejecuta el script puede no tener home), debemos ejecutar el siguiente comando:
sftp -o "IdentityFile=<clave-privada>" <nombre-usuario>@<hostname>
por ej: sftp "IdentityFile=/datos/id_rsa" demasiadovivo@192.168.1.2
Me costó bastante encontrar la forma de armar este último comando, es necesario leer bien el manual de ssh_config =S


Automatizar copiado de archivos

Como les comenté, la razón por la cual necesitaba lo anterior es para automatizar el envío de archivos de un servidor a otro. Una vez que tenemos la configuración necesaria, en el servidor1 o servidor cliente del servidor2, podemos colocar el siguiente script:
#!/bin/bash

CMDFILE="sftp-commands"

echo "put <path-archivo>
bye" > $CMDFILE
sftp -o "IdentityFile=<clave-privada>" -b $CMDFILE <nombre-usuario>@<hostname>
rm $CMDFILE
Como pueden observar, el script es muy simple. Lo que hace es usar sftp en modo batch (-b $CMDFILE) para poder enviar los comandos de forma no-interactiva, ya que en el comportamiento default sftp espera recibir de la standar input los comandos a ejecutar en el servidor.
En resúmen, crea un archivo temporal cuyo nombre es sftp-commands, en el cual coloca los comandos a ejecutar (por ejemplo, el put de un archivo). Luego llama a sftp utilizando autenticación de clave pública/privada y el modo batch, donde le pasa el archivo recién creado como input. Finalmente borra el archivo temporal.


Automatización, opción 2

El segundo script que les quería mostrar es cómo hacer lo anterior, pero con autenticación interactiva, algo un poco más sucio, pero igualmente útil:
#!/usr/bin/expect

spawn sftp <usuario>@<hostname>
expect "password:"
sleep 3
send "<mi-contraseña>\r";
expect ">"
send "put <path-archivo>\r"
expect ">"
send "bye\r"
Como el script es interactivo, si bien no hace falta que ingresemos manualmente ningún comando, éste se verá en la standar output, así que cuando llamemos al script es necesario redirigir la salida a /dev/null. Por ejemplo:
$ ./sftp-command &>/dev/null

Referencias

- How can I automate an SFTP transfer between two servers?
- sftp Linux man page
- ssh_config Linux man page

6 comentarios:

Anónimo dijo...

Está súper esta entrada... me fue muy muy muy pero muy útil, mil gracias!!!!
¿¿Ya dije que me es muy útil??? je...
gracias de veras.

d3m4s1@d0v1v0 dijo...

Me alegra que te sirviera :)

Anónimo dijo...

#!/usr/bin/expect
spawn sftp @
expect "password:"
sleep 3
send "\r";
expect ">"
send "put \r"
expect ">"
send "bye\r"

Me arroja un error de spawn not found

d3m4s1@d0v1v0 dijo...

Verificaste que tengas instalado expect? en debian no se instala por defecto, y el paquete se llama expect:
apt-get install expect
saludos!

robrey26604 dijo...

Como hacer esto mismo en Windows

Anónimo dijo...

Gracias

Publicar un comentario