Este es el tercer artículo de la serie que empezó con “Cambios en mi red de la mano de XMPP.”

El problema

En aquel artículo, casi al final de todo, comentaba que hacía falta aplicar una solución para que el tráfico XMPP del resto de puertos (5000, 5222,… todos los que no son el 80 ó el 443) fuese reenviado desde el VPS a mi servidor en casa.

Probé algunas cosas con la configuración de NGINX y su módulo streams, pero no funcionó, así que voy a describir aquí cómo acabé usando las capacidades de filtrado de paquetes de Linux (el núcleo mismo) a través de iptables (en inglés).

Mi solución está basada y adaptada a partir de lo explicado en este artículo (en inglés) de Ryan Welch.

El diagrama que representa lo que queremos conseguir y acabamos de detallar.

El objetivo

Al final del artículo, en cualquier caso, dejaré las configuraciones que intenté aplicar en NGINX para procesar tráfico TCP y UDP en general. Quizá lo necesitemos más adelante, o a lo mejor decides probarlo y comentarme qué es lo que hice mal.

Solución

Como comentaba antes, Linux (NO GNU/Linux, sino “Linux” sin más, el núcleo) tiene funcionalidades de filtrado de paquetes de red incluidas, que podemos configurar para nuestros propios fines.

Para configurar la redirección de tráfico en el VPS, entramos por SSH y habilitaremos IP forwarding en el núcleo:

sudo sysctl -w net.ipv4.ip_forward=1

Esto puede que se aplique entre arranques, o no, dependiendo de tu distribución. Para asegurarte, edita el fichero /etc/sysctl.conf y descomenta la línea net.ipv4.ip_forward=1.

Tras ello, ya podrás redirigir puertos y rangos de puertos con instrucciones como la siguiente:

iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 5222 -j DNAT --to-destination <IP Destino>:5222
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 5222 -j MASQUERADE

Y, para rangos de puertos:

iptables -A PREROUTING -t nat -p udp -i eth0 -m multiport --dports 49152:65535 -j DNAT --to-destination <IP Destino>:49152-65535
iptables -A POSTROUTING -t nat -p udp -d <IP Destini> -m multiport --dports 49152:65535 -j MASQUERADE

Vas a tener que cambiar los puertos del ejemplo por los tuyos propios, así como poner tu propia dirección IP de destino en lugar del texto <IP Destino>.

Para el caso de XMPP hay que añadir todas las reglas de renvío correspondientes a sus puertos, cambiando el texto <IP Destino> por la dirección IP del servidor de casa, patata en el artículo previo de la serie, y teniendo en cuenta que la dirección que tenemos que configurar es la de patata en Tailscale.

iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 5222 -j DNAT --to-destination <IP Destino>:5222
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 5222 -j MASQUERADE
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 5269 -j DNAT --to-destination <IP Destino>:5269
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 5269 -j MASQUERADE
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 5000 -j DNAT --to-destination <IP Destino>:5000
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 5000 -j MASQUERADE
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 3478 -j DNAT --to-destination <IP Destino>:3478
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 3478 -j MASQUERADE
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 3479 -j DNAT --to-destination <IP Destino>:3479
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 3479 -j MASQUERADE
iptables -A PREROUTING -t nat -p udp -i eth0 --dport 3478 -j DNAT --to-destination <IP Destino>:3478
iptables -A POSTROUTING -t nat -p udp -d <IP Destino> --dport 3478 -j MASQUERADE
iptables -A PREROUTING -t nat -p udp -i eth0 --dport 3479 -j DNAT --to-destination <IP Destino>:3479
iptables -A POSTROUTING -t nat -p udp -d <IP Destino> --dport 3479 -j MASQUERADE
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 5349 -j DNAT --to-destination <IP Destino>:5349
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 5349 -j MASQUERADE
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 5350 -j DNAT --to-destination <IP Destino>:5350
iptables -A POSTROUTING -t nat -p tcp -d <IP Destino> --dport 5350 -j MASQUERADE
iptables -A PREROUTING -t nat -p udp -i eth0 --dport 5349 -j DNAT --to-destination <IP Destino>:5349
iptables -A POSTROUTING -t nat -p udp -d <IP Destino> --dport 5349 -j MASQUERADE
iptables -A PREROUTING -t nat -p udp -i eth0 --dport 5350 -j DNAT --to-destination <IP Destino>:5350
iptables -A POSTROUTING -t nat -p udp -d <IP Destino> --dport 5350 -j MASQUERADE
iptables -A PREROUTING -t nat -p udp -i eth0 -m multiport --dports 49152:65535 -j DNAT --to-destination <IP Destino>:49152-65535
iptables -A POSTROUTING -t nat -p udp -d <IP Destino> -m multiport --dports 49152:65535 -j MASQUERADE

Prueba que todo funciona y graba las reglas de iptables con sudo iptables-save > /etc/iptables/rules.v4, de tal forma que se apliquen cada vez que reinicies el VPS.

En su artículo, Ryan comentaba otras configuraciones adicionales para temas tales como control de congestión. Consulta ese artículo para saber más, y aplica todo aquello que te sea de utilidad.

NGINX con tráfico TCP y UDP genérico

Esta parte del artículo no está del todo probada, porque no me acabó de funcionar para la instalación del servidor XMPP Snikket. Sin embargo, de acuerdo a su documentación, ésta es la forma en la que se configura NGINX para tráfico que no es Web.

Instala libnginx-mod-stream para habilitar el soporte a proxies inversos TCP y UDP.

Modifica /etc/nginx/nginx.conf para añadir un bloque stream:

stream {
        include /etc/nginx/streams-enabled/*;
}

Tras ello, crea los directorios correspondientes siguiendo el mismo esquema existente para las webs: sudo mkdir -p /etc/nginx/streams-available /etc/nginx/streams-enabled

Crea un fichero /etc/nginx/streams-available/tcpudpservice.example.com.conf con reglas como las siguientes:

# Puerto TCP (tráfico no HTTP):
server {
        listen 5222;
        proxy_pass <IP Destino>:5222;
}

# ...

# Puerto UDP:
server {
        listen 3478 udp;
        proxy_pass <IP Destino>:3478;
}

# ...

# Se aceptan rangos, también. En este caso, fíjate
# en el uso de $server_port en la sentencia proxy_pass.

server {
        listen 49152-65535 udp;
        proxy_pass <IP Destino>:$server_port;
}

En caso de definir rangos de puertos muy grande, como los que propone Snikket para conversaciones de audio y vídeo por XMPP en instalaciones con muchos usuarios, NGINX devolverá un error al tener demasiados ficheros abiertos. La configuración de Snikket espera poder tener hasta 16384 conexiones UDP abiertas al mismo tiempo.

Si queremos resolver esto en lugar de reducir el rango de puertos UDP, hay que aumentar el parámetro LimitNOFILE de NGINX. Para eso, ejecuta sudo systemctl edit nginx.service; esto abrirá un fichero de extensión de la definición del servicio

Escribe lo siguiente y graba los cambios:

[Service]
LimitNOFILE=65535

Tras ello, en el preámbulo de /etc/nginx/nginx.conf (fuera de cualquier bloque http, events o stream), añade la línea worker_rlimit_nofile con un valor menor a 65535 (30000 funciona para Snikket), e incrementa el valor de worker_connections dentro de events:

...
include /etc/nginx/modules-enabled/*.conf;

worker_rlimit_nofile 30000;

events {
        # worker_connections 768;
        # 3 veces 8192 será suficiente
        worker_connections 24576;
        # multi_accept on;
}

...

Como dije antes, esta configuración no me sirvió para XMPP, pero puede ser útil para otros casos de uso.