Due nomi DNS, una porta OpenVPN: cosa seleziona davvero
Due nomi host che puntano a un solo indirizzo arrivano comunque a un solo listener. Solo un campo che il client invia può scegliere un backend, e l'OpenVPN nativo non ne invia nessuno.
Due record A, un indirizzo, un demone: qualunque nome digiti l'utente, il pacchetto che arriva è identico, e il processo legato alla porta 1194 non ha nulla per distinguere i due. Non è una configurazione errata in attesa di riparazione; è il limite di ciò che una risoluzione di nome può esprimere. Qualunque cosa scelga tra due istanze OpenVPN deve essere un valore che il client mette nella connessione e che il frontend può rileggere, e un client OpenVPN standard non ne mette nessuno in chiaro. Quello che segue è dove questo lascia, e quali disposizioni separano davvero il traffico.
Parti da ciò che il resolver restituisce
Un client chiede vpn-a.example e riceve un indirizzo; quell'indirizzo più una porta è tutto ciò che compone. Il nome viene consumato localmente, durante la risoluzione, e non diventa mai un campo sul filo, quindi il listener all'altro capo non può vedere quale etichetta sia stata usata. Punta vpn-a.example e vpn-b.example allo stesso host e entrambe le risoluzioni danno la stessa destinazione, ed è per questo che il demone assorbe entrambe le connessioni in una sola istanza senza registrare differenze. I profili basati sull'indirizzo si comportano esattamente come quelli basati sul nome, per la stessa ragione.
Questo chiude anche una famiglia di aggiramenti tentatori: alias aggiuntivi, scelta di un certificato per nome su un solo socket, o una regola del firewall legata al nome interrogato. Nessuno di essi può agire su informazioni già scartate prima che il primo pacchetto lasciasse il client. Se due server devono restare separati, la separazione deve viaggiare con la connessione stessa, cioè un indirizzo, una porta o un campo di payload che il protocollo definisce.
L'unico selettore che conta viaggia dentro il pacchetto
HTTP lo gestisce con un'intestazione Host, e TLS con un'estensione del nome del server nel ClientHello, ed è così che un solo indirizzo serve molti siti dietro un solo listener. Entrambi funzionano perché il protocollo sul filo trasporta una richiesta per un servizio specifico. Il fatto rilevante su OpenVPN è che nessuno dei due campi è presente al livello che una scatola intermedia ispeziona.
In una disposizione standard l'handshake TLS che protegge il canale di controllo avviene dopo che la sessione esterna è stabilita e viene trasportato al suo interno, quindi un listener che non ha terminato quella sessione vede un flusso opaco. Può ancora dire che si tratta di OpenVPN, perché l'opcode e il framing nei primi byte sono riconoscibili, ma il nome del server, se esiste, sta nella parte cifrata. Un progetto che si aspetta di leggere un host virtuale dal pacchetto esterno sta leggendo un campo che il protocollo non scrive mai lì. Questo deriva da come è costruito il tunnel, non da una lacuna che una corrispondenza di pattern più scaltra potrebbe chiudere.
Dove un separatore di protocolli si ferma
Un multiplexer del tipo che documenta il progetto sslh ascolta su una porta, ispeziona i primi byte e consegna la connessione al backend che parla il protocollo riconosciuto. È un valore reale quando una sola porta deve servire insieme SSH, HTTPS e OpenVPN.
sslh mantiene una sonda separata per protocollo, e la sua sonda TLS può estrarre un nome di server o un valore ALPN da un ClientHello autentico. Riconoscere l'OpenVPN nativo è una classificazione diversa dal leggere un'estensione TLS, e le due non si compongono, perché un client che non invia mai un ClientHello al socket in ascolto lascia la sonda senza nulla da leggere. Perciò una regola che corrisponde a OpenVPN risponde «questo è un client OpenVPN» e non va oltre. Due backend OpenVPN dietro quella regola sono indistinguibili, il frontend non ha alcuna base per scegliere, e ogni sessione atterra nello stesso punto. Il multiplexing condivide una porta tra servizi diversi; non fa virtual hosting dentro un servizio.
Disposizioni che separano davvero due server
La separazione onesta più economica è un secondo indirizzo pubblico, uno per istanza, e i profili esistenti portano già il necessario perché puntano a indirizzi diversi. La successiva è una porta diversa sullo stesso indirizzo, che non costa nulla oltre a un numero in ogni profilo. TCP e UDP tengono tabelle delle porte separate, quindi 1194/tcp e 1194/udp sono socket non correlati e uno di ciascuno può coesistere su un solo host; questa è separazione di trasporto e non instradamento per nome, e spesso viene confusa con esso. Entrambe le opzioni sopravvivono a una revisione del firewall, perché la regola corrisponde a qualcosa che il pacchetto contiene davvero.
Se il confine che ti serve corre tra gruppi di utenti invece che tra due implementazioni, un'istanza con configurazioni client distinte, la propria gestione dei certificati e una politica di accesso esplicita può coprirlo. Quello che si compra è politica dentro un server, non isolamento tra server, e sono requisiti diversi. L'isolamento che deve reggere sotto compromissione ha ancora bisogno dell'indirizzo o del bordo del firewall.
Un terminatore TLS posto davanti a OpenVPN può esporre un nome server reale a un frontend capace, ma solo quando anche il lato client è configurato per quel livello aggiuntivo. I profili esistenti non acquisiscono il comportamento perché vengono puntati allo stesso indirizzo.
Dimostra il selettore prima di costruire
Nomina il campo che identificherà un backend, poi conferma che il client lo invia davvero; se la risposta è un indirizzo, il piano regge, e se è un nome server dentro un handshake che non raggiunge mai il listener, nessuna quantità di lavoro DNS lo salverà. Tre comandi risolvono in fretta il lato in ascolto.
ss -lntup | grep -E ':443|:1194'
openssl s_client -connect vpn-a.example:443 -servername vpn-a.example </dev/null | head -n 20
journalctl -u openvpn-server@server-a -n 20 --no-pager
La riga openssl dà un certificato solo dove qualcosa a quell'indirizzo termina TLS; contro un listener OpenVPN nativo si blocca o fallisce, e questo è di per sé la risposta su cosa espone lo strato esterno. La riga journal, letta su ogni backend, mostra quale istanza ha accettato la sessione. I log del frontend annotano ciò che è stato classificato e mai ciò che era intenzionale, quindi il backend resta l'unica autorità su dove è atterrato un utente.
Chiudi con due profili pensati per raggiungere server diversi, collegali uno alla volta e annota il log del backend per ciascuno. Se le due sessioni compaiono nello stesso journal, il selettore manca invece di essere mal configurato, e la correzione è un indirizzo o una porta che il client può raggiungere. Modificare la regola di instradamento non aiuterà, perché alla regola non è mai stato dato nulla su cui instradare. Una volta che la separazione è reale, scrivila accanto ai profili perché la persona successiva non aggiunga un secondo alias aspettandosi che faccia il lavoro.