Problema
En entornos de Proxmox que utilizan SDN para conectar contenedores LXC a una red externa, es frecuente encontrarse con que los contenedores pueden resolver nombres DNS y responder a pings, pero fallan al intentar abrir conexiones TCP a puertos 80 o 443. El síntoma típico es una serie de paquetes SYN enviados desde la IP del contenedor hacia la dirección pública, sin que el servidor remoto responda. En la captura de tráfico del host se observan los paquetes de salida, pero nunca aparecen respuestas ni retransmisiones de ACK. El problema se manifiesta de forma aislada: algunos contenedores (por ejemplo, un HAProxy) funcionan, mientras que otros quedan “ciegos” para HTTP/HTTPS.
Este patrón indica que el tráfico saliente no está siendo traducido correctamente por la SNAT (Source NAT) configurada en la capa de firewall o en las reglas de nftables. Cuando la traducción falla, el paquete sale con una dirección de origen que no es enrutable desde Internet, y el servidor remoto descarta la conexión.
Causa
Varias causas pueden producir este comportamiento en una configuración SDN de Proxmox:
-
Regla de SNAT incompleta o mal posicionada
En nftables la cadenapostroutingdebe aplicarmasqueradea todo el tráfico que salga del rango interno (192.168.18.0/24). Si la regla está después de una reglareturnque coincide con la misma rango, el paquete nunca llega a la regla de masquerade. -
Reglas de firewall en el contenedor que bloquean puertos de salida
El firewall interno del LXC (o la políticaDROPpor defecto del host) puede permitir DNS y ICMP pero negar cualquier otro puerto TCP. En el ejemplo, se aceptan puertos 53 y 8006, pero no 80/443. -
Política de NAT en el host que sobrescribe la SNAT del datacenter
Proxmox permite habilitar SNAT a nivel de datacenter y también a nivel de host. Si ambas están activas y la regla del host es más restrictiva, la traducción del datacenter nunca se ejecuta. -
Interfaz de salida equivocada
En configuraciones SDN, la interfaz virtual (Vnet0) puede estar vinculada a una zona que no tiene ruta predeterminada hacia la interfaz física que lleva la IP pública. El tráfico se envía por una ruta que no tiene NAT. -
Regla
returnen la cadenapostroutingque excluye la subred
La reglaip daddr 192.168.18.0/24 returnimpide que los paquetes cuyo destino sea la subred interna se sometan a masquerade, pero también puede capturar paquetes con destino externo si la coincidencia es demasiado amplia. -
Conflicto entre iptables y nftables
Si el host tiene reglas iptables activas que manipulan NAT, pueden interferir con nftables, provocando que el paquete sea marcado como “already NATed” y se descarte.
Solución
Una solución robusta se basa en tres pilares: (a) garantizar que la cadena postrouting aplique masquerade a todo el tráfico saliente del rango interno, (b) revisar y simplificar las políticas de firewall en los contenedores, y (c) validar que la ruta predeterminada del host apunte a la interfaz con la IP pública.
1. Normalizar la cadena postrouting
# Borrar reglas existentes que puedan interferir
nft flush table inet nat
# Crear tabla y cadena postrouting con política accept
nft add table inet nat
nft 'add chain inet nat postrouting { type nat hook postrouting priority srcnat \; policy accept }'
# Regla de masquerade para todo el rango interno
nft add rule inet nat postrouting ip saddr 192.168.18.0/24 oifname "vmbr0" masquerade
Puntos clave:
oifname "vmbr0"asegura que la NAT se aplique solo cuando el paquete sale por la interfaz que tiene la IP pública.- No se incluye ninguna regla
returnantes demasquerade; de lo contrario, el paquete se detendría.
2. Simplificar firewall en los contenedores
En la mayoría de los casos, basta con una política accept para tráfico saliente y reglas específicas para puertos que deben permanecer bloqueados. Un ejemplo mínimo dentro del contenedor:
# Dentro del LXC
nft flush table inet filter
nft add table inet filter
nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
# Opcional: bloquear puertos no deseados
# nft add rule inet filter output tcp dport {23, 25} drop
Si se necesita mantener reglas de entrada, asegúrese de que no haya una regla output que niegue 80/443.
3. Verificar rutas y interfaces
# En el host
ip route show default
# Debe apuntar a la interfaz que tiene la IP pública, por ejemplo:
# default via 203.0.113.1 dev vmbr0 proto static
# Verificar que la zona SDN tenga la puerta de enlace correcta
pvesh get /nodes/<node>/sdn/zones/CTs
# La puerta de enlace debe ser 192.168.18.1
Si la ruta predeterminada apunta a vnet0 o a otra interfaz sin NAT, corríjala:
ip route replace default via 203.0.113.1 dev vmbr0
4. Desactivar reglas redundantes de SNAT a nivel de datacenter
En la UI de Proxmox, desactive la opción “SNAT” en el nivel de datacenter si ya está gestionada por nftables en el host. Mantener ambas activas genera una doble‑NAT que a veces bloquea la respuesta del servidor remoto.
5. Revisar coexistencia iptables/nftables
# Listar reglas iptables que manipulan NAT
iptables -t nat -L -nv
# Si aparecen reglas que hacen SNAT/MASQUERADE, elimínelas
iptables -t nat -F
Proxmox usa nftables por defecto; mantener iptables limpio evita colisiones.
Cuándo aplicar esta solución
- Síntomas: DNS funciona, ICMP responde, pero
curl http://...ocurl https://...nunca recibe respuesta; los paquetes SYN aparecen entcpdumppero no hay ACK. - Entorno: Proxmox con SDN, contenedores LXC en una zona que usa una subred privada (p.ej., 192.168.18.0/24) y una única IP pública en
vmbr0. - No aplica: Cuando el tráfico saliente se enruta a través de un router externo que ya realiza NAT, o cuando se usa una solución de proxy transparente que gestiona la traducción.
Código
# 1. Resetear tabla NAT y crear regla de masquerade
nft flush table inet nat
nft add table inet nat
nft 'add chain inet nat postrouting { type nat hook postrouting priority srcnat \; policy accept }'
nft add rule inet nat postrouting ip saddr 192.168.18.0/24 oifname "vmbr0" masquerade
# 2. Simplificar firewall del contenedor (ejemplo dentro del LXC)
nft flush table inet filter
nft add table inet filter
nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
# 3. Verificar y corregir ruta por defecto del host
ip route show default
ip route replace default via <GATEWAY_PUBLIC_IP> dev vmbr0
# 4. Eliminar reglas NAT en iptables (si existen)
iptables -t nat -F
Verificación
-
Comprobar NAT
nft list chain inet nat postroutingDebería mostrarse la regla
masqueradeconip saddr 192.168.18.0/24. -
Probar conectividad
Desde el contenedor:curl -I https://www.google.comDebería devolver encabezados HTTP sin tiempo de espera.
-
Captura de tráfico
En el host:tcpdump -i vmbr0 -n host 192.168.18.58 and port 443Verifique que aparecen paquetes SYN y SYN‑ACK, y que la dirección de origen es la IP pública del host, no la interna.
-
Revisar logs de firewall
journalctl -u nftablesNo deben aparecer denegaciones para puertos 80/443.
Notas adicionales
- En configuraciones con varios rangos internos, repita la regla
masqueradepara cada subred o useip saddr 10.0.0.0/8según convenga. - Si necesita que algunos contenedores mantengan su IP original (por ejemplo, para listas blancas externas), añada excepciones antes de la regla
masqueradeusandoip saddr <IP> accept. - Cuando se habilita
SNATa nivel de datacenter, recuerde desactivar la reglamasqueradeen el host para evitar doble NAT. - En entornos con alta concurrencia, considere usar
nft add rule inet nat postrouting oifname "vmbr0" ip saddr 192.168.18.0/24 counter masqueradepara obtener métricas de tráfico NAT.