Add DNSSEC signing, key backups and Porkbun support
Zones get a DNSSEC setting: named signs them with BIND's default (or a custom) dnssec-policy and inline signing, with keys in a per-view key directory. Jarl shows the keys, the DS record and when it is safe to publish it, checks the parent's DS records (check-ds, which also tells named), and refuses steps that would break a signed domain: switching straight to unsigned, finishing a turn-off too early, deleting or moving a signed zone, TTLs above max-zone-ttl. Turning off goes through the insecure policy; wipe starts over once the parent has no DS. Backups store the DNSSEC keys and restore puts them back for named; the backup unit gets CAP_DAC_READ_SEARCH to read them. Jarl raises a zone's serial above named's whenever it writes an older one (after a restore). The serials check compares the signed serial. With a Porkbun API key in jarl.yaml, the registrar's name servers, glue records and DS records can be shown and changed, each change checked first (dry run, force to override). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
293e6ddd33
commit
5630e7a848
72 files changed
+5760
-153
No files matched your search
@@ -100,6 +100,7 @@ bind:
|
||||
zone_dir: $E/zones
|
||||
cache_dir: $E/cache/jarl
|
||||
file_group: ""
|
||||
named_user: $(id -un)
|
||||
named_checkconf: $CHECKCONF
|
||||
named_checkzone: $CHECKZONE
|
||||
rndc: $RNDC
|
||||
@@ -305,9 +306,32 @@ contains "soon"
|
||||
run "status lists the expiring token" 0 $J status
|
||||
contains "Token:.*soon"
|
||||
|
||||
# DNSSEC: sign the external view's zone (the parent can't be asked here).
|
||||
api() { curl -s -H "Authorization: Bearer $(cat $E/admin.token)" -H 'Content-Type: application/json' "$@"; }
|
||||
run "dnssec status: off" 0 $J zone dnssec status adbraun.test --view external
|
||||
contains "Policy:.*none"
|
||||
run "dnssec enable" 0 $J zone dnssec enable adbraun.test --view external
|
||||
contains "named is signing"
|
||||
for i in $(seq 50); do $J zone dnssec status adbraun.test --view external 2>/dev/null | grep -q "signed serial" && break; sleep 0.2; done
|
||||
run "dnssec status: signing" 0 $J zone dnssec status adbraun.test --view external
|
||||
contains "CSK.*ECDSAP256SHA256"
|
||||
contains "named:.*signed serial"
|
||||
contains "DS record for the parent zone"
|
||||
contains "not yet: wait until"
|
||||
run "dnssec: switching off at once is refused" 0 api -X PATCH -d '{"dnssec_policy":""}' "http://127.0.0.1:$HTTP/api/v1/zones/adbraun.test?view=external"
|
||||
contains "switching DNSSEC off at once"
|
||||
run "dnssec: a TTL over a day is refused" 6 $J record add adbraun.test longttl A 203.0.113.9 --view external --ttl 172800
|
||||
contains "max-zone-ttl"
|
||||
run "record in the signed zone" 0 $J record add adbraun.test signed A 203.0.113.5 --view external
|
||||
run "check-ds without a reachable parent (exit 2)" 2 $J zone dnssec check-ds adbraun.test --view external --resolver 127.0.0.1
|
||||
contains "named wasn't told anything"
|
||||
KEYS=$(ls $E/zones/dnssec-keys/*/Kadbraun.test.+013+*.private 2>/dev/null | head -1)
|
||||
if [ -n "$KEYS" ]; then ok "named's key is in Jarl's key directory"; else bad "no key in $E/zones/dnssec-keys"; fi
|
||||
|
||||
# Backups and restore
|
||||
run "jarld backup" 0 $JARLD -c jarl.yaml backup
|
||||
contains "Database backed up to $E/backups/jarl-"
|
||||
contains "with 3 DNSSEC key files"
|
||||
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
|
||||
@@ -317,20 +341,39 @@ 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
|
||||
KEYSUM=$(sha256sum "$KEYS" | cut -d' ' -f1)
|
||||
rm -f "$KEYS"
|
||||
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"
|
||||
contains "Put back 3 DNSSEC key files"
|
||||
if [ "$(sha256sum "$KEYS" 2>/dev/null | cut -d' ' -f1)" = "$KEYSUM" ]; then ok "the private key is back"; else bad "the private key wasn't restored"; fi
|
||||
$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
|
||||
contains "serial"
|
||||
expect_dig "afterbackup gone from named" "" afterbackup.adbraun.test A
|
||||
run "audit shows the restore" 0 $J audit --action system.restore
|
||||
contains "system.restore"
|
||||
|
||||
# Turning DNSSEC off: insecure first; the DS never reached a parent, so
|
||||
# named can finish at once. Then wipe the old keys.
|
||||
run "dnssec disable starts turning off" 0 $J zone dnssec disable adbraun.test --view external --yes
|
||||
contains "remove the DS record"
|
||||
for i in $(seq 75); do $J zone dnssec status adbraun.test --view external 2>/dev/null | grep -q "Turning DNSSEC off: the parent has no DS\|has finished" && break; sleep 0.2; done
|
||||
run "dnssec disable finishes" 0 $J zone dnssec disable adbraun.test --view external --yes
|
||||
contains "DNSSEC is off"
|
||||
run "dnssec status: old keys left" 0 $J zone dnssec status adbraun.test --view external
|
||||
contains "keys from when the zone was signed"
|
||||
run "dnssec wipe" 0 $J zone dnssec wipe adbraun.test --view external --yes --force --resolver 127.0.0.1
|
||||
contains "removed .*Kadbraun.test"
|
||||
run "dnssec status: clean" 0 $J zone dnssec status adbraun.test --view external
|
||||
contains "Status:.*DNSSEC is off.$"
|
||||
|
||||
run "status at the end" 0 $J status
|
||||
contains "Drift:.*none"
|
||||
|
||||
|
||||
Reference in new issue
Block a user