Маршрутизиране на трафика на хоста през VPN във VM: шест проверки
Шест подредени проверки, за да превърнете VM във VPN шлюз, през който собственият ѝ хост маршрутизира трафика си: режим на адаптера, препращане, NAT, маршрути на хоста, DNS, MTU.
Шест проверки, изпълнени по ред, решават дали една VM може да служи като VPN шлюз за собствения си хост. Хипервизорът не прави нищо от това вместо вас: гостът притежава тунела, хостът притежава маршрут, а мрежата между тях е обикновено маршрутизиране. Държа списъка, защото пропускането на една стъпка винаги дава един и същ симптом, тишина, при който хостът весело пингва госта и не стига до нищо вътрешно.
Започнете с причината. Или корпоративният клиент се инсталира само вътре в госта, или политиката го държи там, а вие все още ви трябва вътрешният build сървър от хоста. Ако на хоста е разрешен собствен клиент, направете това вместо другото: един тунел на хоста винаги е по-добър от две мрежи плюс NAT.
Проверка 1: хостът се нуждае от връзка, на която може да даде адрес
Хостът трябва да притежава интерфейс в същия префикс, в който е вторият адаптер на госта. Host-only във VirtualBox, host-only или персонализиран VMnet във VMware Workstation, вътрешен бридж или tap устройство в QEMU/KVM и Internal switch в Hyper-V — всички стават. Гост 192.168.56.2, хост 192.168.56.1, префикс 192.168.56.0/24 е схемата, която използвам в лаборатории.
Мрежата на QEMU в потребителски режим (-netdev user, подразбиращият се SLIRP backend) и NAT на VirtualBox се провалят на тази проверка напълно. Пакетите на госта стигат до прокси процес на хоста, така че хостът никога не вижда устройство с адресите на госта и няма към какво да насочи маршрут. Default Switch на Hyper-V е същата история. Ако ping 192.168.56.2 от хоста се провали, спрете: никакъв маршрут, добавен по-късно, няма да го поправи.
Проверка 2: гостът препраща и преобразува
Пакетите от хоста пристигат с адрес на източника от host-only мрежата и вътрешна дестинация, така че гостът трябва да ги препрати и после да ги скрие зад адреса на своя тунел. На Linux гост това означава препращане, двойка правила FORWARD accept и source NAT към тунела:
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
Правилото MASQUERADE е частта, която хората пропускат. Корпоративните рутери рядко знаят, че 192.168.56.0/24 съществува; с правилото вътрешните сървъри виждат адреса на тунела на госта и отговарят на него, а отговорът намира пътя обратно до госта.
Windows гост е по-непохватен. IPEnableRouter=1 под SYSTEM\CurrentControlSet\Services\Tcpip\Parameters включва препращане без преобразуване, така че работи само там, където корпоративното маршрутизиране вече сочи обратно към вашия host-only префикс. Internet Connection Sharing преобразува, но преномерира споделения адаптер на 192.168.137.1, затова прочетете адреса, който той наистина е заел, преди да пишете маршрути на хоста.
Проверка 3: маршрутите на хоста сочат госта, не тунела
Пускайте през госта само префиксите, от които хостът се нуждае. На Linux един маршрут на префикс е достатъчен и се връща чисто:
sudo ip route add 10.20.0.0/16 via 192.168.56.2
ip route get 10.20.30.40
На Windows еквивалентът е route add 10.20.0.0 mask 255.255.0.0 192.168.56.2. Оставете -p, докато още тествате, за да умира лош маршрут при рестарт, и го добавете, щом пътят проработи. Не премахвайте маршрута по подразбиране на хоста, освен ако всяко приложение на хоста не трябва да минава през тунела; ако наистина е нужен пълен тунел, добавете двата маршрута /1 и запазете директния маршрут към мрежата на хипервизора:
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
На Windows хост проверете и метриката на интерфейса. Маршрут, добавен, докато host-only адаптерът е бил изключен, остава в таблицата, но никога не носи трафик, а втори адаптер с по-ниска метрика може да спечели търсенето за същия префикс.
Проверка 4: DNS следва маршрута, не подразбирането на тунела
Маршрутите движат пакети; резолверът решава кои имена създават пакети. Ограничете корпоративния резолвер до корпоративната зона, вместо да заменяте резолвера на хоста, и го насочете към DNS сървър, до който новият маршрут вече стига. С systemd-resolved на хоста:
resolvectl dns eth1 10.20.0.53
resolvectl domain eth1 '~corp.example.com'
На Windows запис в таблицата на политиката за разрешаване на имена върши същата работа: Add-DnsClientNrptRule -Namespace ".corp.example.com" -NameServers 10.20.0.53. Ако гостът пуска собствен резолвер на host-only адреса, използвайте този адрес, но само след като потвърдите, че отговаря на 192.168.56.2, а не на loopback.
После проверете MTU, преди да обвинявате DNS за бавни страници. Интерфейсът на тунела е по-малък от 1500-байтовия интерфейс на хоста, така че хост, който изпраща сегменти в пълен размер с вдигнат DF бит, ги губи някъде вътре в госта. Класическият симптом е работещ ping и вътрешно уеб приложение, което засяда след TLS ръкостискането. Стегнете MSS по пътя на препращане в госта:
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
Проверка 5: проверете от двата края
От хоста traceroute или tracert към вътрешен адрес трябва да покаже 192.168.56.2 като първи хоп, а не физическия шлюз. В госта наблюдавайте как същият опит излиза: tcpdump -ni any host 10.20.30.40 показва препратената заявка от страната на host-only мрежата и преобразуваната на тунела. Заявка, която излиза, връща се и все пак оставя хоста безмълвен, сочи към обратния път.
Две грешки обясняват по-голямата част от това. Или тунелът на госта не носи обратен маршрут за host-only префикса, защото липсва NAT правилото, или маршрутът на хоста сочи адрес, който гостът вече не държи след рестарт. Прочетете адресите отново с ip -br addr и ip route get, вместо да вярвате на бележки от предишната сесия.
Проверка 6: направете го устойчиво на рестарт и на ъпгрейд на хипервизора
Запазете и двете половини: конфигурация на маршрути с netplan или NetworkManager на хоста, route add ... -p на Windows и net.ipv4.ip_forward=1 в /etc/sysctl.d/ плюс набор правила за защитна стена, който се презарежда в госта. Направете snapshot на госта, щом конфигурацията се потвърди, преди да пристигне следващата актуализация на корпоративния клиент.
Host-only мрежите се преномерират, когато хипервизорът пресъздаде адаптерите си, което тихо обезсилва всеки маршрут и NAT правило, изградени върху 192.168.56.0/24. След ъпгрейд повторете първите три проверки по ред; това отнема минута и хваща случая, в който хостът още има своя маршрут, а гостът е преминал в нова подмрежа.
IPv6 изисква отделен пас. Правилата на ip6tables, AAAA записите и второто семейство маршрути на хоста не следват настройката на IPv4, а корпоративна услуга, достъпна по IPv6, ще изглежда трайно счупена, докато IPv4 работи.
Успех или провал, по ред: хостът може да пингне host-only адреса на госта; гостът препраща и прави MASQUERADE към тунела; ip route get връща госта като шлюз за вътрешния префикс; корпоративните имена се разрешават през ограничения резолвер; traceroute показва госта пръв; и същите шест отговора оцеляват след рестарт.