Un programma dovrebbe usare la connessione normale mentre tutto il resto resta nel tunnel, e il client non ha alcun interruttore per questo. Non c’è nemmeno un menu nascosto da trovare. Una rotta si sceglie dai campi dentro il pacchetto, e il pacchetto porta indirizzi e porte invece dell’identità del processo che lo ha creato, quindi un’esclusione deve esprimersi in uno di quei campi. Seguite un pacchetto una volta e le opzioni disponibili smettono di essere misteriose.

Perché il client non può offrirlo

Il routing per destinazione confronta l’indirizzo di destinazione con i prefissi e conserva la corrispondenza più lunga. In quel momento ogni pacchetto appare uguale, qualunque cosa lo abbia prodotto. Il routing per policy di sorgente allarga il confronto all’indirizzo di sorgente, ed è l’uncino che usa ogni soluzione pratica: cambiate la sorgente e un’altra tabella diventa idonea.

Le porte non aiutano. Un’esclusione per porta andrebbe valutata prima della ricerca della destinazione, e la porta non ha alcun ruolo in quella decisione. Tutto ciò che viene pubblicizzato come esclusione basata sulla porta in realtà esclude per un altro campo e lo descrive in modo approssimativo.

Il vero routing per applicazione esiste dove la piattaforma ha aggiunto il meccanismo, come le chiamate di filtro su Windows o il binding del servizio VPN su Android. Un’istanza desktop di OpenVPN non ne porta nulla, e nessun flag di configurazione lo inventa.

Linux: marcate il processo, poi instradate il marchio

L’approccio più pulito lega la decisione al processo che possiede il socket. La corrispondenza owner funziona per il traffico generato localmente nella catena OUTPUT, quindi un marchio applicato lì può selezionare una tabella separata:

iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 0x1
ip rule add fwmark 0x1 table 200
ip route add 192.168.1.0/24 dev eth0 src 192.168.1.0 table 200
ip route add default via 192.168.1.1 dev eth0 table 200

I pacchetti dell’utente 1001 consultano poi la tabella 200 ed escono dal gateway ordinario, mentre la tabella principale continua a mandare tutto il resto nel tunnel. La corrispondenza owner vale solo per i pacchetti generati localmente, che è esattamente il traffico che vi interessa qui, e non può classificare i pacchetti inoltrati per un altro host. Verificate con gli strumenti e non con l’intenzione: ss -tnp mostra l’indirizzo locale e il processo proprietario delle connessioni stabilite, e ip route get 1.1.1.1 mark 0x1 mostra quale tabella seleziona il marchio.

Un namespace di rete è la risposta più solida per un programma autonomo, perché ogni socket creato al suo interno è legato ad esso per costruzione, così i processi ausiliari e le connessioni figlie ereditano il comportamento senza essere classificati affatto. Il costo è idraulico: una coppia veth, un indirizzo a ogni estremità e una rotta predefinita dentro il namespace che punta all’host.

ip netns add novpn
ip link add veth-host type veth peer name veth-ns
ip link set veth-ns netns novpn
ip addr add 10.200.0.1/24 dev veth-host
ip netns exec novpn ip addr add 10.200.0.2/24 dev veth-ns
ip netns exec novpn ip route add default via 10.200.0.1

Windows: la destinazione decide ancora

Windows sceglie l’interfaccia in uscita dalla destinazione, quindi un programma che vuole un altro percorso deve cambiare qualcosa che lo stack consulta prima di quella scelta. ForceBindIP copre la prima metà: avvia un programma con i socket già legati a un indirizzo locale che indicate. Questo da solo non cambia da quale interfaccia esce il pacchetto, perché la destinazione seleziona ancora la rotta. Legate una sorgente su un portatile la cui rotta predefinita punta nel tunnel e il pacchetto ci entra comunque.

Il sintomo ha una forma leggibile una volta che lo si conosce. Vale per un indirizzo LAN che l’interfaccia scelta raggiunge direttamente e fallisce per qualsiasi cosa su internet la cui rotta punti al tunnel. Sembra anche funzionare finché il programma non affida lavoro a un ausiliario, perché a un ausiliario che apre il proprio socket non è mai stato detto del binding.

Spostate la decisione quando un solo processo è l’unità sbagliata

Se il requisito reale è un dispositivo e non un programma, mettetelo sul suo SSID o VLAN e lasciate decidere il router: quella sorgente prende il percorso normale mentre le altre usano il tunnel. Una macchina gateway dedicata è l’altro progetto onesto: tiene la VPN e riceve solo le destinazioni che il router le manda. Su una VLAN la policy copre ogni flusso che il dispositivo apre, inclusi i processi ausiliari che hanno rotto l’approccio Windows. Lo scambio è la portata: due programmi su quel dispositivo non possono più essere trattati diversamente.

Entrambe sono policy di sorgente o di destinazione con una portata che potete dire in una frase. Nessuna è per applicazione, e un progetto che si vende come per applicazione fallisce la prima volta che il programma delega il suo lavoro a un secondo processo.

Articoli correlati