Corrixide o tráfico de Docker e WSL que ignora a VPN do anfitrión
Os contedores e WSL manteñen as súas propias rutas, resolvedor e camiño de reenvío, así que un túnel de anfitrión que funciona déixaos con DNS obsoleto ou sen saída. Corrixide cada capa.
Pasei boa parte dunha mañá desta primavera perseguindo un contedor que non podía resolver un só nome interno mentres o navegador de ao lado abría o wiki sen queixarse. O túnel estaba activo, o navegador probábao, e o contedor quedaba en outro espazo de nomes de rede cunha outra táboa de rutas e outro resolvedor. O que segue é a orde que comprobo agora: primeiro as táboas de rutas, despois a resolución, logo o reenvío e por último a plataforma anfitrioa. Paráde na primeira etapa que xa falla, porque engadir regras máis abaixo na pila agocha a fronteira en vez de movela.
Comparade as tres táboas de rutas antes de cambiar unha regra
Un contedor na ponte predeterminada de Docker ten unha única ruta predeterminada que apunta á pasarela docker0 e non ten nin idea de que tun0 existe. O anfitrión ten tun0, as rutas que empurrou a VPN e quizais unha regra de política que envía o tráfico de proba por unha interface distinta da que esperades. WSL engade unha terceira táboa detrás do seu propio adaptador virtual. Imprimídeas as tres antes de editar calquera delas.
| Context | Command | What a wrong answer looks like |
|---|---|---|
| Host | ip route get 10.8.0.20 | Leaves a physical interface instead of tun0 |
| Container | ip route | Only 172.17.0.0/16 via 172.17.0.1 |
| Container DNS | cat /etc/resolv.conf | A public resolver, or an upstream the tunnel never pushed |
| WSL | ip addr; ip route | Only the WSL subnet, no Windows VPN adapter |
Se a única ruta do contedor é a ponte e tun0 nunca aparece no seu espazo de nomes, nada do que segue axudará: ningún cambio de servidor de nomes crea unha ruta, e ningunha ruta crea a identidade de túnel que o par aceptará. A orde importa porque un fallo de DNS e un fallo de ruta son idénticos desde dentro dunha shell.
Apuntade o resolvedor ao túnel en vez de a un público
No anfitrión, resolvectl status lista os servidores DNS por ligazón, e readlink -f /etc/resolv.conf divos se o ficheiro é o stub de systemd, un ficheiro de NetworkManager ou algo que reescribiu o cliente dun provedor. Dentro dun contedor nunha rede definida polo usuario, /etc/resolv.conf nomea 127.0.0.11, o resolvedor integrado de Docker, que reenvía ao upstream co que se configurou o daemon. Un contedor na ponte predeterminada, en cambio, recibiu unha copia do ficheiro do anfitrión cando se creou, e consérvaa.
Esa copia é a razón pola que un túnel que se levanta despois de arrancar o contedor deixa o contedor consultando o upstream antigo. Ou pasades o resolvedor da VPN explicitamente con docker run --dns 10.8.0.53, ou estabelecedes "dns": ["10.8.0.53"] en /etc/docker/daemon.json e reiniciades o daemon, ou recreades o contedor cando o túnel estea activo. Recrealo adoita ser máis barato que depurar un resolvedor que era correcto hai unha hora.
Cambiar a un resolvedor público é a reparación que mantén o síntoma. Resolve internet e falla en todas as zonas privadas, e pode saltar en silencio o filtrado que o túnel existe para impor. Se intentades escribir dentro do /etc/resolv.conf dun contedor en execución, esperade Device or resource busy: Docker monta ese ficheiro con bind, así que a edición necesita primeiro un desmontaxe. Estabelecer --dns na creación é a secuencia que sobrevive a un reinicio.
Reenviade e traducide cando os contedores toman prestado o túnel
Coa ponte predeterminada, o anfitrión é un encamiñador entre docker0 e tun0, e por defecto non fai ningún dos dous traballos. Activade o reenvío e traducide o enderezo de orixe para que o par vexa un enderezo de túnel do anfitrión en vez dun ao que non ten ruta de volta (substituíde tun0 polo nome que o voso cliente lle dá á interface):
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i docker0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTAs regras FORWARD importan nos anfitrións cuxa política de reenvío é DROP. Docker insire as súas propias cadeas, así que probade cun paquete en vez de ler iptables -S: desde o contedor, ip route get 10.8.0.20 debería nomear a pasarela da ponte, e nc -vz 10.8.0.20 443 debería conectar ou esgotar o tempo no túnel en vez de no resolvedor.
Sen MASQUERADE o par necesita unha ruta de volta a 172.17.0.0/16, que a maioría das VPN de acceso remoto non empurrarán. Aquí tamén rompe o plan un cliente baseado en políticas: se só captura o tráfico de UID ou cgroups concretos, as vosas regras de reenvío nunca ven o paquete dentro do túnel, e a corrección pertence á política de encamiñamento do cliente e non a iptables. As regras engadidas a man desaparecen ao reiniciar, así que póndeas nunha unidade de systemd ou nunha táboa netfilter-persistent e probade de novo tras un reinicio, xa que un túnel que se levanta tarde pode varrelas.
Pónde o túnel dentro do contedor cando o obxectivo é o illamento
Se un contedor ten que saír por un nodo concreto, executade o cliente nese contedor. Necesita --cap-add NET_ADMIN e --device /dev/net/tun, e entón posúe o seu propio tun0, as súas rutas e o seu cortalumes. Se outros contedores pasan por el, tamén necesita reenvío e unha regra MASQUERADE dentro do contedor, e eses pares necesitan unha ruta ou un proxy explícitos que apunten a el; a ponte predeterminada de Docker non engade esa ruta por vós.
A rede do anfitrión, docker run --network host, é a versión barata: o contedor únese ao espazo de nomes do anfitrión e ve as rutas do túnel directamente. Tamén descarta o illamento de rede, invita a colisións de portos e, na práctica, é unha opción de Linux. Un porto publicado non é un substituto. -p 8080:80 fai que un servizo sexa accesible desde o anfitrión; non fai que os enderezos internos 10.x sexan accesibles desde dentro do contedor, e confundir as dúas cousas é como un problema de encamiñamento se converte nun porto de base de datos exposto.
Distinguide un fallo de nomes dunha subrede solapada
A ponte predeterminada de Docker é 172.17.0.0/16, e moitos rangos de VPN corporativas están dentro de 172.16.0.0/12. Unha vez que o túnel posúe ese rango, o tráfico destinado a un contedor pode encamiñarse cara ao túnel. Os nomes fallan, o erro parece DNS, e o problema son os enderezos. Comprobade o destino, non o resolvedor: ip route get 172.17.0.5 no anfitrión, e despois comparade o prefixo da ponte co rango da VPN e con docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.
A reparación é mover un dos rangos: ou un rango de enderezos predeterminado distinto para Docker en daemon.json ou un rango distinto no concentrador. Engadir outro servidor de nomes non fai nada aquí, e tampouco o fai ssh -b: ligar o cliente a unha interface cambia o enderezo de orixe, non unha ruta de destino que apunta cara ao túnel. Reparto rápido do traballo: un nome que se resolve no anfitrión pero non no contedor é un problema de resolvedor, mentres que unha conexión IP directa que se colga no contedor pero funciona no anfitrión é un problema de ruta ou de cadea de reenvío.
WSL e Docker Desktop non seguen as regras de Linux
No seu modo NAT predeterminado, WSL está detrás do seu propio adaptador virtual e non recibe as rutas internas nin os sufixos DNS da VPN de Windows, e por iso o navegador funciona e ping dentro da distribución non. A rede espello, dispoñible en compilacións recentes de Windows 11, comparte en cambio as interfaces do anfitrión, pero hai que activala en .wslconfig e reiniciar a distribución con wsl --shutdown. Confirmade o modo activo con wslinfo --networking-mode antes de aplicar unha corrección, porque a resposta NAT é incorrecta para o modo espello e viceversa.
Docker Desktop engade outra capa. Os contedores Linux en Windows están detrás dunha implementación NAT chamada historicamente vpnkit, e o seu camiño difire do dun anfitrión Linux nativo. Os contedores Windows conéctanse mediante un conmutador virtual de Hyper-V, e un cliente corporativo pode bloquear o tráfico dese conmutador, así que os contedores Linux da mesma máquina poden chegar á rede mentres os contedores Windows non teñen nada. Un conmutador externo ligado ao adaptador físico é a solución habitual, e cambia a identidade de rede do contedor de maneiras que a política pode non permitir.
Antes de escribir outra regra, percorrede as catro capas en orde e parád onde unha delas estea mal:
- Dentro do contedor:
ip routechega ao prefixo privado, e/etc/resolv.confnomea un upstream que o resolve? - No anfitrión: o reenvío está activo, NAT está ligado á interface do túnel, e a cadea de reenvío acepta a ponte cara ao túnel?
- No par: coñece unha ruta de volta, ou confiades en NAT para agochar a subrede do contedor?
- Na plataforma: WSL está en modo NAT ou espello, e o contedor é Linux ou Windows?
Executade de novo cada proba nun contedor ou unha shell novos. Un proceso de longa duración cunha resposta na caché ou unha conexión estabelecida seguirá fallando cando a configuración xa é correcta, e ese fallo parece exactamente unha corrección que non funcionou.