Jak přes OpenVPN směrovat jen provoz kontejnerů Docker
Nechte host na jeho obvyklém připojení a do tunelu vložte jednu podsíť kontejnerů: pořadí NAT, pravidla zdroje, iroute v OpenVPN a kontroly MSS.
Paket kontejneru nikdy nedorazí do tun0 a další oprávnění na kontejneru to nezmění. Mezi hostem a tunelem stojí dvě hledání trasy: jedno uvnitř síťového jmenného prostoru hosta, jedno na hostiteli poté, co rámec dopadne na most, a druhé se ptá jiné tabulky s jinými pravidly shody. Moje referenční uspořádání je Linuxový hostitel s klientem OpenVPN, který drží 10.8.0.10 na tun0, mostem Docker na 172.18.0.0/16 a privátní adresou, kterou dosáhne jen strana tunelu. Cíl zůstává úzký: tato podsíť hosta odchází tunelem, zatímco hostitel si drží obvyklé připojení.
Provozovat VPN klienta uvnitř kontejneru je jiný návrh, ne zkratka. Kontejner pak vlastní svůj tunel i přihlašovací údaje a práce se přesouvá do Dockerfile místo aby zůstala v iptables.
Předávání vybírá trasu podle cíle, pak NAT přepíše zdroj
Když je nastaveno net.ipv4.ip_forward=1, předávaný paket se směruje pouze podle cíle, v hlavní tabulce. Zdroj v tom rozhodnutí nehraje roli. Teprve po volbě trasy jádro spustí POSTROUTING a právě tam sedí vlastní pravidlo Dockeru: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Všechno, co opustí most na jiné rozhraní, se přeloží a adresa, kterou to získá, závisí na rozhraní, kam to trasa poslala.
Toto pravidlo vysvětluje většinu symptomů, na které se mě ptají. Pokud hlavní tabulka nemá trasu pro privátní cíl, paket opustí připojení s fyzickou adresou hostitele. Váš poskytovatel cíl zahodí, nebo vzdálené místo zahodí zdroj RFC 1918, který nečekalo, a kontejneru vyprší čas, zatímco tunel zůstává nahoru a zdravý. Zachycení na tun0 nic neukáže, a to je ta stopa. Pokud trasa skutečně míří na tun0, MASQUERADE přepíše zdroj na 10.8.0.10 a tentýž kontejner funguje, aniž byste se čehokoli dotkli.
Ptejte se na vyhledání, které pojmenuje zdroj hosta. Bez from a iif hostitel odpovídá za svůj vlastní socket a hlásí cestu, kterou předávaný paket nikdy nenastoupí:
ip route get 192.168.11.222 from 172.18.0.5 iif br-appnetPokud tato odpověď pojmenuje připojení, tunel není ve hře a žádné nastavení na straně tunelu nepomůže.
Převeďte hosta na vlastní adresu tunelu
Nejmenší fungující změna je výslovné přepsání zdroje na cestě ven. Vložte ho, místo abyste ho přidávali: Docker už sedí na čele POSTROUTING, takže pravidlo přidané pomocí -A se vyhodnocuje za tím Dockerovým a nikdy se nespustí.
iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10Dvě podmínky rozhodují, zda to vydrží. Adresa musí být ta, kterou klient skutečně drží, takže ji připněte záznamem pro každého klienta obsahujícím ifconfig-push 10.8.0.10 255.255.255.0; pravidlo, které pojmenuje adresu, jež nebyla nikdy přidělena, způsobí tiché zahazování, ne chybu. A vzdálená strana musí tento zdroj přijmout a směrovat odpovědi zpět, proto seznam povolených tam pojmenuje adresu tunelu, ne podsíť hosta. Když je adresa klienta dynamická, -j MASQUERADE s -o tun0 následuje to, co rozhraní drží, za cenu vzdálené politiky, proti níž nemůžete psát pevnou hodnotu.
Filtrování je druhá polovina. Docker nastaví politiku FORWARD na drop a při každém restartu démona vyčistí řetězce, které vlastní, proto dejte místní politiku do DOCKER-USER, která přežívá:
iptables -I DOCKER-USER -i br-appnet -o tun0 -j ACCEPT
iptables -I DOCKER-USER -i tun0 -o br-appnet -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTPravidlo zpátečky je důležité: odpověď přicházející na tun0 pro spojení otevřené mostem není přijata jen proto, že směr ven byl.
Držte hostitele přímo pomocí pravidla zdroje
Když má celý most používat tunel, ale nic jiného na hostiteli ne, přeskočte NAT a dejte tomu zdroji vlastní tabulku. Adresy hosta pak zůstávají viditelné od začátku do konce.
echo "200 vpnbox" >> /etc/iproute2/rt_tables
ip rule add from 172.18.0.0/16 lookup vpnbox pref 1000
ip route add default dev tun0 table vpnboxPravidla se konzultují před hlavní tabulkou a from odpovídá zdroji. Vlastní vnější pakety tunelu nesou fyzický zdroj hostitele, takže nikdy neodpovídají from 172.18.0.0/16 a tunel nemůže směrovat sám sebe zpět do sebe. Právě toto sebevyloučení dělá uspořádání stabilním.
Zpáteční cesta teď potřebuje pomoc. Vzdálená strana vidí nepřeloženou 172.18.0.x a nemá důvod vědět, kde bydlí. Na serveru potřebuje soubor pro každého klienta iroute 172.18.0.0 255.255.0.0, aby vnitřní tabulka serveru předala tento prefix tomuto klientovi, a pushnutou route 172.18.0.0 255.255.0.0, aby ostatní klienti věděli, že existuje.
Dvě kontroly, než tomu uvěříte. Spusťte ip route show default: s redirect-gateway def1 tabulka drží 0.0.0.0/1 a 128.0.0.0/1 přes tun0, a hostitel rozhodně není přímo, protože tyto poloviny výchozí vyhrávají, aniž by nahradily default. Pravidla přidaná pomocí ip rule add také mizí při restartu, proto je noste v up skriptu.
Velikost paketu je druhá závada, na kterou narazíte
Most s 1500 bajty dá tunelu víc, než unese, jakmile se přidají vnější hlavičky UDP a IP. Malé výměny projdou, takže DNS se vyřeší a TLS handshake dokončí, pak se větší upload zasekne bez jakékoli chyby. Odesílatel stále opakuje stejnou velikost segmentu, protože ICMP zpráva o potřebě fragmentace, která by mu řekla, aby zmenšil, nikdy nedorazí.
Opravte to na rozhraní, na MSS, nebo obojí. Sevření nepotřebuje spolupráci hosta:
ip link set dev tun0 mtu 1400
iptables -t mangle -A FORWARD -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuSevření přepisuje jen TCP. UDP, včetně QUIC, potřebuje místo toho snížené rozhraní hosta, s --opt com.docker.network.driver.mtu=1400 při vytváření sítě nebo odpovídajícím klíčem compose.
DNS a kontroly, které stojí za opakování
Překlad jmen jde stejnou cestou. Vestavěný resolver Dockeru na 127.0.0.11 předává na nakonfigurované servery hostitele, takže kontejner dědí, co hostitel používá. Když privátní zónu odpovídá jen resolver dosažitelný tunelem, nasměrujte hosta na tuto adresu a testujte pomocí getent hosts uvnitř kontejneru, ne na hostiteli.
- Spusťte selhávající požadavek uvnitř hosta; test na straně hostitele používá jinou zdrojovou adresu a problém skrývá.
- Čtěte počítadla na NAT a filtrovacích pravidlech, která jste přidali. Pravidlo s nulou shod je chyba směrování, ne politiky.
- Ověřte, že adresa tunelu ve vašem pravidle SNAT je ta, kterou klient skutečně drží.
- Snižte MTU hosta nebo sevřete MSS, než obviníte server ze zaseknutí.
Projděte ten seznam a zastavte se u první závady. Ta vrstva je ta, kterou je třeba opravit; cokoli přidané níž jen skrývá hranici, která se láme.