Add backups, token expiry warnings and name server checks
- Daily database backups: jarl-backup.timer runs 'jarld backup', which writes jarl-<UTC time>.db into backup.dir, checks it before naming it, and prunes by the time in the name while always keeping the newest keep_at_least. New backup: section in jarl.yaml; the dashboard and 'jarl status' show the newest backup and warn when it is stale or failed. The Debian package enables the timer. - 'jarld restore [FILE | latest]': checks the backup, shows it next to the current database, keeps the current one aside and records the restore. 'jarld serve' holds a database lock, so a restore can't run under a live jarld and two jarlds can't share a database. - Token expiry warnings from 14 days ahead: dashboard, token lists, a daily log line, and a CLI warning via the Jarl-Token-Expires header; 'jarl token list --expiring'. - 'jarl zone serials' and the zone page's Name servers tab: compare the serial on a primary zone's secondaries (NS records, also-notify) or a secondary zone's primaries with this server's. - 'jarl zone delegation': for zones on the internet, check the parent's delegation and glue against the zone; an undelegated zone gets a note. - Server: Jarl/<version> header; CLI exit code 2 for failed checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1afed741bf
commit
293e6ddd33
47 files changed
+3717
-95
No files matched your search
@@ -104,6 +104,8 @@ bind:
|
||||
named_checkzone: $CHECKZONE
|
||||
rndc: $RNDC
|
||||
rndc_args: ["-k", "$E/legacy/rndc.key", "-s", "127.0.0.1", "-p", "$CTL"]
|
||||
backup:
|
||||
dir: $E/backups
|
||||
EOF
|
||||
cd $E
|
||||
echo "secret-pass-123" | $JARLD -c jarl.yaml init --password-stdin --token --nameservers ns1.adbraun.test. --contact admin@adbraun.test > init.out 2>&1 || { cat init.out; exit 1; }
|
||||
@@ -286,6 +288,49 @@ run "acme set --view external only" 0 $J acme unset adbraun.test --view internal
|
||||
expect_dig "acme TXT removed from internal" "" _acme-challenge.adbraun.test TXT
|
||||
run "zone file" 0 $J zone file adbraun.test --view external
|
||||
contains "_acme-challenge"
|
||||
# Name server checks: the external zone's NS (203.0.113.1) doesn't answer;
|
||||
# the internal one lists only this server.
|
||||
run "zone serials: internal, only this server" 0 $J zone serials adbraun.test --view internal
|
||||
contains "this server"
|
||||
run "zone serials: unreachable secondary (exit 2)" 2 $J zone serials adbraun.test --view external
|
||||
contains "NO ANSWER"
|
||||
|
||||
# Server header and token expiry warnings
|
||||
if curl -sI http://127.0.0.1:$HTTP/healthz | grep -qi "^Server: Jarl/"; then ok "Server header"; else bad "no Server: Jarl header"; fi
|
||||
SOON=$($J token create soon --expires 3d -q)
|
||||
run "expiring token works" 0 $J --token "$SOON" whoami
|
||||
contains "warning: the API token expires"
|
||||
run "token list --expiring" 0 $J token list --expiring
|
||||
contains "soon"
|
||||
run "status lists the expiring token" 0 $J status
|
||||
contains "Token:.*soon"
|
||||
|
||||
# Backups and restore
|
||||
run "jarld backup" 0 $JARLD -c jarl.yaml backup
|
||||
contains "Database backed up to $E/backups/jarl-"
|
||||
run "status shows the backup" 0 $J status
|
||||
contains "Backup:.*$E/backups/jarl-"
|
||||
run "restore refused while jarld runs" 1 $JARLD -c jarl.yaml restore latest --yes
|
||||
contains "jarld is running"
|
||||
run "a second jarld is refused" 1 $JARLD -c jarl.yaml serve
|
||||
contains "in use by a running jarld"
|
||||
run "record after the backup" 0 $J record add adbraun.test afterbackup A 192.168.1.99 --view internal
|
||||
expect_dig "afterbackup A" "192.168.1.99" afterbackup.adbraun.test A
|
||||
kill $JARLD_PID; wait $JARLD_PID 2>/dev/null
|
||||
run "jarld restore lists backups" 0 $JARLD -c jarl.yaml restore
|
||||
contains "jarl-.*\.db"
|
||||
run "jarld restore latest" 0 $JARLD -c jarl.yaml restore latest --yes
|
||||
contains "previous database is in"
|
||||
$JARLD -c jarl.yaml serve >> jarld.log 2>&1 &
|
||||
JARLD_PID=$!
|
||||
for i in $(seq 50); do curl -s -o /dev/null http://127.0.0.1:$HTTP/healthz && break; sleep 0.2; done
|
||||
run "restored database lacks the later record" 0 $J record list adbraun.test --view internal
|
||||
if echo "$OUT" | grep -q afterbackup; then bad "afterbackup still there after restore"; else ok "afterbackup gone after restore"; fi
|
||||
run "resync after restore" 0 $J resync --force
|
||||
expect_dig "afterbackup gone from named" "" afterbackup.adbraun.test A
|
||||
run "audit shows the restore" 0 $J audit --action system.restore
|
||||
contains "system.restore"
|
||||
|
||||
run "status at the end" 0 $J status
|
||||
contains "Drift:.*none"
|
||||
|
||||
|
||||
+3
-3
@@ -6,12 +6,12 @@
|
||||
# The package installs, with Debian's paths:
|
||||
# /usr/bin/jarld, /usr/bin/jarl
|
||||
# /usr/share/bash-completion/completions/{jarl,jarld}
|
||||
# /usr/lib/systemd/system/jarl.service
|
||||
# /usr/lib/systemd/system/{jarl.service,jarl-backup.service,jarl-backup.timer}
|
||||
# /usr/lib/sysusers.d/jarl.conf, /usr/lib/tmpfiles.d/jarl.conf
|
||||
# /usr/share/doc/jarl/ (README, CHANGELOG, docs, contrib)
|
||||
# and its scripts (deploy/debian) create the jarl user and directories and
|
||||
# /etc/jarl/jarl.yaml on the first install, restart a running jarld on
|
||||
# upgrades, and stop it on removal. No file in /etc belongs to the package,
|
||||
# /etc/jarl/jarl.yaml on the first install, turn on the daily backup timer,
|
||||
# restart a running jarld on upgrades, and stop both on removal. No file in /etc belongs to the package,
|
||||
# so an upgrade never asks about configuration files.
|
||||
#
|
||||
# Variables: DEB_ARCH (Debian architecture, e.g. amd64, arm64, armhf),
|
||||
|
||||
Reference in new issue
Block a user