Esta receta es una copia (gracias a su licencia CC) de un post del blog de Vicente Navarro (un blog con un contenido estupendo por cierto). La pongo aquí porque me parece un material muy bueno y en previsión de que (FSM no lo quiera) dicho blog desaparezca repentinamente.

La función típica del protocolo de red Secure Shell (SSH) es acceder en modo terminal a un sistema remoto y ejecutar allí comandos de forma segura gracias a que los datos van cifrados. Pero además, a través de esa conexión de datos segura, es posible crear túneles (reenviar puertos / port forwarding) entre los extremos conectados de forma que las conexiones TCP/IP se encauzan a través de la conexión SSH con lo que podemos conseguir saltarnos cualquier firewall o bloqueo de puertos siempre que tengamos la posibilidad de conectar con SSH.

Como este tema está muy tratado por toda la red:

en esta entrada no entraremos en los detalles del reenvío de puertos, sino que pretende ser una chuleta, una referencia rápida (cheat sheet) de cómo reenviar puertos TCP con OpenSSH en los 8 diferentes escenarios que se pueden dar. Otros clientes de SSH como PuTTY también permiten el reenvío de puertos, pero la configuración se hará con un interfaz gráfico. Nosotros nos centraremos en OpenSSH.

En los siguientes ejemplos y situaciones supondremos que tenemos una red externa y una red interna y entre ambas redes, la única conexión posible es una conexión SSH entre el nodo de la red external externo1 y el nodo de la red interna interno1. El nodo externo2 está en la red externa y tiene conectividad total con externo1. El nodo interno2 está en la red interna y tiene conectividad total con interno1.

Escenario 1: Usar en externo1 un servicio TCP ofrecido por interno1

(Local port forwarding / bind_address=localhost / host=localhost)

El sistema externo1 se puede conectar al sistema interno1 a través de OpenSSH y además quiere conectarse al servidor de VNC (puerto 5900) del sistema interno1:

Lo conseguiremos con este comando:

externo1 $ ssh -L 7900:localhost:5900 usuario@interno1

Ahora en el sistema externo1 podemos comprobar que el puerto 7900 está esperando conexiones:

externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
...
tcp        0      0 127.0.0.1:7900            0.0.0.0:*               LISTEN
...

Sólo necesitamos ejecutar ahora en externo1:

externo1 $ vncviewer localhost::7900

para conectarnos al servidor VNC de interno1.

Nota: Esta forma de cambiar el puerto del no está documentada en el man vncviewer. Aparece en: About VNCViewer configuration of the output TCP port. También es así como lo hace el vncviewer de TightVNC.

Escenario 2: Usar en externo2 un servicio TCP ofrecido por interno1

(Local port forwarding / bind_address=0.0.0.0 / host=localhost)

Esta vez partimos de una situación similar a la anterior pero ahora queremos que sea externo2 quien se conecte al servidor VNC de interno1:

El comando adecuado sería este:

externo1 $ ssh -L 0.0.0.0:7900:localhost:5900 usuario@interno1

Es similar al del primer escenario; pero en aquél, si nos fijamos en la salida del netstat, el puerto 7900 se había asociado a la dirección de localhost, a la 127.0.0.1, por lo que sólo procesos locales podían conectarse a él. Esta vez especificamos que el puerto se asocie a 0.0.0.0, para que el sistema acepte conexiones a cualquier IP local de la máquina:

externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
...
tcp        0      0 0.0.0.0:7900            0.0.0.0:*               LISTEN
...

De modo que ahora, desde externo2, podemos ejecutar:

externo2 $ vncviewer externo1::7900

para conectarnos al servidor VNC de interno1.

En lugar de especificar la IP 0.0.0.0, también podríamos usar la opción -g (Allows remote hosts to connect to local forwarded ports) así:

externo1 $ ssh -g -L 7900:localhost:5900 usuario@interno1

con exactamente el mismo resultado que el comando anterior:

externo1$ ssh -L 0.0.0.0:7900:localhost:5900 usuario@interno1

Por otra parte, si hubiéramos querido restringir la conexión a sólo alguna de las IPs locales del sistema, podríamos haber sido más específicos:

externo1 $ ssh -L 192.168.24.80:7900:localhost:5900 usuario@interno1
externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
...
tcp        0      0 192.168.24.80:7900            0.0.0.0:*               LISTEN
...

Escenario 3: Usar en interno1 un servicio TCP ofrecido por externo1

(Remote port forwarding / bind_address=localhost / host=localhost)

En el primer escenario, era el propio sistema con el servidor de SSH el que ofrecía otro servicio. Ahora el sistema con el cliente de SSH es el que ofrece el servicio que el sistema con el servidor de SSH quiere usar:

El comando que usaremos es el mismo que en el primer escenario cambiando el parámetro -L por -R:

externo1 $ ssh -R 7900:localhost:5900 usuario@interno1

Y ahora donde veremos que tenemos el puerto 7900 escuchando es en interno1:

interno1 $ netstat -lnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
...
tcp        0      0 127.0.0.1:7900          0.0.0.0:*               LISTEN
...

De modo que ahora desde interno1 podemos usar el servidor VNC de externo1 así:

interno1 $ vncviewer localhost::7900

Escenario 4: Usar en interno2 un servicio TCP ofrecido por externo1

(Remote port forwarding / bind_address=0.0.0.0 / host=localhost)

Similar al tercer escenario pero ahora, igual que hacíamos en el segundo escenario, asociaremos el puerto reenviado a la IP 0.0.0.0 para que otros nodos puedan usar el servicio:

El comando apropiado es:

externo1 $ ssh -R 0.0.0.0:7900:localhost:5900 usuario@interno1

Sin embargo, es importante entender que, por motivos de seguridad, esto no funcionará si en la configuración del servidor de SSH no modificamos el valor del parámetro GatewayPorts que por defecto está a no:

GatewayPorts

    Specifies whether remote hosts are allowed to connect to ports forwarded for the client. By default, sshd(8) binds remote port
forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. GatewayPorts can be
used to specify that sshd should allow remote port forwardings to bind to non-loopback addresses, thus allowing other hosts to
connect. The argument may be “no” to force remote port forwardings to be available to the local host only, “yes” to force remote
port forwardings to bind to the wildcard address, or “clientspecified” to allow the client to select the address to which the
forwarding is bound. The default is “no”.

Si no tenemos la posibilidad de modificar la configuración del servidor, no podremos usar este tipo de reenvío de puertos. Al menos no de forma sencilla, ya que, si no hay otros impedimentos, un usuario puede abrir un puerto (>1024) para escuchar conexiones externas y reenviar dicha petición a localhost:7900. Esto se podría hacer, por ejemplo, con netcat (Debian #310431: sshd_config should warn about GatewayPorts workaround.)

De modo que en el /etc/ssh/sshd_config añadiremos:

GatewayPorts clientspecified

tras lo cual tendremos que releer la configuración con “sudo /etc/init.d/ssh reload” (Debian y Ubuntu).

Comprobamos que interno1 está escuchando peticiones de todas las IPs:

interno1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
...
tcp        0      0 0.0.0.0:7900            0.0.0.0:*               LISTEN
...

Y ya podremos usar el servicio de VNC desde interno2:

interno2 $ vncviewer interno1::7900

Escenario 5: Usar en externo1 un servicio TCP ofrecido por interno2

(Local port forwarding / bind_address=localhost / host=interno2)

En este escenario usaremos el siguiente comando:

externo1 $ ssh -L 7900:interno2:5900 usuario@interno1

Y accederemos al servicio ejecutando en externo1 el comando:

externo1 $ vncviewer localhost::7900

Escenario 6: Usar en interno1 un servicio TCP ofrecido por externo2

(Remote port forwarding / bind_address=localhost / host=externo2)

En este escenario usaremos el siguiente comando:

externo1 $ ssh -R 7900:externo2:5900 usuario@interno1

Y accederemos al servicio ejecutando en interno1 el comando:

interno1 $ vncviewer localhost::7900

Escenario 7: Usar en externo2 un servicio TCP ofrecido por interno2

(Local port forwarding / bind_address=0.0.0.0 / host=interno2)

En este escenario usaremos el siguiente comando:

externo1 $ ssh -L 0.0.0.0:7900:interno2:5900 usuario@interno1

o alternativamente:

externo1 $ ssh -g -L 7900:interno2:5900 usuario@interno1

Y accederemos al servicio ejecutando en externo2 el comando:

externo2 $ vncviewer externo1::7900

Escenario 8: Usar en interno2 un servicio TCP ofrecido por externo2

(Remote port forwarding / bind_address=0.0.0.0 / host=externo2)

En este escenario usaremos el siguiente comando:

externo1 $ ssh -R 0.0.0.0:7900:externo2:5900 usuario@interno1

El servidor SSH tiene que estar configurado con “GatewayPorts clientspecified“, como hemos visto en el escenario 4.

Y accederemos al servicio ejecutando en interno2 el comando:

interno2 $ vncviewer interno1::7900

:wq

[Nota de CRySoL]

Si tienes alguna duda o aportación que hacer sobre el tema, considera hacerla en el post original para así evitar «fragmentación». Por supuesto, si el comentario es para indicar un error por mi parte al copiar la receta, ponlo aquí.



blog comments powered by Disqus