Rutarea traficului gazdei printr-un VPN într-o VM: șase verificări
Șase verificări ordonate pentru a transforma o VM în poarta VPN prin care gazda ei însăși rutează trafic: modul adaptorului, redirecționare, NAT, rutele gazdei, DNS, MTU.
Șase verificări, executate în ordine, decid dacă o VM poate servi drept poartă VPN pentru propria gazdă. Hipervizorul nu face nimic din asta pentru tine: oaspetele deține tunelul, gazda deține o rută, iar rețeaua dintre ele este rutare obișnuită. Păstrez lista pentru că sărirea unui pas produce mereu același simptom, tăcerea, când gazda face ping cu veselie la oaspete și nu ajunge la nimic intern.
Începe cu motivul. Fie clientul corporativ se instalează doar în oaspete, fie politica îl ține acolo, și tot ai nevoie de serverul de build intern din gazdă. Dacă gazda are voie la propriul client, fă asta în schimb: un singur tunel pe gazdă bate de fiecare dată două rețele plus un NAT.
Verificarea 1: gazda are nevoie de o legătură căreia să-i dea adresă
Gazda trebuie să aibă o interfață în același prefix ca al doilea adaptor al oaspetelui. Host-only în VirtualBox, host-only sau un VMnet personalizat în VMware Workstation, o punte internă sau un dispozitiv tap în QEMU/KVM și un comutator Internal în Hyper-V se califică toate. Oaspete 192.168.56.2, gazdă 192.168.56.1, prefix 192.168.56.0/24 este aranjamentul pe care îl folosesc în laborator.
Rețeaua QEMU în mod utilizator (-netdev user, backendul SLIRP implicit) și NAT-ul din VirtualBox eșuează complet această verificare. Pachetele oaspetelui ajung la un proces proxy pe gazdă, așa că gazda nu vede niciodată un dispozitiv care să poarte adresele oaspetelui și nu are spre ce să îndrepte o rută. Default Switch din Hyper-V este aceeași poveste. Dacă ping 192.168.56.2 din gazdă eșuează, oprește-te: nicio rută adăugată mai târziu nu repară asta.
Verificarea 2: oaspetele redirecționează și traduce
Pachetele de la gazdă sosesc cu o adresă sursă host-only și o destinație internă, deci oaspetele trebuie să le redirecționeze și apoi să le ascundă în spatele adresei tunelului său. Pe un oaspete Linux asta înseamnă redirecționare, o pereche FORWARD accept și source NAT spre tunel:
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -A FORWARD -i eth1 -o tun0 -j ACCEPT
sudo iptables -A FORWARD -i tun0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 192.168.56.0/24 -o tun0 -j MASQUERADE
Regula MASQUERADE este partea pe care oamenii o omit. Routerele corporative știu rareori că 192.168.56.0/24 există; cu regula, serverele interne văd adresa tunelului oaspetelui și răspund la ea, iar răspunsul își găsește drumul înapoi la oaspete.
Un oaspete Windows este mai neîndemânatic. IPEnableRouter=1 sub SYSTEM\CurrentControlSet\Services\Tcpip\Parameters activează redirecționarea fără traducere, așa că funcționează doar acolo unde rutarea corporativă indică deja înapoi spre prefixul tău host-only. Partajarea conexiunii la internet traduce, dar renumerotează adaptorul partajat la 192.168.137.1, așa că citește adresa pe care a luat-o efectiv înainte de a scrie rutele gazdei.
Verificarea 3: rutele gazdei numesc oaspetele, nu tunelul
Trimite prin oaspete doar prefixele de care are nevoie gazda. În Linux o rută pe prefix este suficientă și se întoarce curat:
sudo ip route add 10.20.0.0/16 via 192.168.56.2
ip route get 10.20.30.40
În Windows echivalentul este route add 10.20.0.0 mask 255.255.0.0 192.168.56.2. Lasă -p afară cât încă testezi, ca o rută proastă să moară la repornire, și adaugă-l când calea funcționează. Nu elimina ruta implicită a gazdei decât dacă fiecare aplicație de pe gazdă trebuie să folosească tunelul; dacă tunelul complet este într-adevăr cerința, adaugă cele două rute /1 și păstrează ruta directă către rețeaua hipervizorului:
sudo ip route add 0.0.0.0/1 via 192.168.56.2
sudo ip route add 128.0.0.0/1 via 192.168.56.2
Pe o gazdă Windows, verifică și metrica interfeței. O rută adăugată cât timp adaptorul host-only era dezactivat rămâne în tabelă dar nu transportă niciodată trafic, iar un al doilea adaptor cu metrică mai mică poate câștiga căutarea pentru același prefix.
Verificarea 4: DNS urmează ruta, nu implicitul tunelului
Rutele mută pachete; resolverul decide ce nume produc pachete. Limitează resolverul corporativ la zona corporativă în loc să înlocuiești resolverul gazdei, și îndreaptă-l spre un server DNS pe care noua rută îl atinge deja. Cu systemd-resolved pe gazdă:
resolvectl dns eth1 10.20.0.53
resolvectl domain eth1 '~corp.example.com'
În Windows o intrare în tabelul de politici de rezolvare a numelor face același lucru: Add-DnsClientNrptRule -Namespace ".corp.example.com" -NameServers 10.20.0.53. Dacă oaspetele rulează propriul resolver pe adresa host-only, folosește acea adresă, dar numai după ce confirmi că răspunde pe 192.168.56.2 și nu pe loopback.
Apoi verifică MTU înainte de a da vina pe DNS pentru pagini lente. Interfața tunelului este mai mică decât interfața de 1500 de octeți a gazdei, așa că o gazdă care trimite segmente de dimensiune maximă cu bitul DF setat le vede aruncate undeva în interiorul oaspetelui. Simptomul clasic este un ping care funcționează și o aplicație web internă care se blochează după strângerea de mână TLS. Strânge MSS pe calea de redirecționare a oaspetelui:
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
Verificarea 5: verifică de la ambele capete
Din gazdă, traceroute sau tracert către o adresă internă ar trebui să arate 192.168.56.2 ca prim salt, nu poarta fizică. În oaspete, urmărește aceeași încercare ieșind: tcpdump -ni any host 10.20.30.40 arată cererea redirecționată pe partea host-only și pe cea tradusă pe tunel. O cerere care iese, revine și lasă totuși gazda mută indică calea de întoarcere.
Două eșecuri explică cea mai mare parte din asta. Fie tunelul oaspetelui nu poartă rută de întoarcere pentru prefixul host-only pentru că lipsește regula NAT, fie ruta gazdei indică o adresă pe care oaspetele nu o mai are după o repornire. Recitește adresele cu ip -br addr și ip route get în loc să te bazezi pe notițele din sesiunea anterioară.
Verificarea 6: fă-o să supraviețuiască unei reporniri și unui update al hipervizorului
Fă persistente ambele jumătăți: configurația de rute netplan sau NetworkManager pe gazdă, route add ... -p în Windows și net.ipv4.ip_forward=1 în /etc/sysctl.d/ plus un set de reguli de firewall care se reîncarcă în oaspete. Fă un snapshot al oaspetelui imediat ce configurația este verificată, înainte să sosească următorul update al clientului corporativ.
Rețelele host-only sunt renumerotate când un hipervizor își recrează adaptoarele, ceea ce invalidează în tăcere fiecare rută și regulă NAT construită pe 192.168.56.0/24. După un update, rulează din nou primele trei verificări în ordine; durează un minut și prinde cazul în care gazda are încă ruta ei în timp ce oaspetele s-a mutat pe o subrețea nouă.
IPv6 are nevoie de propria trecere. Regulile ip6tables, înregistrările AAAA și o a doua familie de rute ale gazdei nu urmează configurația IPv4, iar un serviciu corporativ accesibil prin IPv6 va părea permanent stricat în timp ce IPv4 funcționează.
Trecut sau nu, în ordine: gazda poate face ping la adresa host-only a oaspetelui; oaspetele redirecționează și face MASQUERADE spre tunel; ip route get întoarce oaspetele ca poartă pentru prefixul intern; numele corporative se rezolvă prin resolverul limitat; traceroute arată oaspetele primul; și aceleași șase răspunsuri supraviețuiesc unei reporniri.