IT

Backup e ripristino

Work in progress — content may be incomplete.

Una foto del config tree, un ripristino atomico, il daemon che continua a girare.

Cos’è

warden config backup scrive un singolo archivio tar.gz con l’albero di configurazione di Warden — il config.toml master più ogni directory sorella *.d/ che Warden sta usando in quel momento. warden config restore <archivio> prende uno di quegli archivi, sostituisce l’albero live in modo atomico e segnala al daemon di ricaricare — niente systemctl restart.

Sono checkpoint operativi, non snapshot differenziali. Ne fai uno prima di una modifica rischiosa (“sto per cambiare le blocklist”), tieni qualche copia rotante su un disco separato, e ripristini la più recente che funziona quando qualcosa va storto. Warden può anche prendere questi checkpoint in automatico, su pianificazione — vedi backup automatici più sotto.

Quando ti serve

  • Prima di una modifica strutturale. Toccare [server], cambiare i resolver upstream, importare un nuovo set di blocklist — qualunque cosa in cui un refuso o un errore di validazione può lasciarti senza DNS sulla LAN. Fai prima il backup; se va male, ripristini in pochi secondi.
  • Migrazione su una nuova macchina. Esegui warden config backup sulla vecchia, copi l’archivio, esegui warden config restore sulla nuova. Il daemon sulla nuova macchina prende la config senza riavvio.
  • Recupero da un’edit sbagliata. Una warden config edit che ha introdotto un errore logico notato un’ora dopo? Ripristina l’archivio pre-modifica più recente e lascia che il daemon faccia hot-reload.
  • Backup off-host periodico. Un cron notturno che esegue warden config backup --out /mnt/backup/warden/ e ruota gli archivi vecchi ti dà una finestra rotante senza toccare il daemon — oppure lascia fare allo scheduler integrato.

Non ti servono backup/restore per i dati delle blocklist, per le statistiche, per il log di audit o di query — non fanno parte dell’albero di configurazione (vedi sotto). Per lo stato di sistema completo, snapshotta /var/lib/purge-warden/ separatamente.

Cosa contiene

Un backup è un tar.gz dell’albero di configurazione, ancorato alla directory padre del config.toml master. Contiene:

  • Il TOML master (config.toml, o il path che Warden sta usando).
  • Ogni directory sorella *.d/ esistente al momento del backup: devices.d/, profiles.d/, groups.d/, blocklists.d/, subnets.d/, schedules.d/, rules.d/.

Non contiene:

  • Le blocklist scaricate sotto /var/lib/purge-warden/lists/.
  • Gli snapshot di statistiche sotto /var/lib/purge-warden/data/.
  • Il log di audit né il log di query.
  • Token IPC, token REST API, file *.secret.
  • Il PID file.

Niente manifest, niente schema-version embedded, nessuna cifratura — l’archivio è un tar -czf standard. Sorgente: src/cli/commands/config/backup.rs.

Perché solo la config
Le blocklist si riscaricano al refresh successivo; le statistiche si rigenerano dal traffico live; i log di audit e query sono journal append-only di eventi passati che in genere non vuoi sovrascrivere. L’albero di configurazione, invece, è l’unica cosa che è irrimediabilmente lavoro tuo — e l’unica in cui un’edit sbagliata può costarti il DNS dell’intera LAN. Lo scope del backup riflette questa asimmetria.

CLI

ComandoCosa fa
warden config backupScrive <backup-dir>/config-<UTC-timestamp>.tar.gz (dir di default <config-parent>/backups).
warden config backup --out <dir>Scrive invece l’archivio in <dir>/config-<UTC-timestamp>.tar.gz. La directory deve esistere.
warden config restore <archivio>Valida l’archivio, sostituisce l’albero live, segnala al daemon di ricaricare.
warden config restore --listElenca i restore point nella backup dir (data, età, dimensione, dal più recente). Non ripristina nulla.
warden config restore --latestRipristina l’archivio più recente nella backup dir senza nominarlo.

Il nome del file dell’archivio è sempre config-YYYYMMDDTHHMMSSZ.tar.gz; solo la directory è configurabile ([backup] dir o --out). Sorgente: src/cli/mod.rs, src/cli/commands/config/backup.rs.

Non c’è output --jsonbackup info. Per vedere cosa puoi ripristinare, usa warden config restore --list. Per ispezionare il contenuto di un archivio senza ripristinarlo, estrailo in una directory di lavoro e lancia warden config lint <estratto>/config.toml.

I flag dedicati allo scheduler --auto e --reset-auto-failure sono trattati in backup automatici.

Dove finiscono gli archivi

Di default gli archivi finiscono in <config-parent>/backups/ — la directory backups/ accanto al tuo config.toml master. Puoi spostarli altrove con la sezione opzionale [backup]:

config.toml
toml
[backup]
dir = "/srv/purge-backups"   # default: <config-parent>/backups

Il daemon ignora del tutto [backup] — è solo per il tooling. La CLI (warden config backup, restore --list) e il restore picker della dashboard leggono tutte questa singola impostazione. (Modificarla dalla dashboard non è in questa release — impostala nel file.)

Permessi della backup dir. I backup girano come utente del daemon (purge-warden), con gate peer-uid, sia da CLI sia da dashboard. La directory di backup deve essere scrivibile da quell’utente — altrimenti tar fallisce con Permission denied. L’installer (scripts/install.sh) prepara la default <config-parent>/backups con l’ownership corretto, quindi su un’installazione standard non serve fare niente. Se punti dir a un percorso custom (es. /srv/purge-backups, un volume dedicato), i permessi di quel path sono responsabilità tua:

sudo install -d -m 0755 -o purge-warden -g purge-warden /srv/purge-backups

Esempi

Un checkpoint manuale prima di un’edit:

sudo warden config backup
# → /etc/purge-warden/backups/config-20260505T091322Z.tar.gz
sudo warden config edit

Un backup notturno rotante su un mount off-host:

/etc/cron.d/purge-warden-backup
cron
# Snapshot notturno della config Warden, rotazione dopo 30 giorni.
15 3 * * *  root  /usr/bin/warden config backup --out /mnt/backup/warden/ && \
                  find /mnt/backup/warden/ -name 'config-*.tar.gz' -mtime +30 -delete

Una migrazione verso una nuova macchina:

# sulla vecchia
sudo warden config backup --out /tmp/
scp /tmp/config-*.tar.gz nuovo-host:/tmp/

# sulla nuova (dopo `warden init`)
sudo warden config restore /tmp/config-20260505T091322Z.tar.gz

Un recupero da un’edit sbagliata — elenca i restore point, poi prendi il più recente:

sudo warden config restore --list
# config-20260505T091322Z.tar.gz   2 minuti fa      14.2 KiB
# config-20260504T031500Z.tar.gz   1 giorno fa      14.1 KiB
# config-20260503T031500Z.tar.gz   2 giorni fa      13.9 KiB

sudo warden config restore --latest

Dalla dashboard

La tab Settings della TUI pilota lo stesso engine:

  • b — backup immediato. Il path dell’archivio compare nel footer.
  • R — apre il restore picker: ogni restore point con data, età relativa e dimensione, dal più recente. Ne scegli uno, premi Enter, confermi con y; Warden lo ripristina e ricarica il daemon. La config precedente resta accanto come copia .pre-restore, quindi il restore è recuperabile.

Backup automatici

Tutto quanto sopra è manuale. Warden può anche fare backup su pianificazione: un timer systemd sveglia il binario ogni ora, e warden config backup --auto decide se un backup è davvero dovuto. Il daemon resta intatto — lo scheduler è puro tooling, nessuna nuova superficie IPC.

Configurazione

Estendi [backup] con fino a cinque campi opzionali:

config.toml
toml
[backup]
dir                    = "/var/lib/purge-warden/backups"  # esisteva già
auto_interval          = "24h"   # off quando assente. "<N>h" o "<N>d", range 1h..720h (30d).
on_change              = false   # riservato a una release futura; parsato ma inerte.
retention_count        = 30      # tieni al massimo N archivi (0 = illimitato).
retention_days         = 90      # scarta archivi più vecchi di D giorni (0 = illimitato).
disable_after_failures = 3       # auto-disabilita dopo N fallimenti consecutivi (0 = mai).

Tutti opzionali; le config esistenti continuano a caricarsi invariate. Senza auto_interval lo scheduler non fa niente. retention_count e retention_days sono in OR — un archivio viene scartato se supera anche solo una delle due soglie. La retention rimuove solo file config-*.tar.gz; i file .lock, .auto_state e .pre-restore-* non vengono mai potati.

Come gira la pianificazione

Due unit systemd, installate entrambe per te:

  • purge-warden-backup.timer — scatta ogni ora (OnUnitActiveSec=1h, OnBootSec=15min, Persistent=true), e recupera al boot se la macchina era spenta quando un tick era dovuto.
  • purge-warden-backup.serviceType=oneshot, esegue warden config backup --auto come utente del daemon.

Il timer non conosce il tuo intervallo. Ogni run --auto legge lo state file e decide:

  1. disabled = true → exit 0.
  2. Nessun auto_interval impostato → exit 0.
  3. now - last_attempt < auto_interval → exit 0 (“non dovuto”).
  4. Altrimenti → acquisisce il lock, fa il backup, aggiorna lo stato, applica la retention.

Cambiare auto_interval ha effetto al tick orario successivo — niente systemctl daemon-reload, niente reinstall.

State file

<backup-dir>/.auto_state — JSON, scritto in modo atomico, leggibile a mano:

sudo cat /var/lib/purge-warden/backups/.auto_state
# {
#   "consecutive_failures": 0,
#   "last_attempt": "2026-05-29T03:00:00Z",
#   "last_outcome": { "kind": "ok" },
#   "disabled": false
# }
  • consecutive_failures — quanti run --auto di fila sono falliti. Azzerato al primo successo. I run manuali non lo toccano mai.
  • last_attempt — RFC3339 UTC del run più recente (anche il manuale lo aggiorna, così le altre viste restano coerenti).
  • last_outcome{"kind":"ok"} oppure {"kind":"err","message":"…"}.
  • disabledtrue dopo disable_after_failures fallimenti auto consecutivi. I backup manuali lo ignorano e girano comunque.

Riabilitare dopo l’auto-disable

Dopo disable_after_failures fallimenti consecutivi lo scheduler imposta disabled = true e i tick successivi diventano no-op. Per sbloccarlo:

sudo -u purge-warden warden config backup --reset-auto-failure

Azzera consecutive_failures e disabled, non scrive alcun archivio e preserva la storia (last_attempt / last_outcome). È idempotente — un no-op quando non c’è niente da resettare. Il prossimo tick --auto riprova normalmente, e il banner rosso sulla tab Settings sparisce.

Lock file

<backup-dir>/.lock viene creato all’inizio di ogni backup (manuale o auto) e rimosso alla fine:

  • Un altro backup già in corso → exit 75 (EX_TEMPFAIL) con backup in progress (lock held since <ts>) su stderr.
  • Un lock stale (mtime più vecchio di 5 minuti, lasciato da un run crashato) viene rimosso automaticamente dal run successivo.

Ispezionare lo scheduler

systemctl status purge-warden-backup.timer    # quando scatta il prossimo tick
systemctl status purge-warden-backup.service  # ultimo tick + exit code
journalctl -u purge-warden-backup.service     # storia dei tick (incl. "non dovuto" / "disabled")

Come funziona il restore

Conoscere l’ordine delle operazioni aiuta a capire cosa è sicuro e cosa no.

  1. Estrazione. L’archivio viene scompattato in una directory di staging dedicata sotto $TMPDIR. La directory di staging viene pulita automaticamente all’uscita, anche in caso di fallimento.
  2. Localizzazione del master. L’handler cerca, nell’albero estratto, un file con lo stesso nome del master live; se non lo trova, ricade sul primo *.toml alla radice dello staging.
  3. Validazione. Il master estratto viene caricato attraverso il loader v1 completo di Warden (loader::load_config) — lo stesso validatore che usa warden config lint. Se la validazione fallisce, l’albero live non viene toccato. Exit code 1, errori stampati con file e linea.
  4. Snapshot del master live. Il master live corrente viene rinominato in <live>.pre-restore-<UTC-timestamp> come via di rollback.
  5. Replace atomico del master. Il master estratto viene scritto in posizione tramite atomic_write_and_validate — file temp nella stessa directory, seguito da rename POSIX. Se il processo crasha a metà scrittura, il file live è intatto.
  6. Replace di ogni sorella .d/. Per ogni directory *.d/ presente nell’albero estratto, quella live viene rimossa e quella estratta ricopiata ricorsivamente. Questo passo non è atomico fra directory — un crash fra due sorelle lascia un mix.
  7. Segnale al daemon. Se il PID file è presente, l’handler invia un SIGHUP. Il daemon ricarica da disco via ArcSwap — niente riavvio, niente query perse. Se il SIGHUP fallisce, il restore va comunque a buon fine e stampa un warning; il prossimo warden reload (o l’avvio del daemon) prenderà il nuovo stato.

Sorgente: src/cli/commands/config/restore.rs.

Il restore sostituisce, non fonde
Le directory *.d/ live vengono rimosse e sostituite per intero. Se hai file slice locali in devices.d/ o profiles.d/ che non sono nell’archivio, verranno cancellati. Se è un problema, copiali da parte prima del restore e rimettili dopo.
Rollback di un restore
Lo snapshot pre-restore copre solo il TOML master (config.toml.pre-restore-<ts>), non le sorelle .d/. Per fare il rollback completo di un restore, ripristina dall’archivio precedente, non dal file .pre-restore.

Exit code

CodeSignificato
0Successo — backup scritto, restore applicato (SIGHUP recapitato, se applicabile), o un run --auto non dovuto.
1Validazione del restore fallita (l’albero live è invariato, l’errore nomina file e chiave), oppure errore I/O: archivio mancante, illeggibile, tarball malformato, destinazione non scrivibile.
75EX_TEMPFAIL — un backup è già in corso (lock occupato). Riprova fra poco.

Sorgente: src/cli/commands/config/restore.rs, src/cli/commands/config/backup.rs.

Sicurezza

  • Gli archivi contengono config in chiaro, inclusi eventuali token o segreti che hai inserito direttamente nel TOML. Trattali come sensibili: niente push su un Git remoto pubblico, e preferisci chmod 600 sulla directory di destinazione.
  • Nessuna cifratura. Se ti serve un backup off-host cifrato, avvolgi l’archivio: gpg --encrypt, age, oppure includilo in uno snapshot di un filesystem cifrato.
  • Nessuna firma. L’handler di restore si fida del contenuto dell’archivio oltre il validatore v1. Non ripristinare archivi da fonti non fidate.
  • Audit trail. Le azioni di backup e restore sono registrate nel log di audit con uid invocante, path dell’archivio e risultato.

Troubleshooting

warden config restore esce con 1 e schema_version mismatch. L’archivio è di una versione di Warden precedente a 1.x. Lancia warden migrate v0-to-v1 sul master estratto prima di ripristinarlo, oppure ripristina un archivio della stessa major version.

warden config restore esce con 0 ma il daemon serve ancora la config vecchia. Il SIGHUP è andato perso (di solito perché il PID file era stale o il daemon era stato riavviato sotto un PID diverso). Lancia warden reload manualmente; se anche quello dice “no daemon”, il daemon non era in esecuzione durante il restore.

warden config backup esce con 75 e backup in progress. Un altro backup tiene il lock — di solito un run --auto pianificato che si sovrappone al tuo manuale. Aspetta un attimo e riprova. Se non c’è nessun backup davvero in corso, un .lock stale più vecchio di 5 minuti viene ripulito dal run successivo.

I backup pianificati hanno smesso di partire. Lo scheduler si è auto-disabilitato dopo fallimenti ripetuti. Controlla sudo cat <backup-dir>/.auto_state per "disabled": true e il messaggio last_outcome, risolvi la causa (di solito i permessi della directory), poi riabilita con warden config backup --reset-auto-failure.

tar: Cannot open: Permission denied da warden config backup. La directory di backup non esiste o non è scrivibile dall’utente del daemon. Creala con l’ownership giusto (vedi dove finiscono gli archivi) o scegli un --out scrivibile.

La dimensione del backup cresce nel tempo. Probabilmente una directory .d/ ha accumulato file slice obsoleti. Esegui warden config lint per individuare gli orfani e ripulirli prima del prossimo backup.

warden config restore ha avuto successo ma un file slice custom è sparito. Il restore sostituisce le .d/ per intero. Lo slice non era nell’archivio. Se ti serve, ripristinalo da un archivio più vecchio che lo contiene, o ricrealo a mano.

Per altri problemi operativi, vedi troubleshooting.

Vedi anche