El host navega por el túnel y el contenedor de al lado no alcanza nada. Publicar un puerto desde un contenedor que ejecuta su propio cliente VPN es peor: la petición llega y la respuesta nunca sale. Ambas son decisiones de enrutamiento en el host, y Docker solo es el mensajero.

Tres preguntas deciden cuál de los dos casos tienes delante, y tardan alrededor de un minuto.

Dos fallos que se presentan igual

El primero es un problema de ruta de retorno. El puerto publicado se traduce hacia el contenedor, la petición llega y la respuesta se enruta por la ruta predeterminada del host, que el cliente OpenVPN ha apuntado a tun0. La respuesta sale desde una dirección que el par remoto no espera y se descarta. El segundo es una colisión de prefijos: el puente predeterminado de Docker posee 172.17.0.0/16 y un túnel corporativo empuja el mismo rango.

Uno se corrige con una tabla de enrutamiento y el otro renumerando un puente. Editar antes de saber cuál es cuál es la forma de romper un contenedor que funcionaba.

Decídelo con tres preguntas

Pregunta uno: ¿desaparece el fallo cuando detienes la VPN? Si todo funciona con el túnel caído, sospecha de la ruta de retorno. Pregunta dos: ¿ip route show table all lista el mismo prefijo dos veces, una en un puente y otra en tun0? Esa es la colisión. Pregunta tres: ¿la dirección afectada es un contenedor, una invitada NAT o una invitada en puente?

La tercera pregunta importa porque cada caso tiene una solución correcta distinta, y aplicar la solución del contenedor a una invitada libvirt desperdicia una noche.

ip route
ip route show table all | grep -E '172\.1[7-9]|10\.8'
docker network inspect bridge --format '{{.IPAM.Config}}'

Ruta de retorno: una tabla para una dirección de origen

Cuando el tráfico del contenedor debe salir por la interfaz física mientras el host usa el túnel, el enrutamiento basado en el origen es el cambio más pequeño. Añade una regla que coincida con la dirección del contenedor y envíala a una tabla dedicada. Nada más del host se mueve.

ip rule add from 172.22.0.3 lookup 10
ip route add default via 172.22.0.1 table 10

Cada paquete cuya origen es esa dirección consulta primero la tabla 10, y la tabla 10 lo envía a la puerta de enlace del puente en lugar del túnel.

Confírmalo con ip route get 1.1.1.1 from 172.22.0.3. La respuesta debe nombrar la interfaz del puente, no tun0. Las reglas añadidas así desaparecen al reiniciar, así que ponlas en la unidad o el gancho de red que posee el contenedor.

El mismo patrón funciona para un contenedor WireGuard, donde la ruta de respuesta también necesita una regla explícita.

Colisión de prefijos: renumera el puente

Una tabla de enrutamiento envía un prefijo a un solo lugar. Si la VPN empuja 172.17.0.0/16 mientras el puente de Docker ya está ahí, gana la métrica más baja: Docker instala su ruta conectada con métrica 0 y la ruta empujada llega con 50. Entonces el túnel parece roto mientras Docker sigue funcionando, o al revés, según el orden de instalación. Renumera el puente en lugar de pelearte con la métrica.

{
  "bip": "10.200.0.1/24",
  "default-address-pools": [{"base": "10.201.0.0/16", "size": 24}]
}

Reinicia Docker tras el cambio y vuelve a crear las redes construidas con el grupo antiguo. Cambiar la métrica solo traslada el conflicto al siguiente arranque.

Las VM necesitan otra respuesta distinta

Una invitada libvirt en la red NAT predeterminada hereda el enrutamiento del host y deja de alcanzar el exterior cuando el túnel del host posee la ruta predeterminada. Cambiar la invitada a un dispositivo en puente o macvtap le da su propio camino en la red física. En virt-manager, añade hardware, elige Network, pon el origen en el dispositivo de host macvtap, mantén virtio como modelo y luego desactiva la interfaz NAT antigua.

Una invitada macvtap normalmente no puede alcanzar su propio host. Es un comportamiento esperado y no un fallo, y corresponde a la nota que dejas.

Para una invitada que solo necesita la LAN del host, el cliente del proveedor puede tener el interruptor que falta: con Mullvad, mullvad lan set allow restaura el alcance local sin tocar libvirt. Y cuando un contenedor debe ser accesible solo por el túnel, ata el puerto publicado a la dirección del túnel, -p 10.8.0.5:8080:8080, en lugar de publicarlo en todas las direcciones.

Artículos relacionados