OpenVPN non trova ca.crt anche se il profilo si carica
Il percorso del profilo non è la base dei file che contiene. Vedere perché un --config assoluto fallisce ancora su ca.crt e come l’avvio decide la risposta.
Il servizio parte, legge il profilo senza lamentarsi e poi muore su una riga che sembra non c’entrare: non riesce ad aprire ca.crt. Nulla del certificato è cambiato, e il percorso del profilo nell’unità è assoluto. Proprio quella combinazione è l’indizio. Il percorso passato individua un file, e i nomi dentro quel file vengono risolti altrove del tutto, quindi un avvio che sembra pienamente qualificato può ancora leggere le sue chiavi da una directory che nessuno intendeva.
L’errore ha poche cause, e vale la pena separarle in ordine: la directory di base che il processo aveva davvero, il contesto di avvio del servizio e i permessi o la sandbox attorno al file una volta che il percorso è corretto. Ognuna lascia una traccia diversa: nell’unità, nel processo e sul filesystem.
Il percorso del profilo non è una directory di base
Immaginate un processo che parte in /tmp e riceve /etc/vpn/prod/tenant-a.ovpn. Dentro quel profilo, la riga ca ca.crt nomina un percorso relativo. Nulla nell’argomento che ha individuato il profilo riscrive quel nome, quindi il processo cerca /tmp/ca.crt e segnala un file mancante.
La parte confusa è l’ordine degli errori. OpenVPN analizza prima la configurazione e apre i file referenziati man mano che li raggiunge, quindi un errore di battitura nel profilo viene segnalato prima che si tocchi qualsiasi chiave, mentre un certificato mancante compare solo quando l’analisi arriva fin lì. Un profilo che si carica è quindi prova che la sintassi è corretta, non che i file che nomina siano stati trovati.
Cosa separa il manuale: --config e --cd
Il manuale di OpenVPN 2.6 descrive due opzioni distinte. Una carica un file di configurazione. L’altra cambia la directory del processo prima che vengano letti la configurazione, la chiave, lo script e altri file, e richiede una directory assoluta senza riferimenti a quella corrente.
openvpn --cd /etc/vpn/prod --config tenant-a.ovpn
Quella chiamata è una forma, non un’istruzione per avviare il profilo di qualcun altro. Ciò che offre è determinismo: la base di ogni nome relativo della catena è dichiarata nel comando invece di essere ereditata dalla shell o dal gestore che ha avviato il processo. L’alternativa è altrettanto valida e spesso migliore per un servizio: lasciate in pace la directory di lavoro e rendete assoluto ogni file referenziato dentro il profilo.
Qualunque scegliate, controllate tutta la catena e non il primo errore. Un profilo può tirare dentro un altro file di configurazione, e i nomi relativi del file incluso vengono risolti rispetto alla stessa directory del processo, non rispetto alla directory del file che li ha inclusi. I percorsi di log, i file di autenticazione e gli script ausiliari seguono la stessa regola.
Il servizio parte altrove
Una shell interattiva nella directory del profilo non dimostra nulla su un’unità pacchettizzata. I gestori impostano la propria directory di lavoro, e uno script involucro può cambiarla di nuovo prima che il binario parta. L’unità che fallisce è l’unico avvio su cui valga la pena ragionare.
systemctl show -p WorkingDirectory -p ExecStart --value openvpn@tenant-a
ls -l /proc/$(pgrep -n openvpn)/cwd
Il secondo comando mostra la directory che il processo vivo mantiene davvero, e questo è più affidabile del file dell’unità quando c’è un involucro. Se i due non concordano, l’involucro è la risposta e l’unità è una pista falsa.
Dopo la ricerca: permessi, chroot e sandbox
Una directory di base corretta può comunque produrre lo stesso messaggio, perché risoluzione e accesso sono passaggi separati. Il processo ha bisogno del permesso di esecuzione su ogni directory padre e di lettura sul file, e un’unità di servizio che nasconde parte del filesystem cambia ciò che il processo può vedere.
Tre varianti comuni producono lo stesso errore. Il file è leggibile solo da root mentre il servizio rinuncia ai privilegi prima di aprirlo. L’unità imposta una vista protettiva del filesystem che non include la directory del profilo. Il demone gira con chroot, quindi il percorso che esiste sull’host non esiste dentro quella vista.
Diagnosticate con la proprietà del file e l’identità del processo, non allentando i permessi. Rendere una chiave privata leggibile a tutti per tacitare questo errore scambia un errore rumoroso con uno silenzioso, e la chiave è la parte della directory che meno desiderate esporre.
Dimostrate la correzione con l’avvio posseduto
Riavviate l’unità reale da una directory senza relazione con il profilo e osservate la stessa riga di avvio. Poi confermate che il processo detiene la directory prevista mentre gira, e che il certificato caricato è quello che vi aspettate.
systemctl restart openvpn@tenant-a
systemctl status openvpn@tenant-a --no-pager
journalctl -u openvpn@tenant-a -n 20 --no-pager
Quando il caricamento dei percorsi riesce, autenticazione e routing restano fasi separate con i propri errori, e una connessione che sale ma non fa passare traffico è un’altra indagine. La correzione qui riguarda solo da quale directory sono arrivati i file, e dovrebbe essere visibile nella chiamata invece di essere ricordata da chi l’ha eseguita a mano l’ultima volta.
Se il profilo è distribuito da un gestore di configurazione, metteteci anche la directory di base o i nomi assoluti dei file. Una correzione manuale su un solo host è la versione di questo problema che torna dopo la ricostruzione successiva.