Cum un client OpenVPN din spatele NAT devine ieșirea către internet
Ieșirea apelează ea însăși spre exterior, așa că routerul ei nu are nevoie de niciun port de intrare. Urmărește ce rutează hubul, ce transmite mai departe ieșirea și de unde vine calea de întoarcere.
Trei gazde Linux, un singur proiect: un hub OpenVPN accesibil public, un laptop de consum și o mașină de ieșire în spatele unui router de casă obișnuit care nu transmite nimic spre interior. Ieșirea apelează ea însăși hubul. Apoi traficul de internet al consumatorului intră în tunel pe laptop și iese prin legătura proprie a ieșirii. Tot ce surprinde în această configurație decurge dintr-un singur detaliu: ieșirea a inițiat conexiunea, deci drumul spre interior există deja și nu e nevoie de redirecționare de port ca să fie creat.
Cele ce urmează urmăresc calea pachetului, nu ordinea configurației, pentru că configurația are sens doar când știi ce componentă deține ce decizie. Fiecare strat deține exact o decizie, iar greșelile se adună acolo unde două straturi sunt luate drept unul.
Două sesiuni de transport, o cale interioară
Fiecare gazdă își ține propria conexiune exterioară către hub, iar niciuna dintre aceste conexiuni nu știe de cealaltă. Traficul consumatorului către internet este un flux interior separat care se întâmplă să fie purtat în tunelul consumatorului și apoi reemis din tunelul ieșirii. Acest flux interior este ceea ce hubul trebuie să ruteze și ceea ce ieșirea trebuie să transmită.
outer: consumer --TLS--> hub
outer: exit --TLS--> hub
inner: consumer --tun--> hub --internal--> exit --tun--> uplink
Deoarece ambele conexiuni exterioare pleacă din partea clientului, routerul ieșirii nu vede niciodată un SYN de intrare și nu are niciodată nevoie de o regulă NAT de destinație. Trebuie doar să mențină în viață transportul stabilit, ceea ce face deja pentru orice conexiune de durată. Nimic din internetul public nu trebuie să ajungă mai întâi la ieșire, de aceea o adresă dinamică pe acea legătură nu este o problemă. Routerul tot trebuie să permită sesiunea de ieșire, dar aceasta este aceeași permisiune pe care un browser o folosește deja.
Ce instalează de fapt iroute în interiorul serverului
Hubul este singurul dispozitiv care decide ce peer deține ce destinație, și ia acea decizie în propriul tabel de rutare din spațiul utilizator, nu în cel al nucleului. O intrare client-config-dir pentru ieșire numită exit-node poate revendica acolo prefixul implicit:
# /etc/openvpn/server/ccd/exit-node
iroute 0.0.0.0 0.0.0.0
Acea linie îi spune procesului server „pachetele interioare cu orice destinație aparțin acestui client”. Nu adaugă o rută implicită în sistemul de operare al hubului și nu transformă hubul într-o poartă de internet. Referința OpenVPN 2.6 ține rutarea internă specifică clientului și rutarea nucleului ca mecanisme separate, iar această separare este motivul pentru care proiectul funcționează fără să atingă ruta implicită a hubului.
Consumatorul are nevoie de instrucțiunea oglindă, livrată ca push, ca propriul trafic să intre în tunel în loc să iasă prin poarta locală. Cu topologia de subrețea, pool-ul este o rută conectată pe dispozitivul tun al ieșirii, deci direcția de întoarcere nu are nevoie de push suplimentar; ceea ce ieșirea nu trebuie să primească este redirecționarea de mai jos, care ar trimite traficul ei de ieșire înapoi în tunelul pe care încearcă să îl folosească. Trimite-o doar consumatorului și citește rutele aplicate pe client în loc să te bazezi că fișierul CCD a fost ales.
# /etc/openvpn/server/ccd/consumer-01
push "redirect-gateway def1"
Nucleul ieșirii face transmiterea
În interiorul ieșirii, un pachet de la consumator ajunge pe interfața tun adresat unui host de internet. Nucleul își consultă tabelul de rutare, găsește legătura reală ca cea mai bună potrivire și aplică apoi două politici independente: dacă pachetul poate fi transmis deloc și dacă poate ieși pe acea interfață cu acea adresă sursă. Transmiterea este un sysctl și este dezactivată implicit în multe distribuții.
Așadar ieșirea are nevoie de transmiterea IPv4 activată permanent, de o regulă care admite sursele tunelului consumatorului spre legătură și de calea răspunsului înapoi prin tun. Activarea transmiterii pentru întreaga gazdă schimbă și comportamentul oricui altceva care este rutat prin ea, de aceea regula ar trebui să numească sursele și interfețele în loc să accepte tot.
Calea de întoarcere este cumpărată de traducerea sursei
Un host de internet nu are rută către o adresă 10.90.0.x, așa că răspunsurile ar fi eliminate înainte de a ajunge la ieșire. Rescrierea sursei când pachetul părăsește legătura rezolvă asta, iar urmărirea conexiunilor inversează rescrierea la întoarcere.
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
Regula de mai sus este un punct de plecare pentru o singură interfață de legătură, nu o politică de firewall. Restrânge sursa la alocările pe care le-ai autorizat cu adevărat și ține politica de intrare a ieșirii separată, ca mascarea traficului de tunel să nu expună în tăcere servicii locale. Pe o gazdă cu adresă de legătură dinamică, mascarea te scutește și de urmărirea adresei manual.
Unde hubul poate impune și unde nu
Cu client-to-client și calea tradițională de date din spațiul utilizator, hubul predă pachetele interioare între peer-i în interiorul procesului serverului. Ele nu traversează niciodată lanțul FORWARD al nucleului hubului, așa că o regulă de nucleu acolo nu poate restricționa tranzitul între peer-i, oricât de atent ar fi scrisă. Impunerea trebuie să trăiască în politica serverului însuși: cine are voie să se conecteze, ce fișier CCD primește și dacă proprietatea implicită a ieșirii este acordată.
De aceea un iroute implicit este o decizie largă de încredere. Ieșirea trebuie să accepte în mod legitim trafic de întoarcere de la adrese de internet arbitrare și trebuie să fie o mașină pe care o controlezi, o actualizezi și o monitorizezi. A acorda aceeași proprietate unui peer căruia nu i-ai încredința un router este greșeala la care invită acest proiect.
Apartenența este controlul real. Orice client care își poate configura propriile rute poate încerca să folosească ieșirea, așa că restricționarea cine se poate conecta, sau căror peer-i va servi ieșirea, face parte din proiect și nu este un gând ulterior.
Eșec sigur când ieșirea dispare
Oprește ieșirea și proprietatea implicită interioară dispare odată cu sesiunea ei. Ce nu trebuie să se întâmple este ca hubul să devină în tăcere ieșirea de rezervă: nicio rută implicită din pool-ul VPN către legătura hubului și nicio regulă largă de mascare pe hub care ar transmite bucuros același trafic. Testează acel eșec intenționat, apoi testează recuperarea cu un flux nou și nu cu o pagină care era deja deschisă.
# 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
Dacă traficul consumatorului ajunge totuși la internet după ce ieșirea a dispărut, altceva îl transportă și granița pe care ai intenționat-o nu există. Tratează DNS și IPv6 tot ca decizii separate: o rută IPv4 plus mascare nu spun nimic despre traficul AAAA, iar un resolver împins de hub va răspunde de unde iese de fapt interogarea.