Immaginate un host Linux che termina una VPN e allo stesso tempo esegue un container su una rete macvlan. La LAN è 192.168.60.0/24, il container detiene 192.168.60.210 e i client VPN arrivano da 10.95.0.0/24. Il client raggiunge il servizio, la risposta non torna mai e ogni regola del firewall sull’host sembra corretta. Due vincoli separati producono quel sintomo, e correggerne uno lascia l’altro al suo posto.

Due vincoli, un sintomo

Il primo vincolo è l’isolamento: il driver macvlan di Docker non consente a un container di parlare con il proprio host attraverso l’interfaccia padre. Il traffico tra lo stack dell’host e il container non ha alcun percorso, per quanto le rotte sull’host siano scritte bene.

Il secondo vincolo è il percorso di ritorno. Il gateway predefinito del container è di solito il router della LAN, quindi una risposta indirizzata a un client VPN esce da quel router. Se il router non ha una rotta di ritorno al pool VPN, la risposta viene scartata da qualche parte nel mezzo.

I due guasti sembrano identici dal punto di vista del client: una richiesta che arriva e una risposta che non arriva mai. Richiedono riparazioni diverse, e una regola masquerade ampia può nascondere quale dei due è ancora rotto. Per questo il primo test è una cattura sul lato container e non un’altra modifica al firewall. Se la richiesta non compare mai lì, è il vincolo di isolamento quello in gioco.

Costruite lo shim lato host, in modo stretto

Una seconda interfaccia macvlan sull’host, collegata allo stesso padre in modalità bridge, dà allo stack dell’host un’identità propria su quel segmento della LAN. Riservatele un indirizzo fuori dal pool DHCP e fuori dall’intervallo del container, e instradate solo il container attraverso di essa.

ip link add macvlan-vpn link eth0 type macvlan mode bridge
ip address add 192.168.60.250/32 dev macvlan-vpn
ip link set macvlan-vpn up
ip route add 192.168.60.210/32 dev macvlan-vpn src 192.168.60.250

Il primo comando crea l’interfaccia in modalità bridge, che è il valore predefinito documentato per questo driver e la modalità che si vuole per il traffico tra host e container. Il secondo assegna un /32 perché l’indirizzo non entri in collisione con il prefisso della LAN, e il quarto invia solo l’indirizzo del container attraverso lo shim.

Non reindirizzate l’intera LAN attraverso la nuova interfaccia. La rotta connessa ordinaria resta com’è, e la rotta dell’host è volutamente stretta perché un container guadagni un percorso senza che il resto del segmento cambi comportamento. Una rotta predefinita verso lo shim sarebbe un altro disegno con altre conseguenze. Mantenetela specifica e il raggio d’impatto resta piccolo.

Questi comandi cambiano la configurazione di rete su un host in funzione. Su un appliance o una piattaforma gestita, usate il meccanismo di configurazione supportato da quella piattaforma, perché una modifica interattiva in una shell root potrebbe non sopravvivere a un aggiornamento.

Decidete come si preserva l’origine

Con lo shim al suo posto, il container può raggiungere l’host, e la domanda diventa quale indirizzo di origine porta la risposta. Preservare l’origine VPN originale è il modello più pulito: il container vede l’indirizzo reale del client, e i log e le regole di accesso del servizio possono usarlo.

Perché funzioni, la configurazione di rete del container deve inviare per questo percorso il traffico di 10.95.0.0/24 a 192.168.60.250 come proprio gateway. È un’impostazione di rotta o di gateway supportata dentro la configurazione del container stesso, non un trucco lato host. Confermatela dall’interno del container invece di supporre che l’impostazione abbia avuto effetto. Un gateway predefinito che punta ancora al router della LAN continuerà a mandare le risposte nello stesso buco.

Se il disegno approvato nasconde invece le origini VPN, limitate la traduzione in modo stretto: il pool VPN reale, lo shim come interfaccia di uscita e il container e il servizio specifici.

iptables -t nat -A POSTROUTING -s 10.95.0.0/24 -o macvlan-vpn -j SNAT --to-source 192.168.60.250

Fate corrispondere lo shim come interfaccia di uscita, non il tunnel. Una regola che nomina il tunnel come interfaccia di uscita non corrisponderà a un pacchetto diretto verso il container, ed è l’errore che questo tipo di configurazione di solito contiene. Mantenete la regola stretta come la frase sopra e riesaminatela quando il pool cambia.

Verificate dal client, non dall’host

Una richiesta dall’host stesso, legata all’indirizzo dello shim, dimostra che lo shim può raggiungere il container. Non dimostra che il container possa restituire traffico a un’origine preservata 10.95.0.x, perché l’indirizzo di origine in quel test è quello dello shim stesso.

curl -sS --interface 192.168.60.250 -o /dev/null -w '%{http_code}\n' http://192.168.60.210:8080/health
sudo tcpdump -ni macvlan-vpn host 192.168.60.210

Il primo comando prova il tratto dall’host al container, e il secondo mostra cosa attraversa davvero lo shim mentre un vero client VPN fa la richiesta. Guardate l’indirizzo di origine in ingresso e se compare una risposta con lo stesso flusso.

L’inoltro deve essere abilitato sull’host, e il percorso di inoltro ha bisogno di un permesso per il pool VPN verso lo shim sulla porta del servizio, oltre alle risposte stabilite. Una regola INPUT sull’host non autorizza il traffico inoltrato, ed è una causa frequente per cui il pacchetto viene scartato dopo che l’instradamento sembra corretto.

Eseguite l’applicazione reale con credenziali reali, poi verificate che un secondo servizio non correlato sullo stesso container vi rifiuti ancora. Se l’unico modo per farlo funzionare era aprire ogni regola di inoltro, il disegno non è finito. Annotate anche quale indirizzo di origine il servizio ha registrato, perché è quello che le sue regole di accesso useranno d’ora in poi.

Articoli correlati