Le immagini dei container possono contenere tutti i binari e i file di configurazione OpenVPN di cui hanno bisogno e fallire comunque alla prima chiamata di rete. L’errore nomina /dev/net/tun perché l’applicazione ha chiesto al kernel un’interfaccia TUN e il container non aveva né il nodo del dispositivo né la capacità di crearne uno. Sono due permessi separati, e correggerne uno lascia l’altro esattamente dov’era.

Separarli costa un comando ciascuno. Il messaggio di errore successivo dice a quale confine si trovava davvero.

Il nodo del dispositivo e la capacità sono due porte

Il nodo del dispositivo è un oggetto del filesystem, quindi un container deve vederlo nel proprio spazio dei nomi di mount. La capacità è un permesso, quindi il processo ha bisogno di CAP_NET_ADMIN nello spazio dei nomi in cui verrà creata l’interfaccia. Passare il dispositivo senza la capacità dà un nodo che si apre e un ioctl che fallisce. Concedere la capacità senza il dispositivo lascia l’applicazione incapace di aprire qualsiasi cosa, ed è per questo che i due di solito compaiono insieme negli esempi.

L’impostazione devices di Docker mappa il dispositivo dell’host nel container, mentre cap_add regola l’insieme delle capacità. Un bind mount dello stesso percorso mette il file in vista senza applicare la semantica di accesso al dispositivo.

Riproduca il problema fuori dal file Compose

Prima di modificare il servizio, verifichi che l’host abbia il dispositivo e che un container nudo possa creare un’interfaccia. Il comando qui sotto è volutamente minimo, e il suo unico scopo è separare un problema dell’host da un problema di compose. Se fallisce, nessuna modifica a compose aiuterà.

ls -l /dev/net/tun
docker run --rm --cap-add NET_ADMIN --device /dev/net/tun:/dev/net/tun alpine sh -c 'apk add -q iproute2 && ip tuntap add dev tun9 mode tun && ip link show tun9'

Un nodo assente sull’host significa che il modulo tun non è disponibile, il che è una correzione lato host e non lato container. Un nodo che esiste ma non si apre nel container indica un accesso al dispositivo, e il messaggio dirà permission denied invece di no such file. L’ordine di questi due fallimenti è tutta la diagnosi. Annoti quale ha visto prima di cambiare un flag.

Gli errori dopo TUN sono problemi diversi

Appena l’interfaccia compare, il fallimento successivo ha un altro proprietario. Gli errori di autenticazione significano che il tunnel ha raggiunto il server e le credenziali sono state rifiutate, quindi i cambiamenti di capacità non aiuteranno. Un tunnel che si stabilisce mentre le applicazioni continuano a fallire punta a rotte, inoltro, risoluzione DNS nel container o alla disposizione di rete del container stesso. Ognuna di queste è una domanda di rete ordinaria a cui il permesso TUN non può rispondere. Ripetere il test originale dopo ogni modifica evita che i fallimenti si fondano in un’unica impressione.

Copiare ogni impostazione da un esempio funzionante è il modo in cui una correzione minima diventa una definizione di servizio illeggibile. Porti solo quelle che questa distribuzione richiede e annoti perché ciascuna è lì.

Verifichi l’interfaccia dentro il container

Controlli l’interfaccia e l’insieme delle capacità dall’interno del container in esecuzione anziché dall’host. I comandi qui sotto mostrano il nodo del dispositivo, l’elenco delle capacità e se un’interfaccia possa essere creata. Li esegua nello stesso container che esegue il client, e installi gli strumenti se l’immagine è minima.

ls -l /dev/net/tun
capsh --print | grep -i net_admin
ip link add dev tun9 type tun && ip link show tun9

Una capacità presente nel bounding set ma assente nell’effective set si comporta diversamente da una mai concessa, e capsh mostra entrambe. Se il nodo esiste e la capacità c’è mentre la creazione fallisce ancora, il runtime sta imponendo qualcos’altro, come un profilo seccomp o una politica AppArmor che limita l’operazione. È un terzo confine, documentato dal runtime anziché dall’applicazione.

Annoti i flag del container, l’immagine e l’errore esatto di ogni tentativo. La prossima persona che leggerà quella nota potrà dire quale porta era chiusa senza ripetere l’intera indagine.

Articoli correlati