I client di due server OpenVPN possono raggiungersi a vicenda?
Due server OpenVPN creano due pool di client, non un’unica rete. Instradate tra loro con sottoreti distinte, rotte reciproche e un confine di fiducia esplicito.
Mettere in piedi un secondo OpenVPN server vi dà un secondo posto a cui i client possono connettersi, e niente di più. Non mette quei client sulla rete del primo server, e l’opzione chiamata client-to-client cambia solo l’inoltro all’interno di un singolo processo server. I due pool diventano una rete quando instradate tra loro di proposito, con un responsabile per ogni direzione. Tutto quanto segue presuppone la modalità TUN nello spazio utente con quell’inoltro interno disattivato, così che sia il kernel a decidere.
Il mito: un elenco di account, una rete
Autenticazione e raggiungibilità si confondono perché entrambe vivono nella configurazione del server. Un utente che può accedere a entrambi i gateway appare, dall’esterno, come un utente di un unico sistema con due porte. All’interno, ogni server distribuisce indirizzi dal proprio pool e installa rotte solo per i client che termina. Così un client sul gateway A che invia un pacchetto a un client sul gateway B chiede una destinazione di cui il suo stesso server non ha mai sentito parlare. Il pacchetto va alla rotta predefinita di A, lascia il tunnel e arriva a un firewall a cui non è mai stato chiesto di consentirlo.
Aggiungere un secondo server quindi non aggiunge ridondanza, scoperta o un dominio di broadcast condiviso. Aggiunge una rete instradata che richiede un piano, e il piano inizia dall’indirizzamento.
Date a ogni gateway il proprio pool e un indirizzo di transito
Due intervalli che non possono sovrapporsi a nient’altro, più un piccolo collegamento di transito tra gli host, sono l’intero piano degli indirizzi. Tenete il transito su un segmento privato che controllate, e scrivete i numeri prima che qualsiasi server assegni un lease.
| Gateway | Client pool | Transit address |
|---|---|---|
| gateway-a | 10.64.10.0/24 | 172.28.44.1/30 |
| gateway-b | 10.64.20.0/24 | 172.28.44.2/30 |
Resistete alla tentazione di dare a entrambi i server lo stesso pool perché i profili restino identici. Una rotta del kernel non può dire quale di due host possiede al momento un indirizzo arbitrario dentro un intervallo condiviso, quindi la tabella di routing punterebbe a un gateway e il client potrebbe essere collegato all’altro. I nomi delle interfacce sono convenzioni locali, non stato condiviso. Entrambi gli host qui chiamano il dispositivo di tunnel tun0 e il collegamento di transito transit0, il che va bene perché nessun pacchetto porta quei nomi.
Installate la rotta su entrambi i lati
Ogni gateway ha bisogno di una rotta verso il pool dell’altro attraverso l’indirizzo di transito, e l’inoltro deve essere attivo perché uno dei due possa fare da salto. Confermate prima la tabella attuale, poi aggiungete la voce e rendetela persistente tramite il gestore di rete dell’host anziché un hook di riconnessione.
sysctl net.ipv4.ip_forward
ip route add 10.64.20.0/24 via 172.28.44.2 dev transit0
ip route get 10.64.20.37
Ripetete la cosa sull’altro host con i pool invertiti. Aggiungere ripetutamente la stessa rotta da uno script non correlato è il modo in cui un host finisce con diverse copie e un cambiamento misterioso la volta successiva che qualcuno legge la tabella. Se il collegamento di transito è esso stesso un tunnel, la rotta punta alla topologia di quel tunnel e il salto deve sopravvivere alla sua riconnessione. Un indirizzo di transito che sparisce per trenta secondi porta con sé il percorso tra i pool, il che è una proprietà di progetto che vale la pena dichiarare anziché scoprire.
Spingete la rotta del client senza fingere la proprietà
I client hanno bisogno di una rotta verso il pool remoto, altrimenti il loro sistema operativo invia il pacchetto al gateway predefinito e il tunnel non lo vede mai. La configurazione del server è il posto naturale per consegnarla.
push "route 10.64.20.0 255.255.255.0"
Mettete la riga specchio nella configurazione dell’altro server, poi riconnettete un client di prova e leggete la sua tabella. Un client che ignora le rotte spinte ha bisogno della propria configurazione approvata, e una rotta che non arriva mai non è un controllo degli accessi. Non date a un client un iroute per l’intero pool dell’altro gateway. Quella direttiva esiste per una sottorete dietro un client connesso, e usarla per coprire una rotta mancante tra gateway produce un server che crede di possedere indirizzi che non può raggiungere.
Dimostrate una richiesta e rifiutatene un’altra
Scegliete un’operazione reale, per esempio HTTPS da un client su 10.64.10.21 verso un servizio su 10.64.20.37, e consentite esattamente questo su entrambi gli host. Ogni firewall accetta la richiesta sul lato in ingresso e la risposta tracciata sul lato di ritorno; la policy di servizio della destinazione deve comunque accettarla.
iptables -A FORWARD -i tun0 -o transit0 -s 10.64.10.21 -d 10.64.20.37 -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -i transit0 -o tun0 -d 10.64.10.21 -s 10.64.20.37 -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT
Con rotte reciproche, il NAT di origine è inutile e qui attivamente dannoso: tradurre l’origine nasconde quale client ha fatto la richiesta e vanifica la policy basata sull’origine lato destinazione. Verificate che una regola di masquerade esistente per l’uscita internet non intercetti anche questo percorso. Poi provate il rifiuto. Un servizio che deve restare locale al pool B deve restare irraggiungibile dal pool A, e anche le coppie di client sullo stesso gateway vanno provate, dato che la policy di inoltro appena modificata vale anche per loro.
Un’altra correzione al modello mentale: il collegamento di transito trasporta pacchetti interni decifrati, quindi è un confine di fiducia e non una garanzia privata. Se attraversa una rete che non controllate, proteggete quel salto con un tunnel cifrato approvato e instradate attraverso la sua topologia reale. TLS end-to-end autentica comunque l’applicazione per conto suo, ed è per questo che il servizio interno dovrebbe continuare a usarlo.