L'esecuzione del playbook termina in verde, l'unità WireGuard è attiva, l'handshake funziona e il nuovo peer non raggiunge comunque nulla. Vedere un playbook che annuncia successo mentre il server usa la configurazione di ieri è particolarmente frustrante, perché nulla nell'output è sbagliato. Ansible ha fatto esattamente ciò che gli è stato chiesto: ha assicurato che l'unità fosse avviata, e lo era già. Applicare un file di configurazione riscritto è un'altra attività, e avviene solo se qualcosa dice al servizio di rileggerlo.

Guarda cosa riporta davvero una seconda esecuzione

Esegui il playbook due volte con la configurazione modificata e confronta il riepilogo. La seconda esecuzione dovrebbe mostrare l'attività dell'unità come ok, non come changed, perché un servizio già in esecuzione viene lasciato in pace per progetto. È l'idempotenza che funziona correttamente, ed è anche il motivo per cui il nuovo file non arriva mai al kernel.

Aggiungi un'esecuzione di controllo con output diff per vedere quali attività toccherebbero qualcosa.

ansible-playbook -i inventory site.yml --check --diff
systemctl show wg-quick@wg0 -p ActiveEnterTimestamp -p NRestarts
stat -c '%y %n' /etc/wireguard/wg0.conf

I tre timestamp raccontano la storia prima ancora di leggere una riga di log. Se il file di configurazione è più recente del momento in cui l'unità è diventata attiva, l'interfaccia in esecuzione è stata costruita da un file più vecchio, e ogni sintomo successivo deriva da lì.

Non è un difetto di Ansible. Lo stesso comportamento compare con qualsiasi strumento di gestione della configurazione che separa «assicurati che sia in esecuzione» da «assicurati che stia eseguendo questo». La distinzione sembra pedante finché non ti costa un pomeriggio.

Avviato è uno stato, non un ricaricamento

state: started afferma una condizione sull'unità. Non promette che il servizio rilegga i suoi input, e non può sapere che un file che non ha mai aperto è cambiato. In un deployment wg-quick gli input includono la configurazione dell'interfaccia, gli indirizzi, le rotte, le voci DNS e qualsiasi hook post-up.

Per questo un file aggiornato può contenere un nuovo peer, un altro indirizzo o una direttiva di routing modificata mentre l'interfaccia attiva continua a servire il vecchio insieme. L'handshake riesce ancora perché le chiavi e gli endpoint caricati sono ancora validi. È esattamente ciò che rende il guasto difficile da vedere, e perché le persone inseguono il DNS per un'ora prima di controllare un timestamp.

Altre due opzioni del modulo vengono spesso scambiate per una soluzione. daemon_reload fa rileggere a systemd i file delle unità, cosa irrilevante per un file di configurazione WireGuard. state: reloaded chiede all'unità di eseguire la sua azione di ricaricamento, e se ciò faccia quello che ti serve dipende interamente dall'unità. Nessuna delle due sostituisce il fatto di sapere quali input il processo in esecuzione ha realmente letto.

Installa il file, poi notifica un handler

Lo schema che conserva l'idempotenza e applica comunque le modifiche è quello abituale: l'attività che scrive il file notifica un handler, e l'handler viene eseguito solo se il file è cambiato davvero. Una seconda esecuzione senza modifiche non fa nulla, ed è ciò che vuoi.

- name: write the interface configuration
  ansible.builtin.template:
    src: wg0.conf.j2
    dest: /etc/wireguard/wg0.conf
    owner: root
    mode: "0600"
  notify: apply wg0

- name: ensure the interface unit is enabled
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    enabled: true
    state: started

- name: apply wg0
  listen: apply wg0
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    state: restarted

Gli handler vengono eseguiti alla fine del play, dopo le attività che li hanno notificati, quindi un'attività più avanti nello stesso play può ancora vedere la vecchia interfaccia. Se l'ordine conta, svuota gli handler esplicitamente con meta: flush_handlers prima dell'attività che dipende dalla nuova configurazione.

Usa restart invece di reload se non hai verificato l'azione di ricaricamento dell'unità. L'unità wg-quick upstream ne supporta una e sincronizza la configurazione WireGuard, ma non riesegue l'impostazione di indirizzi, rotte, DNS o hook. Un reload dopo aver aggiunto una direttiva di rotta sembra quindi riuscire e non cambia nulla.

Confronta il dispositivo attivo con il file

Il controllo che chiude questa classe di bug è un confronto tra ciò che il kernel detiene e ciò che dice il file. Due comandi producono un output confrontabile, e la differenza è lo scostamento.

sudo wg showconf wg0
sudo wg-quick strip wg0
sudo diff <(sudo wg showconf wg0) <(sudo wg-quick strip wg0)

wg showconf stampa il dispositivo attivo, inclusa la chiave privata, quindi tratta quell'output come un segreto e non incollarlo in un ticket. wg-quick strip stampa il file senza le chiavi specifiche di wg-quick, ed è per questo che i due sono confrontabili.

Una differenza nell'elenco dei peer significa che il file non è mai stato applicato. Una differenza limitata a chiavi o endpoint che ti aspettavi cambiassero significa che qualcosa lo ha applicato in parte. Nessuna differenza significa che la configurazione è aggiornata e il guasto è altrove, il che è già un risultato utile.

Paga il costo del riavvio deliberatamente

Riavviare l'interfaccia fa cadere il tunnel, e se quel tunnel trasporta la tua sessione amministrativa, il riavvio termina la sessione che l'ha richiesto. Metti l'handler dietro una finestra di cambiamento, o esegui il playbook da un percorso di gestione che non dipende dal riavvio dell'interfaccia.

Il costo è il motivo per cui le persone ricorrono al reload. È un istinto ragionevole, e diventa corretto quando sai quali modifiche un reload applica davvero. Per modifiche di peer e chiavi sull'unità wg-quick upstream basta; per indirizzamento, routing e hook no. Chiarirlo una volta costa meno che scoprirlo durante una finestra di cambiamento.

La documentazione del modulo systemd_service spiega la differenza tra started, restarted e reloaded, e vale una lettura invece di dedurla da un riepilogo verde. Tutto in questo percorso deriva da quella sola distinzione.

Articoli correlati