Se uno script hook raggiunge l’altro lato del tunnel dipende da tre condizioni: a quale evento è collegato, se OpenVPN è ancora dentro la propria sequenza di avvio quando lo script viene eseguito e se il lavoro è stato scollegato da quella sequenza. La maggior parte degli script fallisce sulla terza. Aggiungere un’attesa tratta un problema di ciclo di vita come un problema di tempi, e funziona abbastanza spesso da trarre in inganno.

Decidete prima l’evento, poi il meccanismo per rinviare il lavoro, poi il posto in cui viene eseguito il lavoro rinviato. Queste tre decisioni sono indipendenti, e confonderle è il modo in cui un hook di due righe diventa un intero pomeriggio.

Perché l’hook attende un tunnel che attende lui

OpenVPN chiama i propri hook in modo sincrono, dentro lo stesso processo che sposta i pacchetti. up scatta quando il dispositivo tun o tap esiste ma prima che venga aggiunta una rotta, e route-up scatta dopo le rotte; nessuno dei due momenti promette che una controparte risponda. Finché uno script di uno qualsiasi degli hook è ancora in esecuzione, il demone non inoltra, quindi un comando che si aspetta che il traffico attraversi il tunnel attende un tunnel che attende il comando. Questa attesa circolare è il motivo per cui un’esecuzione manuale passa e l’hook va in timeout.

La toppa ovvia, dormire qualche secondo, a volte funziona perché l’estremità remota è salita da sola, non perché l’attesa abbia riparato qualcosa. Un aiutante che interroga la disponibilità è una risposta diversa: l’hook ritorna, l’inoltro inizia e l’aiutante ottiene ciò che gli serve quando è davvero presente.

Scegliete l’hook in base all’evento, non alla comodità

Collegate up al lavoro che ha bisogno solo del dispositivo, route-up al lavoro che ha bisogno delle rotte, e riservate client-connect e client-disconnect alle attività per singolo client sul lato server. La pulizia alla disconnessione deve sopportare di essere eseguita due volte, perché le riconnessioni su un’interfaccia persistente non seguono la stessa sequenza di un riavvio completo. Sul server, client-connect scatta quando un client si autentica, non quando il demone parte, cosa che vale la pena confermare prima di spostare lì la logica. Se il lavoro appartiene al ciclo di vita del servizio e non a una singola connessione, un’unità systemd è una casa migliore di qualsiasi hook.

I comandi hook sono eseguiti direttamente dal demone, senza alcuna shell in mezzo, quindi ifconfig_remote in una riga up arriva come testo letterale. Avvolgete il comando in una shell esplicita, oppure tenete la logica in uno script che legge l’ambiente. ipchange è una direttiva che aggiunge argomenti propri: consegna l’indirizzo e la porta al comando, quindi uno script scritto per nessuno ne vede due inattesi.

Tre modi per scollegare il lavoro

La prima opzione è mandare l’aiutante in background dall’interno dell’hook: avviatelo con setsid o systemd-run --scope, chiudete i descrittori ereditati e uscite subito con zero. La seconda è far scrivere all’hook un piccolo file di stato e lasciare che un timer systemd o un’unità di percorso agiscano su di esso, il che mantiene l’hook banale e mette la politica di nuovo tentativo dove ci sono i log. La terza è abbandonare del tutto l’hook e spostare il lavoro in un’unità ordinata dopo il servizio VPN, che è la risposta giusta quando il lavoro non è affatto per connessione. Tutte e tre condividono una regola: l’hook deve ritornare prima di aver bisogno del tunnel.

Qualunque cosa scegliate, rendete l’aiutante idempotente e osservabile. Due invocazioni sovrapposte non dovrebbero eseguire due volte la stessa azione irreversibile, e un aiutante che fallisce deve lasciare qualcosa che una persona possa leggere.

Separate i guasti della sandbox dai guasti del ciclo di vita

Quando uno script viene eseguito da un prompt di root e fallisce come servizio, la causa è di solito una sandbox e non la rete. Una ProtectHome restrittiva, una ProtectSystem in sola lettura, o una directory di lavoro che non esiste possono rendere irraggiungibili lo script o i suoi dati, e su un host SELinux il file ha bisogno anche di un contesto che consenta l’esecuzione. Leggete l’unità con systemctl cat openvpn-server@server e il journal con journalctl -u openvpn-server@server -n 80, poi cercate un errore di permessi o di contesto invece di un timeout di connessione. Disattivare la sandbox dovrebbe essere l’ultima mossa, non la prima.

Un guasto del ciclo di vita appare diverso: l’hook parte, l’aiutante gira e il socket semplicemente non c’è ancora. Scrivete una riga con marca temporale dall’hook stesso, disconnettete e riconnettete, e leggete gli eventi in ordine. Quando l’aiutante ha davvero bisogno del tunnel, lasciategli interrogare la disponibilità invece di dormire per un periodo fisso.

Tre campi rendono utile il rapporto successivo: quale hook è stato eseguito, quale account ha usato e dove si è fermato. Senza di essi ogni indagine successiva ricomincia da zero.

Articoli correlati