Convert going insecure kasp test to pytest

When going insecure, we publish CDS and CDNSKEY DELETE records. Update
the check_apex function to test this.

Also, skip some tests in the 'check_rollover_step()' function. If
we change the DNSSEC Policy, keys that no longer match the policy will
be retired. When this exactly happens is hard to determine, as it
happens on the reconfigure. So for these tests, we skip the key timing
metadata checks.

Also, the zone becomes unsigned, so don't call 'check_zone_is_signed'
in those cases.

(cherry picked from commit b1d8217d1a)
This commit is contained in:
Matthijs Mekking 2025-03-19 14:37:28 +01:00
parent 62adf9957f
commit ffccf1fe8b
11 changed files with 330 additions and 533 deletions

View file

@ -997,6 +997,15 @@ def check_cdslog_prohibit(server, zone, key, substr):
)
def check_cdsdelete(rrset, expected):
numrrs = 0
for rr in rrset:
for rdata in rr:
assert expected in f"{rdata}"
numrrs += 1
assert numrrs == 1
def _query_rrset(server, fqdn, qtype, tsig=None):
response = _query(server, fqdn, qtype, tsig=tsig)
assert response.rcode() == dns.rcode.NOERROR
@ -1019,7 +1028,15 @@ def _query_rrset(server, fqdn, qtype, tsig=None):
def check_apex(
server, zone, ksks, zsks, cdss=None, offline_ksk=False, zsk_missing=False, tsig=None
server,
zone,
ksks,
zsks,
cdss=None,
cds_delete=False,
offline_ksk=False,
zsk_missing=False,
tsig=None,
):
# Test the apex of a zone. This checks that the SOA and DNSKEY RRsets
# are signed correctly and with the appropriate keys.
@ -1052,10 +1069,13 @@ def check_apex(
# test cdnskey query
cdnskeys, rrsigs = _query_rrset(server, fqdn, dns.rdatatype.CDNSKEY, tsig=tsig)
if "CDNSKEY" in cdss:
check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True)
if cds_delete:
check_cdsdelete(cdnskeys, "0 3 0 AA==")
else:
assert len(cdnskeys) == 0
if "CDNSKEY" in cdss:
check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True)
else:
assert len(cdnskeys) == 0
if len(cdnskeys) > 0:
assert len(rrsigs) > 0
@ -1065,29 +1085,33 @@ def check_apex(
# test cds query
cds, rrsigs = _query_rrset(server, fqdn, dns.rdatatype.CDS, tsig=tsig)
cdsrrs = []
for rr in cds:
for rdata in rr:
rdclass = dns.rdataclass.to_text(rr.rdclass)
rdtype = dns.rdatatype.to_text(rr.rdtype)
cds = f"{rr.name} {rr.ttl} {rdclass} {rdtype} {rdata}"
cdsrrs.append(cds)
numcds = 0
if cds_delete:
check_cdsdelete(cds, "0 0 0 00")
else:
cdsrrs = []
for rr in cds:
for rdata in rr:
rdclass = dns.rdataclass.to_text(rr.rdclass)
rdtype = dns.rdatatype.to_text(rr.rdtype)
cds = f"{rr.name} {rr.ttl} {rdclass} {rdtype} {rdata}"
cdsrrs.append(cds)
for alg in ["SHA-256", "SHA-384"]:
if f"CDS ({alg})" in cdss:
numcds += check_cds(cdsrrs, ksks, alg)
else:
check_cds_prohibit(cdsrrs, ksks, alg)
numcds = 0
if len(cds) > 0:
assert len(rrsigs) > 0
check_signatures(
rrsigs, dns.rdatatype.CDS, fqdn, ksks, zsks, offline_ksk=offline_ksk
)
for alg in ["SHA-256", "SHA-384"]:
if f"CDS ({alg})" in cdss:
numcds += check_cds(cdsrrs, ksks, alg)
else:
check_cds_prohibit(cdsrrs, ksks, alg)
assert numcds == len(cdsrrs)
if len(cds) > 0:
assert len(rrsigs) > 0
check_signatures(
rrsigs, dns.rdatatype.CDS, fqdn, ksks, zsks, offline_ksk=offline_ksk
)
assert numcds == len(cdsrrs)
def check_subdomain(

View file

@ -44,35 +44,6 @@ zone "." {
file "../../_common/root.hint.blackhole";
};
/* These zones are going insecure. */
zone "step1.going-insecure.kasp" {
type primary;
file "step1.going-insecure.kasp.db";
dnssec-policy "unsigning";
};
zone "step1.going-insecure-dynamic.kasp" {
type primary;
file "step1.going-insecure-dynamic.kasp.db";
dnssec-policy "unsigning";
inline-signing no;
allow-update { any; };
};
zone "step1.going-straight-to-none.kasp" {
type primary;
file "step1.going-straight-to-none.kasp.db";
dnssec-policy "default";
};
zone "step1.going-straight-to-none-dynamic.kasp" {
type primary;
file "step1.going-straight-to-none-dynamic.kasp.db.signed";
inline-signing no;
dnssec-policy "default";
allow-update { any; };
};
/* These are alorithm rollover test zones. */
zone "step1.algorithm-roll.kasp" {
type primary;

View file

@ -43,49 +43,6 @@ zone "." {
file "../../_common/root.hint.blackhole";
};
/* Zones for testing going insecure. */
zone "step1.going-insecure.kasp" {
type primary;
file "step1.going-insecure.kasp.db";
dnssec-policy "insecure";
};
zone "step2.going-insecure.kasp" {
type primary;
file "step2.going-insecure.kasp.db";
dnssec-policy "insecure";
};
zone "step1.going-insecure-dynamic.kasp" {
type primary;
file "step1.going-insecure-dynamic.kasp.db";
inline-signing no;
dnssec-policy "insecure";
allow-update { any; };
};
zone "step2.going-insecure-dynamic.kasp" {
type primary;
file "step2.going-insecure-dynamic.kasp.db";
inline-signing no;
dnssec-policy "insecure";
allow-update { any; };
};
zone "step1.going-straight-to-none.kasp" {
type primary;
file "step1.going-straight-to-none.kasp.db";
dnssec-policy "none";
};
zone "step1.going-straight-to-none-dynamic.kasp" {
type primary;
file "step1.going-straight-to-none-dynamic.kasp.db.signed";
inline-signing no;
dnssec-policy "none";
allow-update { any; };
};
/*
* Zones for testing KSK/ZSK algorithm roll.
*/

View file

@ -11,15 +11,6 @@
* information regarding copyright ownership.
*/
dnssec-policy "unsigning" {
dnskey-ttl 7200;
keys {
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
zsk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@;
};
};
dnssec-policy "nsec3" {
nsec3param iterations 0 optout no salt-length 0;
};

View file

@ -29,71 +29,6 @@ R="RUMOURED"
O="OMNIPRESENT"
U="UNRETENTIVE"
# The child zones (step1, step2) beneath these zones represent the various
# steps of unsigning a zone.
for zn in going-insecure.kasp going-insecure-dynamic.kasp; do
# Step 1:
# Set up a zone with dnssec-policy that is going insecure.
setup step1.$zn
echo "$zone" >>zones
T="now-10d"
ksktimes="-P $T -A $T -P sync $T"
zsktimes="-P $T -A $T"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.2)
cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
# Step 2:
# Set up a zone with dnssec-policy that is going insecure. Don't add
# this zone to the zones file, because this zone is no longer expected
# to be fully signed.
setup step2.$zn
# The DS was withdrawn from the parent zone 26 hours ago.
Trem="now-26h"
ksktimes="-P $T -A $T -P sync $T"
zsktimes="-P $T -A $T"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.2)
$SETTIME -s -g $H -k $O $T -r $O $T -d $U $Trem -D ds $Trem "$KSK" >settime.out.$zone.1 2>&1
$SETTIME -s -g $H -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1
# Fake lifetime of old algorithm keys.
echo "Lifetime: 0" >>"${KSK}.state"
echo "Lifetime: 5184000" >>"${ZSK}.state"
cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
done
# This zone is going straight to "none" policy. This is undefined behavior.
setup step1.going-straight-to-none.kasp
echo "$zone" >>zones
TactN="now"
csktimes="-P ${TactN} -A ${TactN} -P sync ${TactN}"
CSK=$($KEYGEN -k default $csktimes $zone 2>keygen.out.$zone.1)
$SETTIME -s -g $O -k $O $TactN -z $O $TactN -r $O $TactN -d $O $TactN "$CSK" >settime.out.$zone.1 2>&1
cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -z -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
# This zone is going straight to "none" policy. This is undefined behavior.
setup step1.going-straight-to-none-dynamic.kasp
echo "$zone" >>zones
TactN="now"
csktimes="-P ${TactN} -A ${TactN} -P sync ${TactN}"
CSK=$($KEYGEN -k default $csktimes $zone 2>keygen.out.$zone.1)
$SETTIME -s -g $O -k $O $TactN -z $O $TactN -r $O $TactN -d $O $TactN "$CSK" >settime.out.$zone.1 2>&1
cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -z -x -s now-1h -e now+2w -o $zone -O full -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
#
# The zones at algorithm-roll.kasp represent the various steps of a ZSK/KSK
# algorithm rollover.

View file

@ -372,166 +372,6 @@ dnssec_verify
# interval.
check_next_key_event 3600
#
# Testing going insecure.
#
#
# Zone step1.going-insecure.kasp
#
set_zone "step1.going-insecure.kasp"
set_policy "unsigning" "2" "7200"
set_server "ns6" "10.53.0.6"
# Policy parameters.
# Lksk: 0
# Lzsk: 60 days (5184000 seconds)
# Iret(KSK): DS TTL (1d) + DprpP (1h) + retire-safety (1h)
# Iret(KSK): 1d2h (93600 seconds)
# Iret(ZSK): RRSIG TTL (1d) + Dprp (5m) + Dsgn (9d) + retire-safety (1h)
# Iret(ZSK): 10d1h5m (867900 seconds)
Lksk=0
Lzsk=5184000
IretKSK=93600
IretZSK=867900
init_migration_insecure() {
key_clear "KEY1"
set_keyrole "KEY1" "ksk"
set_keylifetime "KEY1" "${Lksk}"
set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
set_keysigning "KEY1" "yes"
set_zonesigning "KEY1" "no"
set_keystate "KEY1" "GOAL" "omnipresent"
set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_DS" "omnipresent"
key_clear "KEY2"
set_keyrole "KEY2" "zsk"
set_keylifetime "KEY2" "${Lzsk}"
set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
set_keysigning "KEY2" "no"
set_zonesigning "KEY2" "yes"
set_keystate "KEY2" "GOAL" "omnipresent"
set_keystate "KEY2" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent"
key_clear "KEY3"
key_clear "KEY4"
}
init_migration_insecure
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
# We have set the timing metadata to now - 10 days (864000 seconds).
rollover_predecessor_keytimes -864000
check_keytimes
check_apex
check_subdomain
dnssec_verify
#
# Zone step1.going-insecure-dynamic.kasp
#
set_zone "step1.going-insecure-dynamic.kasp"
set_dynamic
set_policy "unsigning" "2" "7200"
set_server "ns6" "10.53.0.6"
init_migration_insecure
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
# We have set the timing metadata to now - 10 days (864000 seconds).
rollover_predecessor_keytimes -864000
check_keytimes
check_apex
check_subdomain
dnssec_verify
#
# Zone step1.going-straight-to-none.kasp
#
set_zone "step1.going-straight-to-none.kasp"
set_policy "default" "1" "3600"
set_server "ns6" "10.53.0.6"
# Key properties.
set_keyrole "KEY1" "csk"
set_keylifetime "KEY1" "0"
set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256"
set_keysigning "KEY1" "yes"
set_zonesigning "KEY1" "yes"
# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait.
set_keystate "KEY1" "GOAL" "omnipresent"
set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_ZRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_DS" "omnipresent"
# This policy only has one key.
key_clear "KEY2"
key_clear "KEY3"
key_clear "KEY4"
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
# The first key is immediately published and activated.
created=$(key_get KEY1 CREATED)
set_keytime "KEY1" "PUBLISHED" "${created}"
set_keytime "KEY1" "ACTIVE" "${created}"
set_keytime "KEY1" "SYNCPUBLISH" "${created}"
# Key lifetime is unlimited, so not setting RETIRED and REMOVED.
check_keytimes
check_apex
check_subdomain
dnssec_verify
#
# Zone step1.going-straight-to-none-dynamic.kasp
#
set_zone "step1.going-straight-to-none-dynamic.kasp"
set_policy "default" "1" "3600"
set_server "ns6" "10.53.0.6"
# Key properties.
set_keyrole "KEY1" "csk"
set_keylifetime "KEY1" "0"
set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256"
set_keysigning "KEY1" "yes"
set_zonesigning "KEY1" "yes"
# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait.
set_keystate "KEY1" "GOAL" "omnipresent"
set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_ZRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_DS" "omnipresent"
# This policy only has one key.
key_clear "KEY2"
key_clear "KEY3"
key_clear "KEY4"
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
# The first key is immediately published and activated.
created=$(key_get KEY1 CREATED)
set_keytime "KEY1" "PUBLISHED" "${created}"
set_keytime "KEY1" "ACTIVE" "${created}"
set_keytime "KEY1" "SYNCPUBLISH" "${created}"
# Key lifetime is unlimited, so not setting RETIRED and REMOVED.
check_keytimes
check_apex
check_subdomain
dnssec_verify
# Reconfig dnssec-policy (triggering algorithm roll and other dnssec-policy
# changes).
echo_i "reconfig dnssec-policy to trigger algorithm rollover"
@ -581,206 +421,6 @@ wait_for_done_signing() {
status=$((status + ret))
}
#
# Testing going insecure.
#
#
# Zone: step1.going-insecure.kasp
#
set_zone "step1.going-insecure.kasp"
set_policy "insecure" "2" "3600"
set_server "ns6" "10.53.0.6"
# Expect a CDS/CDNSKEY Delete Record.
set_cdsdelete
# Key goal states should be HIDDEN.
init_migration_insecure
set_keystate "KEY1" "GOAL" "hidden"
set_keystate "KEY2" "GOAL" "hidden"
# The DS may be removed if we are going insecure.
set_keystate "KEY1" "STATE_DS" "unretentive"
# Various signing policy checks.
check_keys
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Tell named that the DS has been removed.
rndc_checkds "$SERVER" "$DIR" "KEY1" "now" "withdrawn" "$ZONE"
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Next key event is when the DS becomes HIDDEN. This happens after the
# parent propagation delay, and DS TTL:
# 1h + 1d = 25h = 90000 seconds.
check_next_key_event 90000
#
# Zone: step2.going-insecure.kasp
#
set_zone "step2.going-insecure.kasp"
set_policy "insecure" "2" "3600"
set_server "ns6" "10.53.0.6"
# The DS is long enough removed from the zone to be considered HIDDEN.
# This means the DNSKEY and the KSK signatures can be removed.
set_keystate "KEY1" "STATE_DS" "hidden"
set_keystate "KEY1" "STATE_DNSKEY" "unretentive"
set_keystate "KEY1" "STATE_KRRSIG" "unretentive"
set_keysigning "KEY1" "no"
set_keystate "KEY2" "STATE_DNSKEY" "unretentive"
set_keystate "KEY2" "STATE_ZRRSIG" "unretentive"
set_zonesigning "KEY2" "no"
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
# Next key event is when the DNSKEY becomes HIDDEN. This happens after the
# propagation delay, plus DNSKEY TTL:
# 5m + 2h = 125m = 7500 seconds.
check_next_key_event 7500
#
# Zone: step1.going-insecure-dynamic.kasp
#
set_zone "step1.going-insecure-dynamic.kasp"
set_dynamic
set_policy "insecure" "2" "3600"
set_server "ns6" "10.53.0.6"
# Expect a CDS/CDNSKEY Delete Record.
set_cdsdelete
# Key goal states should be HIDDEN.
init_migration_insecure
set_keystate "KEY1" "GOAL" "hidden"
set_keystate "KEY2" "GOAL" "hidden"
# The DS may be removed if we are going insecure.
set_keystate "KEY1" "STATE_DS" "unretentive"
# Various signing policy checks.
check_keys
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Tell named that the DS has been removed.
rndc_checkds "$SERVER" "$DIR" "KEY1" "now" "withdrawn" "$ZONE"
wait_for_done_signing
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
dnssec_verify
# Next key event is when the DS becomes HIDDEN. This happens after the
# parent propagation delay, retire safety delay, and DS TTL:
# 1h + 1d = 25h = 90000 seconds.
check_next_key_event 90000
#
# Zone: step2.going-insecure-dynamic.kasp
#
set_zone "step2.going-insecure-dynamic.kasp"
set_dynamic
set_policy "insecure" "2" "3600"
set_server "ns6" "10.53.0.6"
# The DS is long enough removed from the zone to be considered HIDDEN.
# This means the DNSKEY and the KSK signatures can be removed.
set_keystate "KEY1" "STATE_DS" "hidden"
set_keystate "KEY1" "STATE_DNSKEY" "unretentive"
set_keystate "KEY1" "STATE_KRRSIG" "unretentive"
set_keysigning "KEY1" "no"
set_keystate "KEY2" "STATE_DNSKEY" "unretentive"
set_keystate "KEY2" "STATE_ZRRSIG" "unretentive"
set_zonesigning "KEY2" "no"
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
check_apex
check_subdomain
# Next key event is when the DNSKEY becomes HIDDEN. This happens after the
# propagation delay, plus DNSKEY TTL:
# 5m + 2h = 125m = 7500 seconds.
check_next_key_event 7500
#
# Zone: step1.going-straight-to-none.kasp
#
set_zone "step1.going-straight-to-none.kasp"
set_policy "none" "1" "3600"
set_server "ns6" "10.53.0.6"
# The zone will go bogus after signatures expire, but remains validly signed for now.
# Key properties.
set_keyrole "KEY1" "csk"
set_keylifetime "KEY1" "0"
set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256"
set_keysigning "KEY1" "yes"
set_zonesigning "KEY1" "yes"
# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait.
set_keystate "KEY1" "GOAL" "omnipresent"
set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_ZRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_DS" "omnipresent"
# This policy only has one key.
key_clear "KEY2"
key_clear "KEY3"
key_clear "KEY4"
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
dnssec_verify
#
# Zone: step1.going-straight-to-none-dynamic.kasp
#
set_zone "step1.going-straight-to-none-dynamic.kasp"
set_policy "none" "1" "3600"
set_server "ns6" "10.53.0.6"
# The zone will go bogus after signatures expire, but remains validly signed for now.
# Key properties.
set_keyrole "KEY1" "csk"
set_keylifetime "KEY1" "0"
set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256"
set_keysigning "KEY1" "yes"
set_zonesigning "KEY1" "yes"
# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait.
set_keystate "KEY1" "GOAL" "omnipresent"
set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_ZRRSIG" "omnipresent"
set_keystate "KEY1" "STATE_DS" "omnipresent"
# This policy only has one key.
key_clear "KEY2"
key_clear "KEY3"
key_clear "KEY4"
# Various signing policy checks.
check_keys
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
dnssec_verify
#
# Testing KSK/ZSK algorithm rollover.
#

View file

@ -28,6 +28,15 @@ dnssec-policy "long-lifetime" {
};
};
dnssec-policy "unsigning" {
dnskey-ttl 7200;
keys {
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
zsk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@;
};
};
{% if RSASHA1_SUPPORTED == "1" %}
dnssec-policy "rsasha1" {
signatures-refresh P5D;

View file

@ -76,3 +76,32 @@ zone unlimit-lifetime {
file "unlimit-lifetime.db";
dnssec-policy short-lifetime;
};
/* These zones are going insecure. */
zone "step1.going-insecure.kasp" {
type primary;
file "step1.going-insecure.kasp.db";
dnssec-policy "unsigning";
};
zone "step1.going-insecure-dynamic.kasp" {
type primary;
file "step1.going-insecure-dynamic.kasp.db";
dnssec-policy "unsigning";
inline-signing no;
allow-update { any; };
};
zone "step1.going-straight-to-none.kasp" {
type primary;
file "step1.going-straight-to-none.kasp.db";
dnssec-policy "default";
};
zone "step1.going-straight-to-none-dynamic.kasp" {
type primary;
file "step1.going-straight-to-none-dynamic.kasp.db.signed";
inline-signing no;
dnssec-policy "default";
allow-update { any; };
};

View file

@ -75,3 +75,46 @@ zone unlimit-lifetime {
file "unlimit-lifetime.db";
dnssec-policy unlimited-lifetime;
};
/* Zones for testing going insecure. */
zone "step1.going-insecure.kasp" {
type primary;
file "step1.going-insecure.kasp.db";
dnssec-policy "insecure";
};
zone "step2.going-insecure.kasp" {
type primary;
file "step2.going-insecure.kasp.db";
dnssec-policy "insecure";
};
zone "step1.going-insecure-dynamic.kasp" {
type primary;
file "step1.going-insecure-dynamic.kasp.db";
inline-signing no;
dnssec-policy "insecure";
allow-update { any; };
};
zone "step2.going-insecure-dynamic.kasp" {
type primary;
file "step2.going-insecure-dynamic.kasp.db";
inline-signing no;
dnssec-policy "insecure";
allow-update { any; };
};
zone "step1.going-straight-to-none.kasp" {
type primary;
file "step1.going-straight-to-none.kasp.db";
dnssec-policy "none";
};
zone "step1.going-straight-to-none-dynamic.kasp" {
type primary;
file "step1.going-straight-to-none-dynamic.kasp.db.signed";
inline-signing no;
dnssec-policy "none";
allow-update { any; };
};

View file

@ -34,3 +34,68 @@ for zn in dynamic2inline.kasp shorter-lifetime longer-lifetime limit-lifetime \
setup $zn
cp template.db.in $zonefile
done
# The child zones (step1, step2) beneath these zones represent the various
# steps of unsigning a zone.
for zn in going-insecure.kasp going-insecure-dynamic.kasp; do
# Step 1:
# Set up a zone with dnssec-policy that is going insecure.
setup step1.$zn
echo "$zone" >>zones
T="now-10d"
S="now-12955mi"
keytimes="-P $T -A $T"
cdstimes="-P sync $S"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $cdstimes $zone 2>keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2)
cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
# Step 2:
# Set up a zone with dnssec-policy that is going insecure. Don't add
# this zone to the zones file, because this zone is no longer expected
# to be fully signed.
setup step2.$zn
# The DS was withdrawn from the parent zone 26 hours ago.
D="now-26h"
keytimes="-P $T -A $T -I $D -D now"
cdstimes="-P sync $S -D sync $D"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $cdstimes $zone 2>keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2)
$SETTIME -s -g $H -k $O $T -r $O $T -d $U $D -D ds $D "$KSK" >settime.out.$zone.1 2>&1
$SETTIME -s -g $H -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1
# Fake lifetime of old algorithm keys.
echo "Lifetime: 0" >>"${KSK}.state"
echo "Lifetime: 5184000" >>"${ZSK}.state"
cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
done
# These zones are going straight to "none" policy. This is undefined behavior.
T="now-10d"
S="now-12955mi"
csktimes="-P $T -A $T -P sync $S"
setup step1.going-straight-to-none.kasp
echo "$zone" >>zones
CSK=$($KEYGEN -k default $csktimes $zone 2>keygen.out.$zone.1)
$SETTIME -s -g $O -k $O $TactN -z $O $TactN -r $O $TactN -d $O $TactN "$CSK" >settime.out.$zone.1 2>&1
cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -z -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
setup step1.going-straight-to-none-dynamic.kasp
echo "$zone" >>zones
CSK=$($KEYGEN -k default $csktimes $zone 2>keygen.out.$zone.1)
$SETTIME -s -g $O -k $O $TactN -z $O $TactN -r $O $TactN -d $O $TactN "$CSK" >settime.out.$zone.1 2>&1
cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
cp $infile $zonefile
$SIGNER -S -z -x -s now-1h -e now+2w -o $zone -O full -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1

View file

@ -387,18 +387,25 @@ def check_rollover_step(server, config, policy, step):
keyrelationships = step.get("keyrelationships", None)
smooth = step.get("smooth", False)
ds_swap = step.get("ds-swap", True)
cds_delete = step.get("cds-delete", False)
check_keytimes = step.get("check-keytimes", True)
zone_signed = step.get("zone-signed", True)
isctest.log.info(f"check rollover step {zone}")
if zone_signed:
isctest.kasp.check_dnssec_verify(server, zone)
ttl = int(config["dnskey-ttl"].total_seconds())
expected = isctest.kasp.policy_to_properties(ttl, keyprops)
isctest.kasp.check_dnssec_verify(server, zone)
keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
ksks = [k for k in keys if k.is_ksk()]
zsks = [k for k in keys if not k.is_ksk()]
isctest.kasp.check_keys(zone, keys, expected)
for kp in expected:
key = kp.key
# Set expected key timing metadata.
kp.set_expected_keytimes(config)
@ -410,12 +417,19 @@ def check_rollover_step(server, config, policy, step):
expected[suc].metadata["Predecessor"] = expected[prd].key.tag
isctest.kasp.check_keyrelationships(keys, expected)
# Policy changes may retire keys, set expected timing metadata.
if kp.metadata["GoalState"] == "hidden" and "Retired" not in kp.timing:
retired = kp.key.get_timing("Inactive")
kp.timing["Retired"] = retired
kp.timing["Removed"] = retired + Iret(
config, zsk=key.is_zsk(), ksk=key.is_ksk()
)
# Check that CDS publication/withdrawal is logged.
if "KSK" not in kp.metadata:
continue
if kp.metadata["KSK"] == "no":
continue
key = kp.key
if ds_swap and kp.metadata["DSState"] == "rumoured":
assert cdss is not None
@ -434,9 +448,11 @@ def check_rollover_step(server, config, policy, step):
# delay, so set the DS withdraw time to now.
server.rndc(f"dnssec -checkds -key {key.tag} withdrawn {zone}")
isctest.kasp.check_keytimes(keys, expected)
if check_keytimes:
isctest.kasp.check_keytimes(keys, expected)
isctest.kasp.check_dnssecstatus(server, zone, keys, policy=policy)
isctest.kasp.check_apex(server, zone, ksks, zsks, cdss=cdss)
isctest.kasp.check_apex(server, zone, ksks, zsks, cdss=cdss, cds_delete=cds_delete)
isctest.kasp.check_subdomain(server, zone, ksks, zsks, smooth=smooth)
def check_next_key_event():
@ -1278,6 +1294,9 @@ def test_rollover_policy_changes(servers):
"zone-propagation-delay": timedelta(seconds=300),
}
unsigning_config = default_config.copy()
unsigning_config["dnskey-ttl"] = timedelta(seconds=7200)
start_time = KeyTimingMetadata.now()
# Test dynamic zones that switch to inline-signing.
@ -1299,6 +1318,7 @@ def test_rollover_policy_changes(servers):
lifetime = {
"P1Y": int(timedelta(days=365).total_seconds()),
"P6M": int(timedelta(days=31 * 6).total_seconds()),
"P60D": int(timedelta(days=60).total_seconds()),
}
lifetime_update_tests = [
{
@ -1335,6 +1355,47 @@ def test_rollover_policy_changes(servers):
}
steps.append(step)
# Test going insecure.
isctest.log.info("check going insecure")
offset = -timedelta(days=10)
offval = int(offset.total_seconds())
zones = [
"step1.going-insecure.kasp",
"step1.going-insecure-dynamic.kasp",
]
for zone in zones:
step = {
"zone": zone,
"cdss": cdss,
"config": unsigning_config,
"policy": "unsigning",
"keyprops": [
f"ksk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{offval}",
f"zsk {lifetime['P60D']} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{offval}",
],
"nextev": None,
}
steps.append(step)
# Test going straight to none.
isctest.log.info("check going straight to none")
zones = [
"step1.going-straight-to-none.kasp",
"step1.going-straight-to-none-dynamic.kasp",
]
for zone in zones:
step = {
"zone": zone,
"cdss": cdss,
"config": default_config,
"policy": "default",
"keyprops": [
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{offval}",
],
"nextev": None,
}
steps.append(step)
for step in steps:
check_rollover_step(server, step["config"], step["policy"], step)
@ -1385,5 +1446,77 @@ def test_rollover_policy_changes(servers):
}
steps.append(step)
# Test going insecure (after reconfig).
isctest.log.info("check going insecure (after reconfig)")
oldttl = unsigning_config["dnskey-ttl"]
offset = -timedelta(days=10)
offval = int(offset.total_seconds())
zones = ["going-insecure.kasp", "going-insecure-dynamic.kasp"]
for parent in zones:
# Step 1.
# Key goal states should be HIDDEN.
# The DS may be removed if we are going insecure.
step = {
"zone": f"step1.{parent}",
"cdss": cdss,
"config": default_config,
"policy": "insecure",
"keyprops": [
f"ksk 0 {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{offval}",
f"zsk {lifetime['P60D']} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent offset:{offval}",
],
# Next key event is when the DS becomes HIDDEN. This
# happens after the# parent propagation delay plus DS TTL.
"nextev": default_config["ds-ttl"]
+ default_config["parent-propagation-delay"],
# Going insecure, check for CDS/CDNSKEY DELETE, and skip key timing checks.
"cds-delete": True,
"check-keytimes": False,
}
steps.append(step)
# Step 2.
# The DS is long enough removed from the zone to be considered
# HIDDEN. This means the DNSKEY and the KSK signatures can be
# removed.
step = {
"zone": f"step2.{parent}",
"cdss": cdss,
"config": default_config,
"policy": "insecure",
"keyprops": [
f"ksk 0 {alg} {size} goal:hidden dnskey:unretentive krrsig:unretentive ds:hidden offset:{offval}",
f"zsk {lifetime['P60D']} {alg} {size} goal:hidden dnskey:unretentive zrrsig:unretentive offset:{offval}",
],
# Next key event is when the DNSKEY becomes HIDDEN.
# This happens after the propagation delay, plus DNSKEY TTL.
"nextev": oldttl + default_config["zone-propagation-delay"],
# Zone is no longer signed.
"zone-signed": False,
"check-keytimes": False,
}
steps.append(step)
# Test going straight to none.
isctest.log.info("check going straight to none (after reconfig)")
zones = [
"step1.going-straight-to-none.kasp",
"step1.going-straight-to-none-dynamic.kasp",
]
for zone in zones:
step = {
"zone": zone,
"cdss": cdss,
"config": default_config,
"policy": None,
# These zones will go bogus after signatures expire, but
# remain validly signed for now.
"keyprops": [
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{offval}",
],
"nextev": None,
}
steps.append(step)
for step in steps:
check_rollover_step(server, step["config"], step["policy"], step)