L’hook funziona la prima volta, e dopo tre riconnessioni la catena contiene tre copie della stessa regola. Niente è fallito rumorosamente; lo script ha semplicemente fatto ciò che gli è stato detto ogni volta che è stato eseguito. Un hook VPN è un’operazione di configurazione che può essere eseguita più di una volta, quindi ha bisogno di un proprietario, di uno stato desiderato e di una modifica serializzata. Segui questo ordine e i duplicati si fermano.

Dai un proprietario alle regole prima di scriverne una

Decidi quale catena o tabella possiede questa integrazione prima di scrivere una sola regola. Una catena dedicata che l’hook crea e svuota è la forma più chiara di proprietà, perché la pulizia può poi rimuovere quella catena senza toccare la politica di nessun altro. Se devi aggiungere regole a una catena condivisa, contrassegnale con un commento così che una ricerca successiva trovi esattamente le tue. La proprietà è ciò che rende sicuro lo smontaggio, e lo smontaggio è la parte che fallisce in silenzio.

iptables -N VPN_HOOK 2>/dev/null || true
iptables -C INPUT -j VPN_HOOK 2>/dev/null || iptables -I INPUT -j VPN_HOOK
iptables -S VPN_HOOK

Un commento qui non è decorazione. È la differenza tra rimuovere la tua regola e rimuovere una regola che per caso sembra simile, e sopravvive nel set di regole salvato. Scrivi il commento una volta in una variabile e usa la stessa stringa in ogni comando che aggiunge, verifica o elimina.

Verifica lo stato desiderato invece di aggiungere di nuovo

Scrivi cosa dovrebbe esistere mentre il tunnel è attivo: quali interfacce, quale origine e destinazione, quale protocollo e quale porta. Poi esprimi ogni regola come un test seguito da un inserimento, così che una seconda esecuzione trovi la regola e non faccia nulla. Il comando di verifica esce con zero quando la regola esiste, ed è esattamente il segnale che serve allo script. Il riferimento di iptables documenta le forme di verifica, aggiunta e inserimento usate qui.

iptables -C VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT ||
  iptables -A VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT

Fai attenzione alla posizione. Verificare una regola e inserirla in testa può spostare un permesso davanti a un rifiuto deliberato che prima veniva per primo, quindi la seconda esecuzione cambia il comportamento anche se non aggiunge nulla. Aggiungi in fondo dentro la tua catena e controlla l’ordine tramite la posizione del salto a quella catena. Se l’ordine all’interno della catena conta, ricostruisci la catena da un elenco noto invece di rattopparla regola per regola.

Fermati se la regola che ti serve non può essere espressa come un test stabile. Di solito questo significa che lo stato desiderato è ancora vago.

Serializza la modifica invece di sperare che tenga

Due invocazioni dell’hook possono sovrapporsi, ed entrambe possono vedere una regola assente prima che una delle due la aggiunga. L’opzione di attesa di xtables fa attendere un comando per il lock, ma non rende atomica una sequenza verifica-poi-aggiungi su due comandi. Serializza l’intera operazione con il tuo lock, oppure usa un gestore che applichi un batch dichiarato come un’unica transazione. nftables può fare quest’ultimo, ed è una ragione per preferirlo sugli host dove è disponibile.

flock /run/vpn-hook.lock /usr/local/sbin/vpn-firewall-apply
nft -f /etc/nftables/vpn-hook.nft

Qualunque meccanismo tu scelga, mantieni breve la sezione critica e rendila rientrante: un hook che fallisce a metà dovrebbe poter essere rieseguito in sicurezza. Registra l’esito così che un’esecuzione ripetuta sia visibile nel journal invece di essere dedotta dal numero di regole. Se due supervisori diversi possono invocare l’hook, correggi prima questo; nessun lock ripara due proprietari.

Smonta solo ciò che possiedi

Lo smontaggio deve distinguere una connessione sostitutiva da quella che ha congedato, e deve sopravvivere a un evento che non riceve mai una pulizia ordinata. Elimina con gli stessi identificatori usati per aggiungere, e non svuotare mai una catena condivisa per semplificare la pulizia. Dopo un arresto brusco, esegui una passata di riconciliazione all’avvio successivo invece di presumere che l’hook di discesa sia stato eseguito.

iptables -D VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT
iptables -F VPN_HOOK
ip -brief link show type tun

Anche la durata dell’interfaccia fa parte del progetto. Una riconnessione può creare un dispositivo con un nome diverso, così una regola che codifica il nome del secondo tunnel diventa obsoleta mentre sembra ancora valida. Preferisci l’identità che la connessione stessa espone, e rileggila a ogni esecuzione. Verifica i tre eventi separatamente: una connessione ripetuta, una connessione sostitutiva e uno spegnimento. Il traffico consentito deve sopravvivere a tutti e tre senza duplicati, e i servizi non correlati devono restare intatti.

Il percorso termina quando l’hook ha un proprietario, un test per ogni regola che aggiunge, un percorso serializzato e uno smontaggio che rimuove solo il proprio stato. Un hook sicuro da eseguire due volte vale più di un hook intelligente una volta.

Articoli correlati