Většinu jednoho rána jsem letos na jaře strávil honěním kontejneru, který nedokázal přeložit jediné interní jméno, zatímco prohlížeč vedle něj otevíral wiki bez stížnosti. Tunel byl nahoře, prohlížeč to dokazoval, a kontejner zůstával v jiném síťovém jmenném prostoru s jinou směrovací tabulkou a jiným resolverem. Následuje pořadí, které teď kontroluji: nejprve směrovací tabulky, potom překlad jmen, potom předávání a nakonec platforma hostitele. Zastavte se u prvního kroku, který už selhává, protože přidávání pravidel níž v zásobníku hranici skrývá, místo aby ji posunulo.

Porovnejte tři směrovací tabulky, než změníte pravidlo

Kontejner na výchozím mostu Dockeru má jednu výchozí trasu ukazující na bránu docker0 a nemá tušení, že tun0 existuje. Hostitel má tun0, trasy, které VPN protlačila, a možná pravidlo zásady, které posílá zkušební provoz přes jiné rozhraní, než očekáváte. WSL přidává třetí tabulku za svým vlastním virtuálním adaptérem. Vypište všechny tři, než kteroukoli upravíte.

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

Pokud je jedinou trasou kontejneru most a tun0 se v jeho jmenném prostoru nikdy neobjeví, nic níže nepomůže: žádná změna jmenného serveru nevytvoří trasu a žádná trasa nevytvoří identitu tunelu, kterou protějšek přijme. Na pořadí záleží, protože selhání DNS a selhání trasy vypadají zevnitř shellu stejně.

Namiřte resolver na tunel místo na veřejný

Na hostiteli, resolvectl status vypisuje servery DNS pro každé spojení, a readlink -f /etc/resolv.conf vám řekne, zda je soubor stub systemd, soubor NetworkManager, nebo něco, co přepsal klientský software dodavatele. Uvnitř kontejneru v uživatelem definované síti, /etc/resolv.conf pojmenovává 127.0.0.11, vestavěný resolver Dockeru, který předává na ten upstream, se kterým byl daemon nakonfigurován. Kontejner na výchozím mostu ale dostal při vytvoření kopii souboru hostitele a drží si ji.

Ta kopie je důvod, proč tunel, který se zvedne po startu kontejneru, nechává kontejner dotazovat starý upstream. Buď předejte resolver VPN výslovně pomocí docker run --dns 10.8.0.53, nebo nastavte "dns": ["10.8.0.53"] v /etc/docker/daemon.json a restartujte daemon, nebo kontejner znovu vytvořte, až je tunel nahoře. Znovuvytvoření je obvykle levnější než ladění resolveru, který byl před hodinou správný.

Nahrazení veřejným resolverem je oprava, která zachovává symptom. Překládá internet a selhává v každé soukromé zóně a může tiše obejít filtrování, kvůli kterému tunel existuje. Pokud zkusíte zapisovat do /etc/resolv.conf běžícího kontejneru, očekávejte Device or resource busy: Docker ten soubor bind-mountuje, takže úprava nejprve potřebuje odmountování. Nastavení --dns při vytvoření je sled, který přežije restart.

Předávejte a překládejte, když si kontejnery půjčují tunel

S výchozím mostem je hostitel routerem mezi docker0 a tun0 a ve výchozím nastavení nedělá ani jednu z těchto úloh. Zapněte předávání a překládejte zdrojovou adresu, aby protějšek viděl adresu tunelu hostitele místo adresy, na kterou nemá zpětnou trasu (nahraďte tun0 tím, jak váš klient nazývá rozhraní):

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

Pravidla FORWARD jsou důležitá na hostitelích, jejichž zásada předávání je DROP. Docker vkládá své vlastní řetězy, takže testujte paketem, ne čtením iptables -S: z kontejneru, ip route get 10.8.0.20 má pojmenovat bránu mostu, a nc -vz 10.8.0.20 443 se má připojit nebo vypršet na tunelu, ne na resolveru.

Bez MASQUERADE protějšek potřebuje zpětnou trasu do 172.17.0.0/16, kterou většina VPN pro vzdálený přístup neprotlačí. Tady se také láme plán u klienta založeného na zásadách: pokud zachytává jen provoz od konkrétních UID nebo cgroups, vaše pravidla předávání paket uvnitř tunelu nikdy neuvidí, a oprava patří do směrovací zásady klienta, ne do iptables. Pravidla přidaná ručně zmizí při restartu, takže je dejte do systemd unit nebo tabulky netfilter-persistent a po restartu znovu otestujte, protože tunel, který se zvedne pozdě, je může smést.

Umístěte tunel dovnitř kontejneru, když je cílem izolace

Pokud musí jeden kontejner vycházet přes konkrétní uzel, spusťte klienta v tomto kontejneru. Potřebuje --cap-add NET_ADMIN a --device /dev/net/tun, a pak vlastní svůj tun0, trasy a firewall. Pokud přes něj procházejí jiné kontejnery, potřebuje uvnitř kontejneru i předávání a pravidlo MASQUERADE a tyto protějšky potřebují explicitní trasu nebo proxy, která na něj ukazuje; výchozí most Dockeru vám tu trasu nepřidá.

Síť hostitele, docker run --network host, je levná verze: kontejner se připojí ke jmennému prostoru hostitele a vidí trasy tunelu přímo. Také zahazuje izolaci sítě, zve ke kolizím portů a v praxi je to možnost Linuxu. Publikovaný port není náhrada. -p 8080:80 zpřístupní službu z hostitele; nezpřístupní interní adresy 10.x zevnitř kontejneru, a zaměňování těchto dvou věcí je způsob, jak se problém směrování změní v otevřený port databáze.

Odlište selhání jména od překrývající se podsítě

Výchozí most Dockeru je 172.17.0.0/16 a mnoho firemních fondů VPN leží uvnitř 172.16.0.0/12. Jakmile tunel ten rozsah vlastní, provoz určený kontejneru se místo toho může směrovat do tunelu. Jména selhávají, chyba vypadá jako DNS a problém je v adresách. Kontrolujte cíl, ne resolver: ip route get 172.17.0.5 na hostiteli, pak porovnejte prefix mostu s fondem VPN a s docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'.

Oprava je posunout jeden z rozsahů: buď jiný výchozí fond adres pro Docker v daemon.json nebo jiný fond na koncentrátoru. Přidání dalšího jmenného serveru tu nic neudělá, a stejně tak ssh -b: připoutání klienta k rozhraní mění zdrojovou adresu, ne cílovou trasu, která ukazuje do tunelu. Rychlé rozdělení práce: jméno, které se přeloží na hostiteli, ale ne v kontejneru, je problém resolveru, zatímco přímé IP spojení, které v kontejneru visí, ale na hostiteli funguje, je problém trasy nebo předávacího řetězu.

WSL a Docker Desktop se neřídí pravidly Linuxu

Ve výchozím režimu NAT stojí WSL za svým vlastním virtuálním adaptérem a nedostává interní trasy ani suffixy DNS VPN Windows, a proto prohlížeč funguje a ping uvnitř distribuce ne. Zrcadlové síťování, dostupné v nedávných sestaveních Windows 11, místo toho sdílí rozhraní hostitele, ale musí se zapnout v .wslconfig a distribuce restartovat pomocí wsl --shutdown. Ověřte aktivní režim pomocí wslinfo --networking-mode před aplikací opravy, protože odpověď pro NAT je špatná pro zrcadlový režim a naopak.

Docker Desktop přidává další vrstvu. Kontejnery Linuxu na Windows stojí za implementací NAT historicky zvanou vpnkit a jejich cesta se liší od nativního hostitele Linuxu. Kontejnery Windows se připojují přes virtuální přepínač Hyper-V a firemní klient může blokovat provoz z tohoto přepínače, takže kontejnery Linuxu na stejném stroji se mohou dostat do sítě, zatímco kontejnery Windows nemají nic. Externí přepínač svázaný s fyzickým adaptérem je obvyklé řešení a mění síťovou identitu kontejneru způsoby, které zásady nemusí dovolit.

Než napíšete další pravidlo, projděte čtyři vrstvy v pořadí a zastavte se tam, kde je jedna z nich špatně:

  • Uvnitř kontejneru: dosáhne ip route soukromého prefixu a pojmenovává /etc/resolv.conf upstream, který ho přeloží?
  • Na hostiteli: je zapnuté předávání, je NAT svázán s rozhraním tunelu a přijímá předávací řetěz most k tunelu?
  • Na protějšku: zná zpětnou cestu, nebo spoléháte na NAT, aby skryl podsíť kontejneru?
  • Na platformě: je WSL v režimu NAT nebo zrcadlovém a je kontejner Linux nebo Windows?

Spusťte každý test znovu v novém kontejneru nebo shellu. Dlouho běžící proces s uloženou odpovědí nebo navázaným spojením bude selhávat i poté, co je konfigurace správná, a toto selhání vypadá přesně jako oprava, která nefungovala.

Související články