Routing ruchu hosta przez VPN w VM: sześć kontroli
Sześć kontroli po kolei, aby zamienić VM w bramę VPN, przez którą routuje ruch jej własny host: tryb adaptera, przekazywanie, NAT, trasy hosta, DNS, MTU.
Sześć kontroli wykonanych po kolei decyduje, czy VM może być bramą VPN dla własnego hosta. Hiperwizor nie robi za ciebie nic z tego: tunel należy do gościa, trasa do hosta, a sieć między nimi to zwykły routing. Trzymam tę listę, bo pominięcie jednego kroku zawsze daje ten sam objaw, ciszę, gdy host wesoło pinguje gościa i nie dociera do niczego wewnętrznego.
Zacznij od powodu. Albo klient korporacyjny instaluje się tylko w gościu, albo polityka trzyma go właśnie tam, a ty nadal potrzebujesz wewnętrznego serwera build z hosta. Jeśli host może mieć własnego klienta, zrób to zamiast tego: jeden tunel na hoście bije za każdym razem dwie sieci plus NAT.
Kontrola 1: host potrzebuje łącza, któremu może nadać adres
Host musi mieć interfejs w tym samym prefiksie co drugi adapter gościa. Host-only w VirtualBox, host-only lub własny VMnet w VMware Workstation, wewnętrzny most lub urządzenie tap w QEMU/KVM i przełącznik Internal w Hyper-V — wszystkie się kwalifikują. Gość 192.168.56.2, host 192.168.56.1, prefiks 192.168.56.0/24 to układ, którego używam w laboratoriach.
Sieć QEMU w trybie użytkownika (-netdev user, domyślny backend SLIRP) i NAT VirtualBox oblewają tę kontrolę całkowicie. Pakiety gościa docierają do procesu proxy na hoście, więc host nigdy nie widzi urządzenia noszącego adresy gościa i nie ma na co skierować trasy. Default Switch w Hyper-V to ta sama historia. Jeśli ping 192.168.56.2 z hosta nie przechodzi, zatrzymaj się: żadna dodana później trasa tego nie naprawi.
Kontrola 2: gość przekazuje i tłumaczy
Pakiety z hosta przychodzą z adresem źródłowym z sieci host-only i wewnętrznym celem, więc gość musi je przekazać, a potem ukryć za adresem swojego tunelu. Na gościu Linux oznacza to przekazywanie, parę FORWARD accept i source NAT w stronę tunelu:
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
Reguła MASQUERADE to ta część, którą ludzie pomijają. Routery korporacyjne rzadko wiedzą, że 192.168.56.0/24 istnieje; z regułą wewnętrzne serwery widzą adres tunelu gościa i odpowiadają na niego, a odpowiedź znajduje drogę powrotną do gościa.
Gość Windows jest niezgrabniejszy. IPEnableRouter=1 pod SYSTEM\CurrentControlSet\Services\Tcpip\Parameters włącza przekazywanie bez tłumaczenia, więc działa tylko tam, gdzie routing korporacyjny już wskazuje z powrotem na twój prefiks host-only. Internet Connection Sharing tłumaczy, ale przenumerowuje współdzielony adapter na 192.168.137.1, więc odczytaj adres, który faktycznie przyjął, zanim napiszesz trasy hosta.
Kontrola 3: trasy hosta nazywają gościa, nie tunel
Przepuszczaj przez gościa tylko te prefiksy, których potrzebuje host. W Linuxie jedna trasa na prefiks wystarcza i wraca czysto:
sudo ip route add 10.20.0.0/16 via 192.168.56.2
ip route get 10.20.30.40
W Windows odpowiednikiem jest route add 10.20.0.0 mask 255.255.0.0 192.168.56.2. Zostaw -p na czas testów, żeby zła trasa umierała przy restarcie, i dodaj go, gdy ścieżka zadziała. Nie usuwaj trasy domyślnej hosta, chyba że każda aplikacja na hoście ma korzystać z tunelu; jeśli pełny tunel jest naprawdę wymaganiem, dodaj dwie trasy /1 i zachowaj trasę wprost do sieci hiperwizora:
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
Na hoście Windows sprawdź też metrykę interfejsu. Trasa dodana, gdy adapter host-only był wyłączony, zostaje w tabeli, ale nigdy nie niesie ruchu, a drugi adapter z niższą metryką może wygrać wyszukiwanie tego samego prefiksu.
Kontrola 4: DNS idzie za trasą, nie za domyślnym tunelem
Trasy przemieszczają pakiety; resolver decyduje, które nazwy tworzą pakiety. Ogranicz resolver korporacyjny do strefy korporacyjnej zamiast zastępować resolver hosta, i wskaż go na serwer DNS, do którego nowa trasa już dociera. Z systemd-resolved na hoście:
resolvectl dns eth1 10.20.0.53
resolvectl domain eth1 '~corp.example.com'
W Windows wpis w tabeli zasad rozwiązywania nazw robi to samo: Add-DnsClientNrptRule -Namespace ".corp.example.com" -NameServers 10.20.0.53. Jeśli gość uruchamia własny resolver na adresie host-only, użyj tego adresu, ale dopiero po potwierdzeniu, że odpowiada na 192.168.56.2, a nie na loopback.
Potem sprawdź MTU, zanim obwinisz DNS za wolne strony. Interfejs tunelu jest mniejszy niż interfejs 1500 bajtów hosta, więc host wysyłający segmenty pełnej wielkości z ustawionym bitem DF widzi, jak giną gdzieś wewnątrz gościa. Klasyczny objaw to działający ping i wewnętrzna aplikacja webowa, która zastyga po uścisku dłoni TLS. Ściśnij MSS na ścieżce przekazywania gościa:
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
Kontrola 5: zweryfikuj z obu stron
Z hosta traceroute lub tracert do adresu wewnętrznego powinno pokazać 192.168.56.2 jako pierwszy skok, a nie fizyczną bramę. W gościu obserwuj, jak ta sama próba wychodzi: tcpdump -ni any host 10.20.30.40 pokazuje przekazane żądanie po stronie host-only i przetłumaczone na tunelu. Żądanie, które wychodzi, wraca i nadal zostawia hosta milczącym, wskazuje na drogę powrotną.
Dwa błędy wyjaśniają większość tego. Albo tunel gościa nie niesie trasy powrotnej dla prefiksu host-only, bo brakuje reguły NAT, albo trasa hosta wskazuje adres, którego gość już nie ma po restarcie. Przeczytaj adresy ponownie za pomocą ip -br addr i ip route get zamiast ufać notatkom z poprzedniej sesji.
Kontrola 6: przetrwaj restart i aktualizację hiperwizora
Utrwal obie połowy: konfigurację tras netplan lub NetworkManager na hoście, route add ... -p w Windows oraz net.ipv4.ip_forward=1 w /etc/sysctl.d/ plus zestaw reguł zapory, który przeładowuje się w gościu. Zrób snapshot gościa, gdy tylko konfiguracja się potwierdzi, przed następną aktualizacją klienta korporacyjnego.
Sieci host-only są przenumerowywane, gdy hiperwizor odtwarza swoje adaptery, co po cichu unieważnia każdą trasę i regułę NAT zbudowaną na 192.168.56.0/24. Po aktualizacji powtórz pierwsze trzy kontrole po kolei; to zajmuje minutę i łapie przypadek, gdy host ma jeszcze swoją trasę, a gość przeniósł się do nowej podsieci.
IPv6 wymaga własnego przebiegu. Reguły ip6tables, rekordy AAAA i druga rodzina tras hosta nie idą za konfiguracją IPv4, a usługa korporacyjna dostępna przez IPv6 będzie wyglądać na trwale zepsutą, gdy IPv4 działa.
Zaliczone czy nie, po kolei: host może pingować adres host-only gościa; gość przekazuje i robi MASQUERADE w stronę tunelu; ip route get zwraca gościa jako bramę dla prefiksu wewnętrznego; nazwy korporacyjne rozwiązują się przez ograniczony resolver; traceroute pokazuje gościa jako pierwszy; i te same sześć odpowiedzi przetrwa restart.