Test removing DNSKEY records with class ANY

The update should ignore DNSKEY, CDNSKEY and CDS records
for keys that are used for signing.
This commit is contained in:
Matthijs Mekking 2026-06-02 11:41:13 +02:00
parent 767dc16481
commit 3711f3b3c6
12 changed files with 282 additions and 39 deletions

View file

@ -12,6 +12,9 @@ a.root-servers.nil. A 10.53.0.1
multisigner. NS ns2.multisigner.
ns2.multisigner. A 10.53.0.2
update-any. NS ns2.update-any.
ns2.update-any. A 10.53.0.2
bad-dsync. NS ns2.bad-dsync.
ns2.bad-dsync. A 10.53.0.2

View file

@ -22,7 +22,7 @@ zonefile=root.db
echo_i "ns1/setup.sh"
for tld in multisigner bad-dsync secondary; do
for tld in multisigner update-any bad-dsync secondary; do
cp "../ns2/dsset-${tld}." .
done

View file

@ -27,6 +27,11 @@ zone "multisigner" {
file "multisigner.db.signed";
};
zone "update-any" {
type primary;
file "update-any.db.signed";
};
zone "bad-dsync" {
type primary;
file "bad-dsync.db.signed";

View file

@ -30,10 +30,10 @@ setup() {
$DSFROMKEY $KSK.key >dsset-ns2-${zone}.
cat $infile $KSK.key $ZSK.key >$zonefile
$SIGNER -g -o $zone $zonefile
# >/dev/null 2>&1
$SIGNER -g -o $zone $zonefile >/dev/null 2>&1
}
setup "multisigner"
setup "update-any"
setup "bad-dsync"
setup "secondary"

View file

@ -0,0 +1,23 @@
$TTL 300
$ORIGIN update-any.
update-any. IN SOA mname1. . (
1 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns2
ns2 A 10.53.0.2
scanner A 10.53.0.2
model2 NS ns3
NS ns4
ns3.model2 A 10.53.0.3
ns4.model2 A 10.53.0.4
*._dsync DSYNC CDS NOTIFY @PORT@ scanner

View file

@ -0,0 +1,16 @@
$TTL 300
@ IN SOA mname1. . (
1 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns3
ns3 A 10.53.0.3
a A 10.0.0.1
b A 10.0.0.2
c A 10.0.0.3

View file

@ -33,6 +33,14 @@ zone "model2.multisigner." {
inline-signing no;
};
zone "model2.update-any." {
type primary;
allow-update { any; };
file "model2.update-any.db";
dnssec-policy model2;
inline-signing no;
};
zone "model2.bad-dsync." {
type primary;
allow-update { any; };

View file

@ -29,6 +29,15 @@ $SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1
$SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1
$DSFROMKEY $KSK.key >dsset-ns3-${zone}.
zone="model2.update-any"
echo_i "setting up zone: $zone"
zonefile="${zone}.db"
KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone)
ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone)
$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1
$SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1
$DSFROMKEY $KSK.key >dsset-ns3-${zone}.
zone="model2.bad-dsync"
echo_i "setting up zone: $zone"
zonefile="${zone}.db"

View file

@ -0,0 +1,15 @@
$TTL 300
@ IN SOA mname1. . (
1 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns4
ns4 A 10.53.0.4
a A 10.0.0.1
b A 10.0.0.2
c A 10.0.0.3

View file

@ -33,6 +33,14 @@ zone "model2.multisigner." {
inline-signing yes;
};
zone "model2.update-any." {
type primary;
allow-update { any; };
file "model2.update-any.db";
dnssec-policy model2;
inline-signing yes;
};
zone "model2.bad-dsync." {
type primary;
allow-update { any; };

View file

@ -29,6 +29,15 @@ $SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1
$SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1
$DSFROMKEY $KSK.key >dsset-ns4-${zone}.
zone="model2.update-any"
echo_i "setting up zone: $zone"
zonefile="${zone}.db"
KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone)
ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone)
$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1
$SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1
$DSFROMKEY $KSK.key >dsset-ns4-${zone}.
zone="model2.bad-dsync"
echo_i "setting up zone: $zone"
zonefile="${zone}.db"

View file

@ -233,7 +233,15 @@ def _check_remove_zsk_fail(
def check_remove_zsk(
server, zone, keys, expected, extra_keys, extra, primary=None, check_fail=False
server,
zone,
keys,
expected,
extra_keys,
extra,
primary=None,
check_fail=False,
update_any=False,
):
isctest.log.info("remove dnskey record:")
@ -242,20 +250,31 @@ def check_remove_zsk(
if check_fail:
_check_remove_zsk_fail(
server, zone, keys, expected, extra_keys, extra, primary=primary
server,
zone,
keys,
expected,
extra_keys,
extra,
primary=primary,
)
# Remove actual ZSK.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove ZSK from other providers"
)
update_msg = dns.update.UpdateMessage(zone)
for zsk in extra_keys:
dnskey = str(zsk.dnskey).split()
rdata = " ".join(dnskey[4:])
update_msg.delete(f"{zone}.", "DNSKEY", rdata)
primary.nsupdate(update_msg)
if update_any:
# Remove ZSK with update ANY.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove DNSKEY RRset with update ANY (expect ours)"
)
update_msg = dns.update.UpdateMessage(zone)
update_msg.delete(f"{zone}.", "DNSKEY")
primary.nsupdate(update_msg)
else:
# Remove actual ZSK.
update_msg = dns.update.UpdateMessage(zone)
for zsk in extra_keys:
dnskey = str(zsk.dnskey).split()
rdata = " ".join(dnskey[4:])
update_msg.delete(f"{zone}.", "DNSKEY", rdata)
primary.nsupdate(update_msg)
wait_for_serial(primary, server, zone)
@ -349,7 +368,15 @@ def _check_remove_cdnskey_fail(
def check_remove_cdnskey(
server, zone, keys, expected, extra_keys, extra, primary=None, check_fail=False
server,
zone,
keys,
expected,
extra_keys,
extra,
primary=None,
check_fail=False,
update_any=False,
):
isctest.log.info("remove cdnskey record:")
@ -358,20 +385,35 @@ def check_remove_cdnskey(
if check_fail:
_check_remove_cdnskey_fail(
server, zone, keys, expected, extra_keys, extra, primary=primary
server,
zone,
keys,
expected,
extra_keys,
extra,
primary=primary,
)
# Remove actual CDNSKEY.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove CDNSKEY from other providers"
)
if update_any:
# Remove CDNSKEY with update ANY.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove CDNSKEY RRset with update ANY (expect ours)"
)
update_msg = dns.update.UpdateMessage(zone)
update_msg.delete(f"{zone}.", "CDNSKEY")
primary.nsupdate(update_msg)
else:
# Remove actual CDNSKEY.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove CDNSKEY from other providers"
)
update_msg = dns.update.UpdateMessage(zone)
for ksk in extra_keys:
dnskey = str(ksk.dnskey).split()
rdata = " ".join(dnskey[4:])
update_msg.delete(f"{zone}.", "CDNSKEY", rdata)
primary.nsupdate(update_msg)
update_msg = dns.update.UpdateMessage(zone)
for ksk in extra_keys:
dnskey = str(ksk.dnskey).split()
rdata = " ".join(dnskey[4:])
update_msg.delete(f"{zone}.", "CDNSKEY", rdata)
primary.nsupdate(update_msg)
wait_for_serial(primary, server, zone)
@ -465,7 +507,15 @@ def _check_remove_cds_fail(
def check_remove_cds(
server, zone, keys, expected, extra_keys, extra, primary=None, check_fail=False
server,
zone,
keys,
expected,
extra_keys,
extra,
primary=None,
check_fail=False,
update_any=False,
):
isctest.log.info("remove cds record:")
@ -477,17 +527,26 @@ def check_remove_cds(
server, zone, keys, expected, extra_keys, extra, primary=primary
)
# Remove actual CDS.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove CDS from other providers"
)
if update_any:
# Remove CDS with update ANY.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove CDS RRset with update ANY (expect ours)"
)
update_msg = dns.update.UpdateMessage(zone)
update_msg.delete(f"{zone}.", "CDS")
primary.nsupdate(update_msg)
else:
# Remove actual CDS.
isctest.log.info(
f"- zone {zone} {primary.identifier}: remove CDS from other providers"
)
update_msg = dns.update.UpdateMessage(zone)
for ksk in extra_keys:
ds = dsfromkey(ksk)
rdata = " ".join(ds[4:])
update_msg.delete(f"{zone}.", "CDS", rdata)
primary.nsupdate(update_msg)
update_msg = dns.update.UpdateMessage(zone)
for ksk in extra_keys:
ds = dsfromkey(ksk)
rdata = " ".join(ds[4:])
update_msg.delete(f"{zone}.", "CDS", rdata)
primary.nsupdate(update_msg)
wait_for_serial(primary, server, zone)
@ -595,6 +654,94 @@ def test_multisigner(ns2, ns3, ns4, default_algorithm):
check_no_dnssec_in_journal(ns4, zone)
def test_multisigner_update_any(ns2, ns3, ns4, default_algorithm):
zone = "model2.update-any"
keyprops = [
f"ksk 0 {default_algorithm.number} {default_algorithm.bits} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
f"zsk 0 {default_algorithm.number} {default_algorithm.bits} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent",
]
# First make sure the zone is properly signed.
isctest.log.info(f"basic DNSSEC tests for {zone}")
isctest.kasp.wait_keymgr_done(ns3, zone)
isctest.kasp.wait_keymgr_done(ns4, zone)
with ns3.watch_log_from_start() as watcher:
watcher.wait_for_line(
f"zone {zone}/IN: dsyncfetch: send NOTIFY(CDS) query to scanner.update-any"
)
with ns4.watch_log_from_start() as watcher:
watcher.wait_for_line(
f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.update-any"
)
with ns2.watch_log_from_start() as watcher:
# Receiving NOTIFY(CDS) has not been implemented yet. Until
# then, notifies for child zones towards the parent result in
# not authoritative (unless child and parent are served by the
# same name server).
watcher.wait_for_line(f"received notify for zone '{zone}': NOTAUTH")
keys3 = isctest.kasp.keydir_to_keylist(zone, ns3.identifier)
ksks3 = [k for k in keys3 if k.is_ksk()]
zsks3 = [k for k in keys3 if not k.is_ksk()]
expected3 = isctest.kasp.policy_to_properties(ttl=TTL, keys=keyprops)
check_dnssec(ns3, zone, keys3, expected3)
keys4 = isctest.kasp.keydir_to_keylist(zone, ns4.identifier)
ksks4 = [k for k in keys4 if k.is_ksk()]
zsks4 = [k for k in keys4 if not k.is_ksk()]
expected4 = isctest.kasp.policy_to_properties(ttl=TTL, keys=keyprops)
check_dnssec(ns4, zone, keys4, expected4)
# Add DNSKEY to RRset.
newprops = [f"zsk unlimited {default_algorithm.number} {default_algorithm.bits}"]
extra = isctest.kasp.policy_to_properties(ttl=TTL, keys=newprops)
extra[0].private = False
extra[0].legacy = True
check_add_zsk(ns3, zone, keys3, expected3, [zsks4[0]], extra)
check_add_zsk(ns4, zone, keys4, expected4, [zsks3[0]], extra)
check_no_dnssec_in_journal(ns4, zone)
# Remove DNSKEY from RRset.
check_remove_zsk(ns3, zone, keys3, expected3, [zsks4[0]], extra, update_any=True)
check_remove_zsk(ns4, zone, keys4, expected4, [zsks3[0]], extra, update_any=True)
check_no_dnssec_in_journal(ns4, zone)
# Add CDNSKEY RRset.
newprops = [f"ksk unlimited {default_algorithm.number} {default_algorithm.bits}"]
extra = isctest.kasp.policy_to_properties(ttl=TTL, keys=newprops)
extra[0].private = False
extra[0].legacy = True
check_add_cdnskey(ns3, zone, keys3, expected3, [ksks4[0]], extra)
check_add_cdnskey(ns4, zone, keys4, expected4, [ksks3[0]], extra)
check_no_dnssec_in_journal(ns4, zone)
# Remove CDNSKEY RRset.
check_remove_cdnskey(
ns3, zone, keys3, expected3, [ksks4[0]], extra, update_any=True
)
check_remove_cdnskey(
ns4, zone, keys4, expected4, [ksks3[0]], extra, update_any=True
)
check_no_dnssec_in_journal(ns4, zone)
# Update CDS RRset.
check_add_cds(ns3, zone, keys3, expected3, [ksks4[0]], extra)
check_add_cds(ns4, zone, keys4, expected4, [ksks3[0]], extra)
check_no_dnssec_in_journal(ns4, zone)
# Remove CDS RRset.
check_remove_cds(ns3, zone, keys3, expected3, [ksks4[0]], extra, update_any=True)
check_remove_cds(ns4, zone, keys4, expected4, [ksks3[0]], extra, update_any=True)
check_no_dnssec_in_journal(ns4, zone)
def test_multisigner_bad_dsync(ns3, ns4):
zone = "model2.bad-dsync"