Hace un tiempo expliqué cómo sacar provecho de ssh para
navegar usando un proxy SOCKS remoto.
Así, estando en la ubicación geográfica A, se puede navegar
cómo si se estuviera en la ubicación geográfica B, siempre y
cuando se tenga acceso directo ssh a una máquina en la
ubicación B.
Pero, ¿qué se puede hacer si no se cuenta (o se perdió)
acceso ssh directo a esa ubicación B? Eso es precisamente
lo que ocurrió hace unos meses con uno de los servidores a los
cuales tengo acceso: hubo un cambio de proveedor de servicio,
y ahora es prácticamente imposible tener acceso ssh directo
entrando a menos que uno dé más explicaciones de las que estoy
dispuesto a dar.
De nuevo, sólo necesitas SSH…
…y un tercer servidor C al cual se pueda llegar usando
ssh tanto desde A como desde B. Es decir, puedo conectarme
desde mi ubicación A hacia el servidor C usando ssh, y también
es posible conectarse desde la ubicación B hacia el
servidor C usando ssh. Y lo mejor es que no es necesario tener
privilegios de superusuario en ninguna de las tres máquinas.
La estrategia se basa en usar la conexión entre B y C para
establecer un túnel reverso: en C se activará un puerto TCP
particular sobre la interfaz localhost, que llevaŕa el tráfico
en la dirección contraria hasta B.
Hecho esto, es posible iniciar una conexión directa entre A y
B, usando a C como «puente». Esa conexión, a su vez, establece
un túnel cifrado para tráfico HTTP y HTTPS, e incluso DNS.
El tráfico termina fluyendo entre A y B, pasando por C
de manera transparente.
Si estás llevando la cuenta: un túnel SOCKS, dentro de un túnel
ssh de dos tramos, cuyo segundo tramo es un túnel reverso
dentro de un túnel ssh. Cuando sea grande quiero ser como yo.
Desde el punto de vista de los sitios accedidos, la conexión
parecerá venir desde el servidor B, y no desde la ubicación
actual en A. Desde el punto de vista de un curioso, hay una
conexiones SSH autorizadas entre A y C, y otra entre B
y C, pero sus propósitos no pueden relacionarse desde afuera.
Además del usuario regular que quiere navegar en A, que
para este ejemplo es mi usuario (emhn), serán necesarios dos
usuarios regulares: pana es un usuario regular en B, y
puente es un usuario regular en C.
Túnel SSH reverso entre B y C
Se comienza por establecer una conexión SSH entre pana@B y
puente@C, usando llaves SSH:
El usuario
panaen la máquinaB, crea una pareja de llaves dedicada para este propósito. En general, es preferible que la llave no tenga passphrase y eso es suficiente si el usuariopanatiene protegido su usuario —man ssh-keygensi quiere explorar alternativas más sofisticadas.pana@B:~$ ssh-keygen -t ed25519 -f ~/.ssh/b-to-cEl usuario
panaen la máquinaB, prepara un archivo de configuraciónsshdedicado para el túnel. Como mínimo se sugieren las siguientes directivaspana@B:~$ cat .ssh/tunel-b-to-c.conf Host b-to-c HostName C User puente PasswordAuthentication no KeepAlive yes IdentityFile /home/pana/.ssh/b-to-c RemoteForward 127.0.0.1:23432 127.0.0.1:22de manera que se use la pareja de llaves creada a propósito (
IdentityFile), y una vez establecida la conexión desdeBhaciaC, se active un túnel desde ellocalhost:23432de C, hacia el serviciosshde B (RemoteForward). Efectivamente, es un túnel que «regresa» desdeChacia elsshenB, y como usalocalhosten ambos extremos, es invisible al exterior.El usuario
puenteen la máquinaC, agrega la parte pública (b-to-c.pub) de la pareja de llaves del usuariopana, al archivo de llaves autorizadas para conectarse como usuariopuente, i.e.puente@C:~$ cat b-to-c.pub >> ~/.ssh/authorized_keysDe ese modo, se permite que el usuario
panaen el servidorBpueda conectarse como el usuariopuenteen el servidorC, siempre que use la llave dedicada.Si todos los pasos anteriores se completaron correctamente, el usuario
panaen el servidorBserá capaz de hacerpana@B:~$ ssh -N -F /home/pana/.ssh/tunel-b-to-c.conf b-to-cestableciendo conexión desde
BhaciaC, y la conexión se va a quedar allí esperando tráfico. Se puede verificar que está funcionando porque enCaparece un procesosshdescuchando en la entrada del túnel reversopuente@C:~$ netstat -anp | grep 23432 tcp 0 0 127.0.0.1:23432 0.0.0.0:* LISTEN 2012187/sshd: b-to-cy enseguida, el usuario
puentepuede hacerpuente@C:~$ ssh -p 23432 pana@127.0.0.1para intentar conectarse a
Bpor el túnel reverso.
Estableciendo el túnel SSH con proxy SOCKS
Pero no es el usuario puente el que tiene que entrar como pana,
sino mi usuario emhn desde A hacia B, pasando por C, y encima
active un proxy SOCKS.
Con la cooperación del usuario
puenteen el servidorC, agrego la parte pública (emhn.pub) de mi pareja de llaves al archivo de llaves autorizadas para conectarse como usuariopuente@C, i.e.puente@C:~$ cat emhn.pub >> ~/.ssh/authorized_keysCon la cooperación del usuario
panaen el servidorB, agrego la parte pública (emhn.pub) de mi pareja de llaves al archivo de llaves autorizadas para conectarse como el usuariopana@B, i.e.pana@B:~$ cat emhn.pub >> ~/.ssh/authorized_keysVerifico que puedo conectarme desde mi máquina
Ahasta el servidorCcomo el usuariopuenteemhn@A:~$ ssh -A puente@CNótese que la conexión reenvía mi identidad SSH (
-AoForwardAgent) para que pueda ser reutilizada en la máquinaC. Entonces, aunque esté conectado con el usuariopuenteen el servidorC, se cuenta con mi pareja de llaves, de manera quepuente@C:~$ ssh -p 23432 pana@Bse conectará directamente a
B. Esto comprueba que con la pareja de llaves deemhnes posible conectarse directamente desdeAhaciaB, pasando porC.Para simplificar la conexión de mi lado agrego un bloque de configuración
sshen mi~/.ssh/configsimilar aHost C User puente ForwardAgent yes Host B ProxyJump C Hostname 127.0.0.1 Port 23432 User panaque describle la configuración necesaria para establecer las conexiones:
Para conectarme a
C, hacerlo con el usuario remotopuentey propagar mis credenciales SSH.Para conectarme a
B, primero conectarme aC, y luego usarlocalhosten el puerto23432con el usuariopana.
Ahora, trabajando con mi usuario en la máquina
A, puedo verificar que todo funciona con un simpleemhn@A:~$ ssh BSi la conexión funciona correctamente, y me encuentro conectado a
B, me desconecto, y vuelvo a conectarme, pero agregando las opciones necesarias para aprovechar la conexión como proxy SOCKS, tal como se describe al final del artículo previo.Por supuesto que puedo tener una definición
Hosten mi~/.ssh/configque active el proxy SOCKS de usa sola vez, de manera que hacerssh B-proxydeje todo listo. Eso queda de ejercicio para el lector.
La cooperación del pana
Como la mayoría de los proveedores de servicio del mundo no tienen
problemas en permitir conexiones ssh saliendo, el tráfico entre
B y C nunca es un inconveniente. Lo más rebuscado que ocurre
es un ISP que bloquea ssh saliendo (TCP 22); en ese caso, se
corre ssh en C de manera que escuche en un puerto no
privilegiado, y se ajusta la configuración en b-to-c.conf.
No debería sorprender a nadie que el usuario puente en realidad
es mi usuario, pues tengo varios servidores dedicados que puedo
usar para cubrir el rol de C.
Pero la permanencia de esa conexión ssh entre B y C es el
elemento clave en esta operación. Por eso está en manos de un «pana»
de confianza que pueda revisarla y restablecerla en caso de
interrupciones de servicio u otras anomalías en la operación del
servidor B.
Ciertamente, el pana puede establecer esa conexión manualmente
cuando hace falta, previo acuerdo por otros canales confidenciales
de comunicación. Sin embargo, es mucho más práctico mantener el
túnel entre B y C permanentemente gracias a un SystemD Unit.
Si el «pana» es el administrador de la máquina B, puede ser
un SystemD Unit Global que incluya restablecer la conexión al
reiniciar la máquina, o en caso de timeouts. Si el «pana» no
es el administrador, puede ser un SystemD Unit User que se
activa cuando el usuario entra a su sesión.
SSH es mucho más que «conectarse para correr comandos»…