Vaig passar bona part d'un matí d'aquesta primavera perseguint un contenidor que no podia resoldre ni un sol nom intern mentre el navegador del costat obria el wiki sense queixar-se. El túnel estava actiu, el navegador ho demostrava, i el contenidor es quedava a un altre espai de noms de xarxa amb una altra taula de rutes i un altre resolutor. El que segueix és l'ordre que comprovo ara: primer les taules de rutes, després la resolució, després el reenviament i, finalment, la plataforma amfitriona. Atureu-vos al primer pas que ja falla, perquè afegir regles més avall a la pila amaga la frontera en lloc de moure-la.

Compareu les tres taules de rutes abans de canviar una regla

Un contenidor al pont predeterminat de Docker té una única ruta predeterminada que apunta a la passarel·la docker0 i no té ni idea que tun0 existeix. L'amfitrió té tun0, les rutes que va empènyer la VPN i potser una regla de política que envia el trànsit de prova per una interfície diferent de la que espereu. WSL afegeix una tercera taula darrere del seu propi adaptador virtual. Imprimiu les tres abans d'editar-ne cap.

ContextCommandWhat a wrong answer looks like
Hostip route get 10.8.0.20Leaves a physical interface instead of tun0
Containerip routeOnly 172.17.0.0/16 via 172.17.0.1
Container DNScat /etc/resolv.confA public resolver, or an upstream the tunnel never pushed
WSLip addr; ip routeOnly the WSL subnet, no Windows VPN adapter

Si l'única ruta del contenidor és el pont i tun0 no apareix mai al seu espai de noms, res del que segueix ajudarà: cap canvi de servidor de noms crea una ruta, i cap ruta crea la identitat de túnel que el parell acceptarà. L'ordre importa perquè una fallada de DNS i una fallada de ruta són idèntiques des de dins d'una shell.

Apunteu el resolutor al túnel en lloc d'un de públic

A l'amfitrió, resolvectl status llista els servidors DNS per enllaç, i readlink -f /etc/resolv.conf us diu si el fitxer és l'stub de systemd, un fitxer de NetworkManager o alguna cosa que va reescriure el client d'un proveïdor. Dins d'un contenidor en una xarxa definida per l'usuari, /etc/resolv.conf anomena 127.0.0.11, el resolutor integrat de Docker, que reenvia a l'upstream amb què es va configurar el dimoni. Un contenidor al pont predeterminat, en canvi, va rebre una còpia del fitxer de l'amfitrió quan es va crear, i la conserva.

Aquesta còpia és la raó per la qual un túnel que s'aixeca després que arrenqui el contenidor deixa el contenidor consultant l'upstream antic. Passeu el resolutor de la VPN explícitament amb docker run --dns 10.8.0.53, o establiu "dns": ["10.8.0.53"] a /etc/docker/daemon.json i reinicieu el dimoni, o recreieu el contenidor quan el túnel estigui aixecat. Recrear-lo sol ser més barat que depurar un resolutor que era correcte fa una hora.

Canviar a un resolutor públic és la reparació que manté el símptoma. Resol internet i falla a totes les zones privades, i pot saltar-se en silenci el filtratge que el túnel existeix per fer complir. Si intenteu escriure dins del /etc/resolv.conf d'un contenidor en execució, espereu Device or resource busy: Docker munta aquest fitxer amb bind, així que l'edició necessita primer un desmuntatge. Establir --dns en crear-lo és la seqüència que sobreviu a un reinici.

Reenvieu i traduïu quan els contenidors manllevin el túnel

Amb el pont predeterminat, l'amfitrió és un encaminador entre docker0 i tun0, i per defecte no fa cap de les dues feines. Activeu el reenviament i traduïu l'adreça d'origen perquè el parell vegi una adreça de túnel de l'amfitrió en lloc d'una a la qual no té ruta de tornada (substituïu tun0 pel nom que el vostre client dona a la interfície):

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 ACCEPT

Les regles FORWARD importen en amfitrions la política de reenviament dels quals és DROP. Docker insereix les seves pròpies cadenes, així que proveu amb un paquet en lloc de llegir iptables -S: des del contenidor, ip route get 10.8.0.20 ha de nomenar la passarel·la del pont, i nc -vz 10.8.0.20 443 ha de connectar o esgotar el temps al túnel en lloc del resolutor.

Sense MASQUERADE el parell necessita una ruta de tornada a 172.17.0.0/16, que la majoria de VPN d'accés remot no empènyer. Aquí també es trenca el pla amb un client basat en polítiques: si només captura el trànsit d'UID o cgroups concrets, les vostres regles de reenviament no veuen mai el paquet dins del túnel, i la correcció pertany a la política d'encaminament del client i no a iptables. Les regles afegides a mà desapareixen en reiniciar, així que poseu-les en una unitat de systemd o en una taula netfilter-persistent i torneu a provar després d'un reinici, ja que un túnel que s'aixeca tard pot esborrar-les.

Poseu el túnel dins del contenidor quan l'objectiu és l'aïllament

Si un contenidor ha de sortir per un node concret, executeu el client en aquest contenidor. Necessita --cap-add NET_ADMIN i --device /dev/net/tun, i llavors posseeix el seu propi tun0, les seves rutes i el seu tallafoc. Si altres contenidors hi passen, també necessita reenviament i una regla MASQUERADE dins del contenidor, i aquests parells necessiten una ruta o un proxy explícits que hi apuntin; el pont predeterminat de Docker no afegeix aquesta ruta per vosaltres.

La xarxa de l'amfitrió, docker run --network host, és la versió barata: el contenidor s'uneix a l'espai de noms de l'amfitrió i veu les rutes del túnel directament. També descarta l'aïllament de xarxa, convida a col·lisions de ports i, a la pràctica, és una opció de Linux. Un port publicat no és un substitut. -p 8080:80 fa que un servei sigui accessible des de l'amfitrió; no fa que les adreces internes 10.x siguin accessibles des de dins del contenidor, i confondre les dues coses és com un problema d'encaminament es converteix en un port de base de dades exposat.

Distingiu una fallada de noms d'una subxarxa superposada

El pont predeterminat de Docker és 172.17.0.0/16, i molts rangs de VPN corporatives seuen dins de 172.16.0.0/12. Un cop el túnel posseeix aquest rang, el trànsit destinat a un contenidor es pot encaminar cap al túnel. Els noms fallen, l'error sembla DNS, i el problema són les adreces. Comproveu la destinació, no el resolutor: ip route get 172.17.0.5 a l'amfitrió, i després compareu el prefix del pont amb el rang de la VPN i amb docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.

La reparació és moure un dels rangs: o un altre rang d'adreces predeterminat per a Docker a daemon.json o un altre rang al concentrador. Afegir un altre servidor de noms no fa res aquí, i tampoc no ho fa ssh -b: enllaçar el client a una interfície canvia l'adreça d'origen, no una ruta de destinació que apunta al túnel. Repartiment ràpid de la feina: un nom que es resol a l'amfitrió però no al contenidor és un problema de resolutor, mentre que una connexió IP directa que es penja al contenidor però funciona a l'amfitrió és un problema de ruta o de cadena de reenviament.

WSL i Docker Desktop no segueixen les regles de Linux

En el seu mode NAT predeterminat, WSL se situa darrere del seu propi adaptador virtual i no rep les rutes internes ni els sufixos DNS de la VPN de Windows, i per això el navegador funciona i ping dins de la distribució no. La xarxa mirall, disponible a compilacions recents de Windows 11, comparteix en canvi les interfícies de l'amfitrió, però s'ha d'habilitar a .wslconfig i reiniciar la distribució amb wsl --shutdown. Confirmeu el mode actiu amb wslinfo --networking-mode abans d'aplicar una correcció, perquè la resposta NAT és incorrecta per al mode mirall i viceversa.

Docker Desktop afegeix una altra capa. Els contenidors Linux a Windows seuen darrere d'una implementació NAT anomenada històricament vpnkit, i el seu camí difereix del d'un amfitrió Linux natiu. Els contenidors Windows es connecten mitjançant un commutador virtual de Hyper-V, i un client corporatiu pot bloquejar el trànsit d'aquest commutador, així que els contenidors Linux de la mateixa màquina poden arribar a la xarxa mentre els contenidors Windows no en tenen cap. Un commutador extern vinculat a l'adaptador físic és la solució habitual, i canvia la identitat de xarxa del contenidor de maneres que la política pot no permetre.

Abans d'escriure una altra regla, recorreu les quatre capes en ordre i atureu-vos on una d'elles estigui malament:

  • Dins del contenidor: ip route arriba al prefix privat, i /etc/resolv.conf anomena un upstream que el resol?
  • A l'amfitrió: el reenviament està activat, la NAT està vinculada a la interfície del túnel i la cadena de reenviament accepta el pont cap al túnel?
  • Al parell: coneix una ruta de tornada, o confieu en la NAT per amagar la subxarxa del contenidor?
  • A la plataforma: WSL està en mode NAT o mirall, i el contenidor és Linux o Windows?

Torneu a executar cada prova en un contenidor o una shell nous. Un procés de llarga durada amb una resposta en memòria cau o una connexió establerta continuarà fallant quan la configuració sigui correcta, i aquesta fallada sembla exactament una correcció que no ha funcionat.

Articles relacionats