Trys Linux hostai, viena konstrukcija: viešai pasiekiamas OpenVPN mazgas, vartotojo nešiojamasis kompiuteris ir išėjimo mašina už įprasto namų maršrutizatoriaus, kuris nieko neperduoda į vidų. Išėjimas pats prisijungia prie mazgo. Tada vartotojo interneto srautas įeina į tunelį nešiojamajame kompiuteryje ir išeina per paties išėjimo aukštyn krypties jungtį. Viskas, kas šioje schemoje stebina, kyla iš vienos detalės: ryšį inicijavo išėjimas, todėl kelias į vidų jau egzistuoja ir jam sukurti nereikia prievado peradresavimo.

Toliau sekamas paketo kelias, o ne konfigūracijos tvarka, nes konfigūracija turi prasmę tik tada, kai žinoma, kuris komponentas priima kurį sprendimą. Kiekvienas sluoksnis priima lygiai vieną sprendimą, o klaidos telkiasi ten, kur du sluoksniai laikomi vienu.

Dvi transporto sesijos, vienas vidinis kelias

Kiekvienas hostas palaiko savo išorinį ryšį su mazgu, ir nė vienas iš šių ryšių nežino apie kitą. Vartotojo srautas į internetą yra atskiras vidinis srautas, kuris atsitiktinai pernešamas vartotojo tunelyje ir po to vėl išleidžiamas iš išėjimo tunelio. Būtent šį vidinį srautą mazgas turi maršrutizuoti, o išėjimas jį turi perduoti.

outer: consumer --TLS--> hub
outer: exit     --TLS--> hub
inner: consumer --tun--> hub --internal--> exit --tun--> uplink

Kadangi abu išoriniai ryšiai išeina iš kliento pusės, išėjimo maršrutizatorius niekada nemato įeinančio SYN ir niekada neprireikia paskirties NAT taisyklės. Jam tereikia palaikyti nusistovėjusį transportą gyvą, ką jis jau daro bet kuriam ilgalaikiam ryšiui. Niekas viešajame internete neturi pirmiau pasiekti išėjimo, todėl dinaminis adresas toje aukštyn jungtyje nėra problema. Maršrutizatorius vis tiek turi leisti išeinamąją sesiją, bet tai ta pati prieiga, kurią naršyklė jau naudoja.

Ką iroute iš tikrųjų įdiegia serverio viduje

Mazgas yra vienintelis įrenginys, sprendžiantis, kuriam partneriui priklauso kuris tikslas, ir tą sprendimą priima savo vartotojo erdvės maršrutizavimo lentelėje, o ne branduolio. Išėjimui skirta client-config-dir eilutė pavadinimu exit-node gali ten reikalauti numatytojo prefikso:

# /etc/openvpn/server/ccd/exit-node
iroute 0.0.0.0 0.0.0.0

Ši eilutė pasako serverio procesui "vidiniai paketai su bet kokiu tikslu priklauso šiam klientui". Ji neprideda numatytojo maršruto prie mazgo operacinės sistemos ir nepaverčia mazgo interneto tarpine. OpenVPN 2.6 vadovas laiko klientui būdingą vidinį maršrutizavimą ir branduolio maršrutizavimą atskirais mechanizmais, ir būtent šis atskyrimas yra priežastis, kodėl konstrukcija veikia neliečiant mazgo numatytojo maršruto.

Vartotojui reikia veidrodinės instrukcijos, pristatomos kaip push, kad jo srautas patektų į tunelį, o ne išeitų per vietinę tarpinę. Naudojant potinklio topologiją, telkinys yra prijungtas maršrutas išėjimo tun įrenginyje, todėl grįžimo krypčiai papildomo push nereikia; ko išėjimas neturi gauti, yra žemiau esantis peradresavimas, kuris nusiųstų jo paties aukštyn srautą atgal į tunelį, kurį jis bando naudoti. Siųskite jį tik vartotojui ir skaitykite pritaikytus maršrutus kliente, o ne pasitikėkite, kad CCD failas buvo parinktas.

# /etc/openvpn/server/ccd/consumer-01
push "redirect-gateway def1"

Perdavimą atlieka išėjimo branduolys

Išėjimo viduje paketas iš vartotojo atvyksta į tun sąsają, adresuotas kokiam nors interneto hostui. Branduolys pasižiūri savo maršrutizavimo lentelę, randa tikrąją aukštyn jungtį kaip geriausią atitikimą ir tada taiko dvi nepriklausomas politikas: ar šį paketą apskritai galima perduoti, ir ar jis gali išeiti per tą sąsają su tuo šaltinio adresu. Perdavimas yra sysctl, ir daugelyje distribucijų jis išjungtas pagal nutylėjimą.

Taigi išėjimui reikia nuolat įjungto IPv4 perdavimo, taisyklės, kuri įleidžia vartotojo tunelio šaltinius link aukštyn jungties, ir atsakymo kelio atgal per tun. Perdavimo įjungimas visam hostui keičia ir viso kito, kas maršrutizuojama per jį, elgseną, todėl taisyklė turėtų įvardyti šaltinius ir sąsajas, o ne priimti viską.

Grįžimo kelią nusiperka šaltinio vertimas

Interneto hostas neturi maršruto į 10.90.0.x adresą, todėl atsakymai būtų išmesti dar nepasiekę išėjimo. Šaltinio perrašymas paketui paliekant aukštyn jungtį tai išsprendžia, o ryšių sekimas grįžtant perrašymą atsuka atgal.

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.90.0.0/24 -o enp1s0 -j MASQUERADE
ip -4 route get 10.90.0.9   # run on the exit: must point at the tun

Aukščiau pateikta taisyklė yra vienos aukštyn sąsajos atspirties taškas, o ne ugniasienės politika. Susiaurinkite šaltinį iki tų priskyrimų, kuriuos tikrai įgaliojote, ir laikykite atskirą paties išėjimo įvesties politiką, kad tunelio srauto maskavimas tyliai neatskleistų vietinių paslaugų. Hoste su dinaminiu aukštyn adresu maskavimas taip pat sutaupo rankinį adreso sekimą.

Kur mazgas gali užtikrinti, o kur ne

Su client-to-client ir tradiciniu vartotojo erdvės duomenų keliu mazgas perduoda vidinius paketus tarp partnerių serverio proceso viduje. Jie niekada neperžengia mazgo branduolio FORWARD grandinės, todėl branduolio taisyklė ten negali apriboti tranzito tarp partnerių, kad ir kaip kruopščiai ji būtų parašyta. Užtikrinimas turi gyventi pačioje serverio politikoje: kam leidžiama jungtis, kokį CCD failą jis gauna ir ar apskritai suteikiama numatytoji išėjimo nuosavybė.

Todėl numatytasis iroute yra platus pasitikėjimo sprendimas. Išėjimas turi teisėtai priimti grįžtamąjį srautą iš savavališkų interneto adresų, ir jis turi būti mašina, kurią kontroliuojate, atnaujinate ir stebite. Suteikti tą pačią nuosavybę partneriui, kuriam nepatikėtumėte maršrutizatoriaus yra klaida, į kurią ši konstrukcija kviečia.

Narystė yra tikroji kontrolė. Bet kuris klientas, galintis susikonfigūruoti savo maršrutus, gali bandyti naudoti išėjimą, todėl apribojimas, kas gali jungtis ar kuriems partneriams išėjimas tarnaus, yra konstrukcijos dalis, o ne vėlesnė mintis.

Saugiai nepavyksta, kai išėjimas dingsta

Išjunkite išėjimą ir vidinė numatytoji nuosavybė dingsta kartu su jo sesija. Netaip neturi atsitikti, kad mazgas tyliai taptų pakaitiniu išėjimu: jokio numatytojo maršruto iš VPN telkinio į mazgo aukštyn jungtį ir jokios plačios maskavimo taisyklės mazge, kuri mielai perduotų tą patį srautą. Išbandykite šią nesėkmę tyčia, o tada atkūrimą išbandykite nauju srautu, o ne jau atidarytu puslapiu.

# on the consumer, with the exit up and then down
ip -4 route get 1.1.1.1
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com

Jei vartotojo srautas pasiekia internetą ir po to, kai išėjimas dingo, jį neša kažkas kitas ir numatytos ribos nėra. Su DNS ir IPv6 taip pat elkitės kaip su atskirais sprendimais: IPv4 maršrutas su maskavimu nieko nesako apie AAAA srautą, o mazgo įstumtas resolveris atsakys iš ten, iš kur užklausa iš tikrųjų išeina.

Susiję straipsniai