Voy a contar un método muy útil para poder acceder a un servidor remoto al que por diversos motivos en principio no tenemos acceso directo por SSH, creando un túnel reverso tenebroso por SSH.
Una aplicación podría ser para poder acceder a una Raspberry Pi que tiene conexión a internet por medio de un modem 3G de la que desconocemos la IP (y que probablemente aún conociéndola, el ISP no nos dejaría acceder a la misma por SSH). Es el caso que tengo entre manos para una Raspberry Pi que quiero dejar monitorizando unas temperaturas de fraguado del hormigón en una obra, que cuento en esta entrada.
Necesitamos en primer lugar un Servidor A al que tenemos acceso por SSH. Podría ser una Raspberry Pi ubicada dentro de una red, y a la que podemos acceder desde fuera de la red. Para ello es muy posible que hayamos tenido que redirigir puertos del Router de la red a la misma, como es mi caso. Tengo el router configurado para que cualquier petición de conexión a un puerto concreto, digamos el 22122, se redirija al puerto 22 de la Raspberry Pi, que está escuchando y esperando conexiones SSH por ese puerto.
Tengo además instalado un servicio externo de DNS (en mi caso el gratuito No-IP) en ese Servidor A de forma que se le asigne un determinado nombre al host a la IP dinámica que mi ISP me ofrece. Digamos que ese nombre es miServidor.com
Desde el Servidor B (la Raspberry Pi con conexión 3G a internet, a la que voy a querer poder acceder en el futuro) pruebo a conectarme al Servidor A:
ssh usuarioMiServidorA@miServidor.com
Introduzco la contraseña del usuario usuarioMiServidor, y listo, ya esto conectado desde el Servidor B al Servidor A por SSH.
Habiendo comprobado que tengo acceso, salgo:
exit
Y ahora vuelvo a entrar, pero con el siguiente comando:
ssh -N -R 2222:localhost:22 usuarioMiServidorA@miServidor.com -p 22122
Y vuelvo a introducir la contraseña para conectarme (más abajo veremos cómo evitar tener que introducir la contraseña). Con esto lo que le estoy diciendo al Servidor A es que que toda conexión que le llegue al puerto 2222 (por ejemplo) lo redirija por el túnel al puerto 22. La opción -N es específica para redireccionamiento de puertos, e indica que no viene un comando después.
Y ahora, desde el Servidor A, abro el túnel para conectarme con el Servidor B, que básicamente consiste en redirigir todo el tráfico SSH a un determinado puerto:
ssh -l usuarioServidorB -p 2222 localhost
Introduzco la contraseña del usuarioServidor B y ¡ya estoy en el Servidor B!
Hasta aquí todo bien, pero nos quedan cosas por hacer. Por un lado, como en teoría no voy a tener acceso al Servidor B al iniciar la conexión, es necesario que se conecte por SSH sin necesitar introducir la contraseña. Y por otro, tendremos que tener algún tipo de servicio que se encargue de vigilar que el túnel está funcionando, y volver a abrirlo en el caso de que se haya caído.
Acceso SSH por medio de certificado
En el Servidor B (desde el que quiero acceder al Servidor B sin que me pida la contraseña), tengo que generar el par de claves público-privada. :
cd ~/.ssh
ssh-keygen -t rsa
Doy al enter varias veces aceptando las opciones por defecto, sin passphrase. Con esto se generan dos ficheros: id_rsa y id_rsa.pub.
Ahora copiamos la clave pública al Servidor A:
scp -P 22122 id_rsa.pub usuarioServidorB@servidorB:.ssh/authorized_keys
(O lo copiamos con un pincho USB o como se quiera.)
Ahora pruebo a conectarme desde el Servidor B al Servidor A, y a ver si no me pide la clave:
ssh usuarioMiServidorA@miServidor.com -p 22122
Si a la hora de hacer la conexión tenemos algún problema, puede interesar empelar el comando con la opción -v, y ver lo que pasa:
ssh -v usuarioMiServidorA@miServidor.com -p 22122
¡Funciona!
Control de la conexión
Ahora tenemos que comprobar que el túnel permanece abierto a lo lagro del tiempo, y garantizar que se reconecta en caso de pérdida de que lo haya perdido.
Creo un fichero ~/create_ssh_tunnel.sh, con el siguiente contenido (sacado y adaptado de aquí):
#!/bin/bash
createTunnel() {
/usr/bin/ssh -N -R 2222:localhost:22 pi@miServidor.com -p 22122
if [[ $? -eq 0 ]]; then
echo Tunnel to jumpbox created successfully
else
echo An error occurred creating a tunnel to jumpbox. RC was $?
fi
}
/bin/pidof ssh
if [[ $? -ne 0 ]]; then
echo Creating new tunnel connection
createTunnel
fi
Este script comprueba que haya un proceso ssh ejecutándose, y si no, abre el túnel.
Hacemos ejecutable el script:
chmod 700 ~/create_ssh_tunnel.sh
Modifico crontab para que se ejecute cada minuto:
crontab -e
Y añado al final del fichero:
*/1 * * * * ~/create_ssh_tunnel.sh > tunnel.log 2>&1
Con esto decimos que se ejecute el script cada minuto.
Y listo, ya está funcionando.
Referencias
https://blog.devolutions.net/2017/3/what-is-reverse-ssh-port-forwarding
https://www.tunnelsup.com/raspberry-pi-phoning-home-using-a-reverse-remote-ssh-tunnel/