Kaip per OpenVPN nukreipti tik Docker konteinerių srautą
Palikite mazgą įprastame uplink'e ir į tunelį įdėkite vieną konteinerių potinklį: NAT tvarka, šaltinio taisyklės, OpenVPN iroute ir MSS patikros.
Konteinerio paketas niekada nepasiekia tun0 ir papildomi konteinerio pajėgumai to nepakeis. Tarp svečio ir tunelio yra dvi maršruto paieškos: viena svečio tinklo vardų erdvėje, kita mazge po to, kai kadras nusileidžia ant tilto, o antroji klausia kitos lentelės su kitomis atitikimo taisyklėmis. Mano etaloninė schema yra Linux mazgas, kuriame veikia OpenVPN klientas, laikantis 10.8.0.10 adresu tun0, Docker tiltas adresu 172.18.0.0/16, ir privatus adresas, kurį pasiekia tik tunelio pusė. Tikslas lieka siauras: ta svečio potinklis išeina per tunelį, o mazgas išlaiko įprastą uplink'ą.
VPN kliento paleidimas konteinerio viduje yra kita architektūra, o ne apylanka. Tada konteineris valdo savo tunelį ir prisijungimo duomenis, o darbas persikelia į Dockerfile, o ne lieka iptables.
Persiuntimas renkasi maršrutą pagal paskirties vietą, tada NAT perrašo šaltinį
Kai nustatytas net.ipv4.ip_forward=1 , persiųstas paketas maršrutizuojamas tik pagal paskirties vietą pagrindinėje lentelėje. Šaltinis tame sprendime nevaidina jokio vaidmens. Tik po maršruto pasirinkimo branduolys vykdo POSTROUTING , ir būtent ten yra pati Docker taisyklė: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Viskas, kas palieka tiltą į kitą sąsają, yra verčiama, o adresas, kuriuo jis tampa, priklauso nuo sąsajos, į kurią maršrutas jį nusiuntė.
Ši taisyklė paaiškina daugumą simptomų, apie kuriuos manęs klausia. Jei pagrindinėje lentelėje nėra maršruto į privatų tikslą, paketas išeina per uplink'ą su fiziniu mazgo adresu. Jūsų tiekėjas numeta tikslą, arba nuotolinė svetainė numeta RFC 1918 šaltinį, kurio nesitikėjo, ir konteineris baigia laiką, kol tunelis stovi ir yra sveikas. Užrašymas tun0 neparodo nieko, ir tai yra ženklas. Jei maršrutas tikrai rodo į tun0 , MASQUERADE perrašo šaltinį į 10.8.0.10 , ir tas pats konteineris veikia jums nieko neliečiant.
Paprašykite paieškos, kuri įvardija svečio šaltinį. Be from ir iif mazgas atsako už savo lizdą ir praneša kelią, kuriuo persiųstas paketas niekada neis:
ip route get 192.168.11.222 from 172.18.0.5 iif br-appnetJei tas atsakymas įvardija uplink'ą, tunelio nėra paveiksle ir jokia tunelio pusės nuostata nepadės.
Perkelkite svečią į paties tunelio adresą
Mažiausias veikiantis pakeitimas yra aiškus šaltinio perrašymas išėjime. Įterpkite jį, o ne prirašykite gale: Docker jau sėdi POSTROUTING priekyje, todėl taisyklė, pridėta su -A , vertinama po Docker taisyklės ir niekada nesuveikia.
iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10Ar tai išsilaikys, sprendžia dvi sąlygos. Adresas turi būti toks, kurį klientas tikrai turi, todėl prisekite jį įrašu kiekvienam klientui, kuriame yra ifconfig-push 10.8.0.10 255.255.255.0 ; taisyklė, įvardijanti niekada nepaskirtą adresą, sukelia tylų numetimą, o ne klaidą. Ir nuotolinė pusė turi priimti tą šaltinį ir nukreipti atsakymus atgal, todėl leidžiamų sąrašas ten įvardija tunelio adresą, o ne svečio potinklį. Kai kliento adresas dinaminis, -j MASQUERADE su -o tun0 seka tuo, ką turi sąsaja, tačiau už nuotolinės politikos, kuriai negalite rašyti fiksuotos reikšmės, kainą.
Filtravimas yra kita pusė. Docker nustato FORWARD politiką į drop ir kiekvieno demono paleidimo metu išvalo jo valdomas grandines, todėl vietinę politiką dėkite į DOCKER-USER , kuri išlieka:
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 ACCEPTGrąžinimo taisyklė svarbi: atsakymas, atvykstantis į tun0 tiltu atidarytam ryšiui, nėra priimamas vien todėl, kad išėjimo kryptis buvo.
Laikykite mazgą tiesioginį šaltinio taisykle
Kai visas tiltas turi naudoti tunelį, o niekas kitas mazge neturi, praleiskite NAT ir duokite tam šaltiniui savo lentelę. Svečio adresai tada lieka matomi nuo pradžios iki galo.
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 vpnboxTaisyklės tikrinamos prieš pagrindinę lentelę, ir from atitinka šaltinį. Paties tunelio išoriniai paketai neša fizinį mazgo šaltinį, todėl jie niekada neatitinka from 172.18.0.0/16 , ir tunelis negali nukreipti savęs atgal į save. Būtent šis savęs išskyrimas daro schemą stabilią.
Grąžinimo keliui dabar reikia pagalbos. Nuotolinė pusė mato neišverstą 172.18.0.x ir neturi priežasties žinoti, kur jis gyvena. Serveryje failui kiekvienam klientui reikia iroute 172.18.0.0 255.255.0.0 , kad serverio vidinė lentelė perduotų tą prefiksą šiam klientui, ir stumiamo route 172.18.0.0 255.255.0.0 , kad kiti klientai žinotų, jog jis egzistuoja.
Du patikrinimai prieš pasitikint. Paleiskite ip route show default : su redirect-gateway def1 lentelėje yra 0.0.0.0/1 ir 128.0.0.0/1 per tun0 , o mazgas visai ne tiesioginis, nes tie pusiau numatytieji laimi nepakeisdami default . Taisyklės, pridėtos su ip rule add , taip pat dingsta perkraunant, todėl neškite jas up scenarijuje.
Paketo dydis yra antra nesėkmė, kurią sutiksite
1500 baitų tilto sąsaja tuneliui duoda daugiau, nei jis gali panešti, kai pridedamos išorinės UDP ir IP antraštės. Maži mainai praeina, todėl DNS išsprendžiamas ir TLS rankos paspaudimas baigiamas, tada didesnis įkėlimas pakimba be jokios klaidos. Siuntėjas kartoja tą patį segmento dydį, nes ICMP pranešimas apie fragmentavimo poreikį, kuris pasakytų mažinti, niekada neatvyksta.
Pataisykite tai sąsajoje, MSS arba abiejuose. Suspaudimas nereikalauja svečio bendradarbiavimo:
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-pmtuSuspaudimas perrašo tik TCP. UDP, įskaitant QUIC, vietoj to reikia žemesnės svečio sąsajos, su --opt com.docker.network.driver.mtu=1400 kuriant tinklą arba atitinkamu compose raktu.
DNS ir patikrinimai, kuriuos verta kartoti
Vardų sprendimas eina tuo pačiu keliu. Įtaisytasis Docker resolveris 127.0.0.11 perduoda į sukonfigūruotus mazgo serverius, todėl konteineris paveldi tai, ką naudoja mazgas. Kai privatų zoną atsako tik per tunelį pasiekiamas resolveris, nukreipkite svečią į tą adresą, ir tikrinkite su getent hosts konteinerio viduje, o ne mazge.
- Paleiskite nepavykusią užklausą svečio viduje; mazgo pusės testas naudoja kitą šaltinio adresą ir paslepia problemą.
- Skaitykite pridėtų NAT ir filtro taisyklių skaitiklius. Taisyklė be vieno atitikimo yra maršrutizavimo klaida, o ne politikos.
- Patvirtinkite, kad jūsų SNAT taisyklės tunelio adresas yra tas, kurį klientas tikrai turi.
- Sumažinkite svečio MTU arba suspauskite MSS, prieš kaltindami serverį dėl sustojimo.
Eikite sąrašu ir sustokite ties pirmąja nesėkme. Būtent tą sluoksnį reikia taisyti; visa, kas pridedama žemiau, tik paslepia lūžtančią ribą.