Kiel direkti nur la trafikon de Docker-ujoj tra OpenVPN
Tenadu la gastiganton sur ĝia kutima uplink kaj metu unu ujan subreton en la tunelon: ordo de NAT, fontaj reguloj, iroute de OpenVPN kaj MSS-kontroloj.
La paketo de la ujo neniam atingas tun0 kaj pliaj kapabloj sur la ujo tion ne ŝanĝos. Inter la gasto kaj la tunelo staras du itineraj serĉoj: unu ene de la reta nomspaco de la gasto, unu sur la gastiganto post kiam la kadro surteriĝas sur la ponto, kaj la dua konsultas alian tabelon kun aliaj kongruaj reguloj. Mia referenca aranĝo estas Linux-gastiganto kun OpenVPN kliento, kiu tenas 10.8.0.10 sur tun0, Docker-ponto sur 172.18.0.0/16, kaj privata adreso atingebla nur de la tunela flanko. La celo restas mallarĝa: tiu gastuĝa subreto eliras tra la tunelo dum la gastiganto konservas sian kutiman uplink.
Kuri la VPN-klienton ene de la ujo estas alia dezajno, ne fuĝvojo. Tiam la ujo posedas sian tunelon kaj siajn akreditaĵojn, kaj la laboro transiras al Dockerfile anstataŭ resti en iptables.
Plusendado elektas itineron laŭ celo, poste NAT reskribas la fonton
Kun net.ipv4.ip_forward=1 agordita, plusendita paketo estas enkursigita nur laŭ celo, en la ĉefa tabelo. La fonto ludas neniun rolon en tiu decido. Nur post kiam la itinero elektiĝis, la kerno ruligas POSTROUTING, kaj ĝuste tie sidas la propra regulo de Docker: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Ĉio, kio forlasas la ponton al alia interfaco, estas tradukata, kaj la adreso, kiun ĝi fariĝas, dependas de la interfaco, al kiun la itinero sendis ĝin.
Tiu regulo klarigas la plej multajn simptomojn, pri kiuj oni demandas min. Se la ĉefa tabelo ne havas itineron por la privata celo, la paketo eliras tra la uplink portante la fizikan adreson de la gastiganto. Via provizanto forĵetas la celon, aŭ la fora retejo forĵetas fonton RFC 1918, kiun ĝi neniam atendis, kaj la ujo elĉerpiĝas je tempo dum la tunelo restas supre kaj sana. Kaptaĵo sur tun0 montras nenion, kaj tio estas la signo. Se la itinero ja montras al tun0, MASQUERADE reskribas la fonton al 10.8.0.10 kaj la sama ujo funkcias sen ke vi tuŝu ion.
Petu la serĉon, kiu nomas la fonton de la gasto. Sen from kaj iif la gastiganto respondas por sia propra soketo kaj raportas vojon, kiun la plusendita paketo neniam prenos:
ip route get 192.168.11.222 from 172.18.0.5 iif br-appnetSe tiu respondo nomas la uplink, la tunelo ne estas en la bildo kaj neniu agordo ĉe la tunela flanko helpos.
Translokigu la gaston al la propra adreso de la tunelo
La plej malgranda funkcianta ŝanĝo estas eksplicita fonta reskribo ĉe la eliro. Enmetu ĝin anstataŭ aldoni ĝin: Docker jam sidas ĉe la kapo de POSTROUTING, do regulo aldonita per -A estas taksata malantaŭ la Docker-a kaj neniam ekfunkcias.
iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10Du kondiĉoj decidas, ĉu tio teniĝos. La adreso devas esti tiu, kiun la kliento vere tenas, do fiksu ĝin per rikordo por ĉiu kliento enhavanta ifconfig-push 10.8.0.10 255.255.255.0; regulo nomanta adreson, kiu neniam estis atribuita, produktas silentajn forĵetojn, ne eraron. Kaj la fora flanko devas akcepti tiun fonton kaj enkursigi respondojn reen, tial permeslisto tie nomas la tunelan adreson anstataŭ la gastan subreton. Kiam la klienta adreso estas dinamika, -j MASQUERADE kun -o tun0 sekvas tion, kion la interfaco tenas, je la kosto de fora politiko, kontraŭ kiu vi ne povas skribi fiksan valoron.
Filtrado estas la alia duono. Docker agordas la politikon FORWARD al drop kaj malplenigas la ĉenojn, kiujn ĝi posedas, ĉe ĉiu demona restarto, do metu lokan politikon en DOCKER-USER, kiu postvivas:
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 ACCEPTLa reena regulo gravas: respondo alvenanta sur tun0 por ponto-malfermita konekto ne estas akceptata nur ĉar la elira direkto estis.
Tenu la gastiganton rekta per fonta regulo
Kiam la tuta ponto uzu la tunelon sed nenio alia sur la gastiganto, preterlasu NAT kaj donu al tiu fonto sian propran tabelon. Gastaj adresoj tiam restas videblaj de fino al fino.
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 vpnboxReguloj estas konsultataj antaŭ la ĉefa tabelo, kaj from kongruas kun la fonto. La propraj eksteraj paketoj de la tunelo portas la fizikan fonton de la gastiganto, do ili neniam kongruas kun from 172.18.0.0/16 kaj la tunelo ne povas enkursigi sin reen en sin. Ĝuste tiu sin-ekskludo igas la aranĝon stabila.
La reena vojo nun bezonas helpon. La fora flanko vidas netradukitan 172.18.0.x kaj havas neniun kialon scii, kie ĝi loĝas. Sur la servilo, dosiero por ĉiu kliento bezonas iroute 172.18.0.0 255.255.0.0, por ke la interna tabelo de la servilo transdonu tiun prefikson al ĉi tiu kliento, kaj puŝitan route 172.18.0.0 255.255.0.0, por ke aliaj klientoj sciu, ke ĝi ekzistas.
Du kontroloj antaŭ fidi ĝin. Rulu ip route show default: kun redirect-gateway def1 la tabelo tenas 0.0.0.0/1 kaj 128.0.0.0/1 tra tun0, kaj la gastiganto tute ne estas rekta, ĉar tiuj duon-defaŭltoj venkas sen anstataŭigi default. Reguloj aldonitaj per ip rule add ankaŭ malaperas ĉe restarto, do portu ilin en up-skripto.
Paketa grandeco estas la dua fiasko, kiun vi renkontas
Ponta interfaco je 1500 bajtoj donas al la tunelo pli, ol ĝi povas porti, kiam aldoniĝas la eksteraj UDP- kaj IP-kapoj. Malgrandaj interŝanĝoj pasas, do DNS solviĝas kaj la TLS-manpremo kompletiĝas, poste pli granda alŝuto haltas sen ia ajn eraro. La sendanto daŭre ripetas la saman segmentan grandecon, ĉar la ICMP-mesaĝo pri bezono de fragmentado, kiu dirus al ĝi malgrandiĝi, neniam alvenas.
Riparu ĝin ĉe la interfaco, ĉe la MSS, aŭ ambaŭ. Krampado bezonas neniun kunlaboron de la gasto:
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-pmtuLa krampado reskribas nur TCP. UDP, inkluzive QUIC, bezonas anstataŭe malaltigitan gastan interfacon, per --opt com.docker.network.driver.mtu=1400 ĉe reta kreado aŭ la kongrua compose-ŝlosilo.
DNS kaj la kontroloj indaj je ripetado
Noma solvado sekvas la saman vojon. La enkonstruita solvilo de Docker ĉe 127.0.0.11 plusendas al la agorditaj serviloj de la gastiganto, do ujo heredas tion, kion la gastiganto uzas. Kiam la privatan zonon respondas nur solvilo atingebla tra la tunelo, direktu la gaston al tiu adreso, kaj testu per getent hosts ene de la ujo anstataŭ sur la gastiganto.
- Rulu la malsukcesan peton ene de la gasto; testo ĉe la gastiganta flanko uzas alian fontan adreson kaj kaŝas la problemon.
- Legu la nombrilojn sur la NAT- kaj filtraj reguloj, kiujn vi aldonis. Regulo kun nulo da kongruoj estas itinerada eraro, ne politika eraro.
- Konfirmu, ke la tunela adreso en via SNAT-regulo estas tiu, kiun la kliento vere tenas.
- Malaltigu la MTU de la gasto aŭ krampu MSS antaŭ ol kulpigi la servilon pri halto.
Trairu tiun liston kaj haltu ĉe la unua fiasko. Ĝuste tiu tavolo estas riparinda; io ajn aldonita pli malsupre nur kaŝas la limon, kiu rompiĝas.