Navegar a través de SSH a través de SSH

Posted on 2024-12-30 by Ernesto Hernández-Novich
Tags: , ,

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:

  1. El usuario pana en la máquina B, 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 usuario pana 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
  2. El usuario pana en la máquina B, prepara un archivo de configuración ssh dedicado para el túnel. Como mínimo se sugieren las siguientes directivas

    pana@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 desde B hacia C, se active un túnel desde el localhost:23432 de C, hacia el servicio ssh de B (RemoteForward). Efectivamente, es un túnel que «regresa» desde C hacia el ssh en B, y como usa localhost en ambos extremos, es invisible al exterior.

  3. El usuario puente en la máquina C, agrega la parte pública (b-to-c.pub) de la pareja de llaves del usuario pana, al archivo de llaves autorizadas para conectarse como usuario puente, i.e.

    puente@C:~$ cat b-to-c.pub >> ~/.ssh/authorized_keys

    De ese modo, se permite que el usuario pana en el servidor B pueda conectarse como el usuario puente en el servidor C, siempre que use la llave dedicada.

  4. Si todos los pasos anteriores se completaron correctamente, el usuario pana en el servidor B será capaz de hacer

    pana@B:~$ ssh -N -F /home/pana/.ssh/tunel-b-to-c.conf b-to-c

    estableciendo conexión desde B hacia C, y la conexión se va a quedar allí esperando tráfico. Se puede verificar que está funcionando porque en C aparece un proceso sshd escuchando en la entrada del túnel reverso

    puente@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 hacer

    puente@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.

  1. Con la cooperación del usuario puente en el servidor C, agrego la parte pública (emhn.pub) de mi pareja de llaves al archivo de llaves autorizadas para conectarse como usuario puente@C, i.e.

    puente@C:~$ cat emhn.pub >> ~/.ssh/authorized_keys
  2. Con la cooperación del usuario pana en el servidor B, agrego la parte pública (emhn.pub) de mi pareja de llaves al archivo de llaves autorizadas para conectarse como el usuario pana@B, i.e.

    pana@B:~$ cat emhn.pub >> ~/.ssh/authorized_keys
  3. Verifico que puedo conectarme desde mi máquina A hasta el servidor C como el usuario puente

    emhn@A:~$ ssh -A puente@C

    Nótese que la conexión reenvía mi identidad SSH (-A o ForwardAgent) para que pueda ser reutilizada en la máquina C. Entonces, aunque esté conectado con el usuario puente en el servidor C, se cuenta con mi pareja de llaves, de manera que

    puente@C:~$ ssh -p 23432 pana@B

    se conectará directamente a B. Esto comprueba que con la pareja de llaves de emhn es posible conectarse directamente desde A hacia B, pasando por C.

  4. Para simplificar la conexión de mi lado agrego un bloque de configuración ssh en mi ~/.ssh/config similar a

    Host 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 remoto puente y propagar mis credenciales SSH.

    • Para conectarme a B, primero conectarme a C, y luego usar localhost en el puerto 23432 con el usuario pana.

    Ahora, trabajando con mi usuario en la máquina A, puedo verificar que todo funciona con un simple

    emhn@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 hacer ssh 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»…