Llegar a un servicio de cliente VPN: túnel inverso o DNAT
Llegar a un servicio en un cliente OpenVPN sin reenvío de puertos: compare un túnel SSH inverso con DNAT en el servidor y las tres condiciones que eligen uno.
Incluso un portátil detrás del NAT del operador puede atender a un colega, siempre que el servidor VPN haga de punto de encuentro. Dos mecanismos llegan hasta ahí: el cliente marca hacia fuera y pide al servidor que mantenga un puerto de escucha, o el servidor publica un puerto y lo traduce dentro del túnel. Ambos resuelven el mismo problema de accesibilidad y se comportan de forma distinta ante fallos, supervisión y registro. Tres condiciones deciden cuál encaja en su instalación.
Dos formas, un punto de encuentro
En la primera forma el cliente abre una sesión SSH hacia fuera y pide al servidor que escuche en un puerto elegido, reenviando cada conexión por esa sesión. En el servidor no se reenvía nada y no ocurre ninguna traducción de paquetes, así que los únicos requisitos son una cuenta SSH, un reenvío permitido y una regla de cortafuegos para el puerto. El servicio queda ligado a loopback en el cliente, que suele ser lo que se desea.
ssh -N -R 8081:127.0.0.1:8080 -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 [email protected]
En la segunda forma el servidor es dueño del puerto público y reescribe cada conexión hacia la dirección del túnel del cliente. Eso exige el reenvío IP activado, una traducción de destino a la entrada y una traducción de origen a la salida, y sigue funcionando cuando no existe ninguna sesión SSH. El puerto pertenece al servidor y no a una sesión, y esa es la propiedad que decide la mayoría de los casos reales.
Ninguna de las dos formas es más segura por construcción. Ambas acaban exponiendo un puerto de servicio a cualquiera que pueda alcanzar la dirección pública del servidor, y ambas siguen dependiendo de la autenticación propia del servicio.
Condición uno: debe el puerto sobrevivir a la sesión
Un reenvío inverso vive exactamente lo que vive la sesión SSH que lo creó. Cuando cae el enlace, el oyente desaparece y el colega ve una conexión rechazada en lugar de una ruta lenta. Supervisar el comando con una unidad de systemd y una política de reinicio lo arregla, y ExitOnForwardFailure=yes hace visible de inmediato una vinculación fallida en vez de dejar una sesión que funciona sin reenvío.
Un puerto traducido sobrevive a los reinicios si las reglas se conservan, pero depende de que el cliente mantenga la misma dirección de túnel. Un grupo dinámico que da al portátil otra dirección tras reconectar apunta la regla en silencio a la nada. Reserve primero la dirección del túnel, luego escriba la regla y trate ambas cosas como un solo cambio.
Si el servicio tiene que responder a las tres de la madrugada sin nadie mirando, la forma con el puerto publicado por el servidor tiene menos piezas móviles, porque el único proceso que debe seguir en pie es el túnel mismo.
Condición dos: quién es dueño del cortafuegos y de los registros
La forma inversa mantiene toda la política en dos lugares. sshd decide si los reenvíos remotos están permitidos en absoluto mediante AllowTcpForwarding, y GatewayPorts decide si el oyente se liga a loopback o a todas las direcciones. Con el valor predeterminado no un colega fuera del servidor no puede alcanzar el puerto aunque el reenvío haya funcionado, y eso parece un túnel roto cuando en realidad es una dirección de vinculación.
sudo ss -ltn '( sport = :8081 )'
sudo grep -E '^(GatewayPorts|AllowTcpForwarding)' /etc/ssh/sshd_config
La forma traducida necesita el reenvío activado y dos reglas, y la segunda regla es donde fallan la mayoría de las recetas. Limitar la traducción de origen al estado de conntrack DNAT significa que solo se reescriben las conexiones que creó la regla de destino, en lugar de cada paquete que el servidor envía al túnel.
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8081 -j DNAT --to-destination 10.88.0.6:8080
sudo iptables -t nat -A POSTROUTING -o tun0 -m conntrack --ctstate DNAT -j SNAT --to-source 10.88.0.1
Esa reescritura también destruye la atribución. Después, el cliente ve a cada llamante como la dirección del túnel del servidor, lo que colapsa cualquier límite de tasa por dirección en un solo cubo y oculta quién envió qué. Si la atribución importa, termine la conexión en el servidor o mantenga allí la decisión de permiso y pase una identidad en una cabecera en la que el servicio confíe. Elegir entre esas dos correcciones depende de si el servicio lee alguna cabecera de proxy.
Condición tres: un puerto o muchos, y quién es dueño del nombre
Un solo servicio en un solo puerto es el caso en el que el reenvío inverso gana en esfuerzo. Necesita un comando, una cuenta y una regla de cortafuegos, y la limpieza es matar la sesión. Varios servicios en la misma dirección pública le empujan hacia la forma publicada por el servidor, porque cada reenvío inverso necesita su propio puerto de servidor y el conjunto crece con cada servicio.
Los nombres lo complican más. Un reenvío inverso puede estar detrás de un proxy TCP que enruta por SNI, mientras que un puerto traducido suele ser un puerto por servicio. Si el servicio termina TLS por sí mismo y necesita el nombre de host original, el enfoque de proxy lo conserva y el enfoque DNAT solo lo conserva si el servicio acepta ese nombre en ese puerto.
Anote cuál de las tres condiciones decidió la forma. De lo contrario, la siguiente persona que añada un segundo servicio reabrirá la misma comparación con menos contexto.
Decida y luego pruebe desde fuera
Pruebe la ruta desde una red que no tenga ninguna ruta hacia el portátil y observe los contadores del servidor mientras se ejecuta la petición. Probar desde el servidor hacia su propia dirección pública no demuestra nada aquí, porque el paquete se crea localmente y nunca llega al gancho de traducción de entrada, así que la prueba puede pasar mientras la ruta real está rota.
sudo iptables -t nat -L PREROUTING -n -v
sudo tcpdump -ni any 'tcp port 8081' -c 20
Los contadores que se quedan planos significan que el paquete nunca llegó a la regla, lo que apunta al proveedor o al cortafuegos del host. Los contadores que se mueven mientras el servicio rechaza apuntan al cortafuegos propio del cliente o a la dirección en la que escucha el servicio. Un túnel que transporta el paquete pero no devuelve nada es un problema de ruta o de camino inverso, y ninguno de estos tres resultados debería depurarse editando el adaptador del cliente. Anote qué contador se movió antes de cambiar nada, porque la siguiente prueba necesita esa línea base.
Cuando la forma funcione, anote el puerto, el propietario y el método de supervisión junto a la configuración del túnel. Una corrección de accesibilidad que solo existe en el historial de shell de alguien está a un reinicio de un incidente repetido.