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
pana
en 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 usuariopana
tiene protegido su usuario —man ssh-keygen
si quiere explorar alternativas más sofisticadas.pana@B:~$ ssh-keygen -t ed25519 -f ~/.ssh/b-to-c
El usuario
pana
en la máquinaB
, prepara un archivo de configuraciónssh
dedicado 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:22
de manera que se use la pareja de llaves creada a propósito (
IdentityFile
), y una vez establecida la conexión desdeB
haciaC
, se active un túnel desde ellocalhost:23432
de C, hacia el serviciossh
de B (RemoteForward
). Efectivamente, es un túnel que «regresa» desdeC
hacia elssh
enB
, y como usalocalhost
en ambos extremos, es invisible al exterior.El usuario
puente
en 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_keys
De ese modo, se permite que el usuario
pana
en el servidorB
pueda conectarse como el usuariopuente
en el servidorC
, siempre que use la llave dedicada.Si todos los pasos anteriores se completaron correctamente, el usuario
pana
en el servidorB
será capaz de hacerpana@B:~$ ssh -N -F /home/pana/.ssh/tunel-b-to-c.conf b-to-c
estableciendo conexión desde
B
haciaC
, y la conexión se va a quedar allí esperando tráfico. Se puede verificar que está funcionando porque enC
aparece un procesosshd
escuchando 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-c
y enseguida, el usuario
puente
puede hacerpuente@C:~$ ssh -p 23432 pana@127.0.0.1
para intentar conectarse a
B
por 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
puente
en 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_keys
Con la cooperación del usuario
pana
en 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_keys
Verifico que puedo conectarme desde mi máquina
A
hasta el servidorC
como el usuariopuente
emhn@A:~$ ssh -A puente@C
Nótese que la conexión reenvía mi identidad SSH (
-A
oForwardAgent
) para que pueda ser reutilizada en la máquinaC
. Entonces, aunque esté conectado con el usuariopuente
en el servidorC
, se cuenta con mi pareja de llaves, de manera quepuente@C:~$ ssh -p 23432 pana@B
se conectará directamente a
B
. Esto comprueba que con la pareja de llaves deemhn
es posible conectarse directamente desdeA
haciaB
, pasando porC
.Para simplificar la conexión de mi lado agrego un bloque de configuración
ssh
en mi~/.ssh/config
similar aHost C User puente ForwardAgent yes Host B ProxyJump C Hostname 127.0.0.1 Port 23432 User pana
que describle la configuración necesaria para establecer las conexiones:
Para conectarme a
C
, hacerlo con el usuario remotopuente
y propagar mis credenciales SSH.Para conectarme a
B
, primero conectarme aC
, y luego usarlocalhost
en el puerto23432
con el usuariopana
.
Ahora, trabajando con mi usuario en la máquina
A
, puedo verificar que todo funciona con un simpleemhn@A:~$ ssh B
Si 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
Host
en mi~/.ssh/config
que active el proxy SOCKS de usa sola vez, de manera que hacerssh B-proxy
deje 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»…