Sei controlli, eseguiti in ordine, decidono se una VM può fare da gateway VPN per il proprio host. L'hypervisor non fa nulla di tutto ciò per te: l'ospite possiede il tunnel, l'host possiede una rotta, e la rete fra loro è instradamento ordinario. Tengo la lista perché saltare un passaggio produce sempre lo stesso sintomo, il silenzio, dove l'host fa ping all'ospite allegramente e non raggiunge nulla di interno.

Parti dal motivo. O il client aziendale si installa solo dentro l'ospite, o la policy lo tiene lì, e hai comunque bisogno del server di build interno dall'host. Se l'host può avere il proprio client, fai quello invece: un solo tunnel sull'host batte ogni volta due reti più un NAT.

Controllo 1: l'host ha bisogno di un collegamento a cui dare un indirizzo

L'host deve possedere un'interfaccia nello stesso prefisso del secondo adattatore dell'ospite. Host-only in VirtualBox, host-only o una VMnet personalizzata in VMware Workstation, un bridge interno o un dispositivo tap in QEMU/KVM e uno switch Internal in Hyper-V vanno tutti bene. Ospite 192.168.56.2, host 192.168.56.1, prefisso 192.168.56.0/24 è la disposizione che uso in laboratorio.

La rete QEMU in modalità utente (-netdev user, il backend SLIRP predefinito) e il NAT di VirtualBox falliscono questo controllo del tutto. I pacchetti dell'ospite arrivano a un processo proxy sull'host, così l'host non vede mai un dispositivo che porti gli indirizzi dell'ospite e non ha nulla su cui puntare una rotta. Il Default Switch di Hyper-V è la stessa storia. Se ping 192.168.56.2 dall'host fallisce, fermati: nessuna rotta aggiunta dopo lo riparerà.

Controllo 2: l'ospite inoltra e traduce

I pacchetti dall'host arrivano con un indirizzo sorgente host-only e una destinazione interna, quindi l'ospite deve inoltrarli e poi nasconderli dietro l'indirizzo del suo tunnel. Su un ospite Linux questo significa inoltro, una coppia FORWARD accept e source NAT verso il tunnel:

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

La regola MASQUERADE è la parte che le persone omettono. I router aziendali raramente sanno che 192.168.56.0/24 esiste; con la regola, i server interni vedono l'indirizzo del tunnel dell'ospite e vi rispondono, e la risposta trova la via del ritorno verso l'ospite.

Un ospite Windows è più goffo. IPEnableRouter=1 sotto SYSTEM\CurrentControlSet\Services\Tcpip\Parameters attiva l'inoltro senza traduzione, quindi funziona solo dove l'instradamento aziendale punta già al tuo prefisso host-only. La condivisione della connessione a Internet traduce, ma rinumera l'adattatore condiviso su 192.168.137.1, quindi leggi l'indirizzo che ha effettivamente preso prima di scrivere le rotte dell'host.

Controllo 3: le rotte dell'host nominano l'ospite, non il tunnel

Manda attraverso l'ospite solo i prefissi che servono all'host. Su Linux una rotta per prefisso basta e si ritira in modo pulito:

sudo ip route add 10.20.0.0/16 via 192.168.56.2
ip route get 10.20.30.40

Su Windows l'equivalente è route add 10.20.0.0 mask 255.255.0.0 192.168.56.2. Lascia fuori -p finché stai ancora provando, così una rotta sbagliata muore al riavvio, e aggiungilo quando il percorso funziona. Non rimuovere la rotta predefinita dell'host a meno che ogni applicazione dell'host non debba usare il tunnel; se il tunnel completo è davvero il requisito, aggiungi le due rotte /1 e mantieni la rotta diretta verso la rete dell'hypervisor:

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

Su un host Windows controlla anche la metrica dell'interfaccia. Una rotta aggiunta mentre l'adattatore host-only era disattivato resta nella tabella ma non porta mai traffico, e un secondo adattatore con metrica più bassa può vincere la ricerca per lo stesso prefisso.

Controllo 4: il DNS segue la rotta, non il default del tunnel

Le rotte spostano pacchetti; il resolver decide quali nomi producono pacchetti. Limita il resolver aziendale alla zona aziendale invece di sostituire il resolver dell'host, e puntalo a un server DNS che la nuova rotta già raggiunge. Con systemd-resolved sull'host:

resolvectl dns eth1 10.20.0.53
resolvectl domain eth1 '~corp.example.com'

Su Windows una voce nella tabella dei criteri di risoluzione dei nomi fa lo stesso lavoro: Add-DnsClientNrptRule -Namespace ".corp.example.com" -NameServers 10.20.0.53. Se l'ospite esegue il proprio resolver sull'indirizzo host-only, usa quell'indirizzo invece, ma solo dopo aver confermato che risponde su 192.168.56.2 e non su loopback.

Poi controlla l'MTU prima di dare la colpa al DNS per le pagine lente. L'interfaccia del tunnel è più piccola dell'interfaccia da 1500 byte dell'host, quindi un host che invia segmenti a dimensione piena con il bit DF attivo li vede cadere da qualche parte dentro l'ospite. Il sintomo classico è un ping che funziona e un'applicazione web interna che si blocca dopo la stretta di mano TLS. Stringi l'MSS sul percorso di inoltro dell'ospite:

sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Controllo 5: verifica da entrambe le estremità

Dall'host, traceroute o tracert verso un indirizzo interno dovrebbe mostrare 192.168.56.2 come primo salto, non il gateway fisico. Nell'ospite, osserva lo stesso tentativo uscire: tcpdump -ni any host 10.20.30.40 mostra la richiesta inoltrata sul lato host-only e quella tradotta sul tunnel. Una richiesta che esce, torna e lascia comunque l'host muto punta al percorso di ritorno.

Due guasti spiegano la maggior parte di questo. O il tunnel dell'ospite non porta alcuna rotta di ritorno per il prefisso host-only perché manca la regola NAT, o la rotta dell'host punta a un indirizzo che l'ospite non ha più dopo un riavvio. Rileggi gli indirizzi con ip -br addr e ip route get invece di fidarti delle note della sessione precedente.

Controllo 6: fallo sopravvivere a un riavvio e a un aggiornamento dell'hypervisor

Rendi persistenti entrambe le metà: configurazione delle rotte con netplan o NetworkManager sull'host, route add ... -p su Windows, e net.ipv4.ip_forward=1 in /etc/sysctl.d/ più un insieme di regole del firewall che si ricarica nell'ospite. Fai uno snapshot dell'ospite appena la configurazione è verificata, prima che arrivi il prossimo aggiornamento del client aziendale.

Le reti host-only vengono rinumerate quando un hypervisor ricrea i suoi adattatori, il che invalida in silenzio ogni rotta e regola NAT costruita su 192.168.56.0/24. Dopo un aggiornamento, riesegui i primi tre controlli in ordine; costa un minuto e intercetta il caso in cui l'host ha ancora la sua rotta mentre l'ospite è passato a una nuova sottorete.

IPv6 richiede il proprio passaggio. Le regole ip6tables, i record AAAA e una seconda famiglia di rotte dell'host non seguono la configurazione IPv4, e un servizio aziendale raggiungibile via IPv6 sembrerà permanentemente guasto mentre IPv4 funziona.

Superato o no, in ordine: l'host può fare ping all'indirizzo host-only dell'ospite; l'ospite inoltra e fa MASQUERADE verso il tunnel; ip route get restituisce l'ospite come gateway per il prefisso interno; i nomi aziendali si risolvono tramite il resolver limitato; traceroute mostra l'ospite per primo; e le stesse sei risposte sopravvivono a un riavvio.

Articoli correlati