Gli hook che girano senza errori e segnalano comunque un file mancante di solito guardano nel posto sbagliato, non falliscono nel loro compito. I percorsi relativi in uno script si risolvono rispetto alla directory di lavoro del processo, e un servizio parte con una directory di lavoro che non avete scelto. Il file accanto allo script e il file che il processo apre sono la stessa cosa solo quando qualcosa li ancora. Questo scarto spiega gran parte dei fallimenti degli hook letti per errore come problemi di permessi.

Controlla il percorso prima dei permessi, perché un errore di permessi sul file sbagliato è ancora il file sbagliato.

Un percorso relativo si risolve rispetto al processo, non al file

Quando uno script chiama open("policy.json"), il kernel risolve il nome rispetto alla directory di lavoro corrente del processo. Nulla della posizione dello script stesso partecipa a quella ricerca. OpenVPN avvia gli hook da una directory che sceglie da sé, e l’opzione cd può cambiarla, quindi uno script provato a mano dalla propria cartella si comporta diversamente quando il demone lo esegue. Il test manuale passa, l’esecuzione del servizio fallisce e la riga di log menziona solo il nome mancante.

Python espone la posizione dello script come __file__, ed è questo che rende possibile l’ancoraggio. Path(__file__).resolve().parent / "policy.json" costruisce un percorso a partire dal file invece che dal processo. Quella singola riga rimuove la dipendenza dalla directory di lavoro senza cambiare nulla dei privilegi.

Ancora il percorso di proposito, poi controlla cosa fa resolve

resolve() segue i collegamenti simbolici, quindi un’installazione che collega l’hook al suo posto si àncora alla destinazione del collegamento, non alla directory che contiene il collegamento. Di solito è ciò che si vuole per un deployment versionato, ed è sbagliato per una disposizione che si aspetta dati accanto al collegamento simbolico. Decidi quale delle due cose intende il deployment e scrivi il codice di conseguenza, invece di scoprire la differenza in produzione. Path(__file__).parent senza resolve() conserva la directory propria del collegamento.

Se i dati appartengono all’istanza e non al programma, prendi un percorso assoluto dalla configurazione. Codificare una posizione fissa lega l’hook a una sola disposizione di host.

Attraversa quattro confini in ordine

Inizia dalla directory di lavoro: confronta il WorkingDirectory, l’opzione cd di OpenVPN e qualunque wrapper che cambi directory prima dell’exec. Poi conferma la posizione risolta, perché un file con un nome simile in un altro deployment può rispondere alla ricerca e nascondere l’errore. Poi viene la vista del filesystem: un chroot o uno spazio dei nomi di mount del servizio possono rendere un percorso assoluto dell’host semplicemente assente. Infine l’identità di accesso, che è una questione separata dalla visibilità.

ProtectHome e PrivateTmp nascondono posizioni diverse, e nessuno dei due compare in un elenco di directory preso dall’host. Un percorso che esiste per root in una shell può essere invisibile dentro lo spazio dei nomi dell’unità, ed è per questo che provare lo stesso comando fuori dal servizio dimostra così poco.

Correggi la ricerca senza allargare il servizio

Ancorare il percorso risolve solo il primo confine. Non espone una directory che il servizio nasconde e non concede a un account senza privilegi l’accesso a un file riservato a root. Aumentare script-security cambia quali script OpenVPN eseguirà; non ha alcun effetto su dove si risolve un nome relativo. Cambiare la directory di lavoro per tutto il servizio è più ampio del problema, perché ogni altro percorso relativo di certificato, log o helper si sposta con esso.

Preferisci la correzione stretta: un percorso deterministico nello script, un valore di configurazione esplicito per i dati dell’istanza e una proprietà dei file che corrisponda all’account con cui l’hook gira davvero.

Registra prove che nominano il confine

Un rapporto di guasto utile contiene tre cose: il percorso esatto che l’hook ha tentato, l’ambiente che l’ha prodotto e quale confine ha rifiutato. Stampa il percorso risolto e l’eccezione senza riversare il contenuto del file. Quella terna separa un ancoraggio errato da un mount nascosto e da un diniego di permessi, che sono tre correzioni diverse.

L’affermazione che root possa leggere il file da un terminale risponde a una di quelle domande e a nessuna delle altre. Prova invece dal contesto del servizio stesso.

Articoli correlati