Problema
Muchos usuarios de self‑hosting tienen el router de su casa detrás de un NAT estricto, una IP pública dinámica o simplemente no quieren exponer directamente la dirección IP del ISP. En esos entornos, publicar un servicio (web, SSH, Plex, etc.) requiere una solución que atraviese el NAT sin abrir puertos innecesarios en el router y sin depender de servicios de terceros como DynDNS. La necesidad recurrente es crear un túnel fiable entre la red doméstica y un punto accesible en Internet, y luego redirigir el tráfico entrante hacia los servidores internos.
Causa
- NAT de capa 3/4: el router descarta paquetes que no pertenecen a conexiones iniciadas desde la LAN.
- IP dinámica: el cliente doméstico pierde la dirección pública cada vez que el ISP renueva el lease, lo que rompe cualquier regla estática de port‑forwarding.
- Políticas de seguridad: abrir puertos en el router puede ser riesgoso; algunos ISP bloquean ciertos puertos por completo.
- Falta de encaminamiento: incluso con un túnel activo, el tráfico que llega al VPS no se redirige automáticamente hacia la máquina de origen.
Solución
Utilizar un VPS barato como punto de entrada y establecer un túnel Wireguard entre el VPS y el servidor doméstico. En el VPS se configura nftables para DNAT (destino NAT) de los puertos que se quieren publicar y MASQUERADE de la salida hacia la interfaz Wireguard. El mismo esquema funciona para cualquier número de servicios, basta con añadir reglas adicionales.
Pasos resumidos
- Provisionar un VPS (cualquier proveedor, Debian/Ubuntu base).
- Instalar Wireguard en ambos extremos.
- Generar pares de claves y crear archivos
wg0.confcon direcciones internas (ej. 10.0.0.0/24). - Activar la interfaz con
systemctl enable --now wg-quick@wg0. - Comprobar conectividad (
ping 10.0.0.1desde el hogar y viceversa). - Instalar nftables en el VPS.
- Crear una tabla que:
- Permita el tráfico entrante desde
eth0(o la interfaz pública) hacia la IP interna del home server (10.0.0.2) en los puertos deseados. - Realice DNAT a esa IP.
- Mascarée el tráfico saliente por
wg0.
- Permita el tráfico entrante desde
- Habilitar nftables y validar que el tráfico llega al servidor doméstico.
Este flujo es independiente del número de servicios; cada puerto adicional solo necesita una línea de regla en la sección prerouting y, opcionalmente, en forward si se desea filtrar.
Cuándo aplicar esta solución
- NAT restrictivo: el router no permite port‑forwarding o bloquea puertos críticos.
- IP dinámica: el ISP cambia la IP con frecuencia y no se dispone de un DNS dinámico confiable.
- Separación de superficies de ataque: se prefiere exponer solo el VPS y mantener el router sin puertos abiertos.
- Múltiples servicios internos: se necesita publicar varios puertos sin crear reglas complejas en el router.
No es la mejor opción cuando se dispone de una IP estática y el router permite configuraciones de port‑forwarding sin restricciones, ya que el overhead del túnel no aporta valor añadido.
Código
# 1. Instalar Wireguard y nftables
apt update
apt install -y wireguard nftables
# 2. Generar claves (ejecutar en cada host)
wg genkey | tee wg.key | wg pubkey > wg.pub
# Copiar wg.pub al otro host y wg.key al propio host
# 3. Configuración del VPS ( /etc/wireguard/wg0.conf )
cat > /etc/wireguard/wg0.conf <<'EOF'
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <VPS_PRIVATE_KEY>
MTU = 1300
[Peer]
PublicKey = <HOME_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 15
EOF
# 4. Configuración del Home Server ( /etc/wireguard/wg0.conf )
cat > /etc/wireguard/wg0.conf <<'EOF'
[Interface]
Address = 10.0.0.2/24
ListenPort = 51820
PrivateKey = <HOME_PRIVATE_KEY>
MTU = 1300
[Peer]
PublicKey = <VPS_PUBLIC_KEY>
Endpoint = <VPS_PUBLIC_IP>:51820
AllowedIPs = 10.0.0.1/24
PersistentKeepalive = 25
EOF
# 5. Activar Wireguard en ambos extremos
systemctl enable --now wg-quick@wg0
# 6. nftables en el VPS ( /etc/nftables.conf )
cat > /etc/nftables.conf <<'EOF'
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
}
chain forward {
type filter hook forward priority 0;
policy drop;
# tráfico que llega por eth0 y va a wg0
iifname "eth0" oifname "wg0" ip daddr 10.0.0.2 accept
iifname "wg0" oifname "eth0" accept
}
chain output {
type filter hook output priority 0;
}
}
table ip nat {
chain prerouting {
type nat hook prerouting priority -100;
iifname "eth0" tcp dport {80, 443} dnat to 10.0.0.2
iifname "eth0" udp dport {80, 443} dnat to 10.0.0.2
}
chain postrouting {
type nat hook postrouting priority 100;
oifname "wg0" masquerade
}
}
EOF
# 7. Habilitar nftables
systemctl enable --now nftables
Verificación
- Desde el home server:
ping -c 3 10.0.0.1. - Desde el VPS:
ping -c 3 10.0.0.2. - En una máquina externa,
curl http://<VPS_IP>o abrir el dominio apuntado al VPS; la respuesta debe provenir del servicio que corre en el home server (por ejemplo, Nginx en puerto 80). - Revisar contadores de nftables:
nft list counterspara confirmar que los paquetes atraviesan la cadenaprerouting.
Si el tráfico no llega, revisar:
- Que
eth0sea realmente la interfaz pública (ip a). - Que los puertos estén abiertos en el firewall del VPS (no solo nftables).
- Que
PersistentKeepalivesea suficiente para mantener el túnel activo detrás de NAT.
Notas adicionales
- MTU: los enlaces VPN suelen requerir una MTU menor (1300 funciona en la mayoría de los casos). Ajusta si observas fragmentación.
- Persistencia: nftables guarda la configuración en
/etc/nftables.conf; cualquier cambio debe recargarse consystemctl reload nftables. - Escalabilidad: para añadir más servicios, basta con ampliar las listas
tcp dport { … }yudp dport { … }en la tablanat. - Seguridad: mantén la política
dropen la cadenaforwardy permite solo los puertos que realmente necesitas. - Monitorización:
wg showmuestra estadísticas de transferencia; combina conjournalctl -u wg-quick@wg0para depurar desconexiones.
Con este patrón, cualquier servicio interno puede ser publicado sin tocar la configuración del router doméstico, y el VPS actúa como único punto expuesto a Internet.