From: Matthijs Mekking Date: Wed, 19 Mar 2025 13:37:28 +0000 (+0100) Subject: Convert going insecure kasp test to pytest X-Git-Tag: v9.21.10~48^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b1d8217d1a39f798983152345f29c48ffa2427f9;p=thirdparty%2Fbind9.git 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. --- diff --git a/bin/tests/system/isctest/kasp.py b/bin/tests/system/isctest/kasp.py index 537ebce0ba6..1aec27c3f5f 100644 --- a/bin/tests/system/isctest/kasp.py +++ b/bin/tests/system/isctest/kasp.py @@ -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 - - 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) + 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) + + numcds = 0 + + 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) - if len(cds) > 0: - assert len(rrsigs) > 0 - check_signatures( - rrsigs, dns.rdatatype.CDS, fqdn, ksks, zsks, offline_ksk=offline_ksk - ) + 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) + assert numcds == len(cdsrrs) def check_subdomain( diff --git a/bin/tests/system/kasp/ns6/named.conf.in b/bin/tests/system/kasp/ns6/named.conf.in index f77069fdb33..df02890922a 100644 --- a/bin/tests/system/kasp/ns6/named.conf.in +++ b/bin/tests/system/kasp/ns6/named.conf.in @@ -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; diff --git a/bin/tests/system/kasp/ns6/named2.conf.in b/bin/tests/system/kasp/ns6/named2.conf.in index 88e67dc3713..83af019480d 100644 --- a/bin/tests/system/kasp/ns6/named2.conf.in +++ b/bin/tests/system/kasp/ns6/named2.conf.in @@ -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. */ diff --git a/bin/tests/system/kasp/ns6/policies/kasp-fips.conf.in b/bin/tests/system/kasp/ns6/policies/kasp-fips.conf.in index 51c4d884889..4f7db5ad517 100644 --- a/bin/tests/system/kasp/ns6/policies/kasp-fips.conf.in +++ b/bin/tests/system/kasp/ns6/policies/kasp-fips.conf.in @@ -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; }; diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh index 6f963e33d02..306178c7b32 100644 --- a/bin/tests/system/kasp/ns6/setup.sh +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -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. diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 4c775678ab3..2dc7957c7b1 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -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. # diff --git a/bin/tests/system/rollover/ns6/kasp.conf.j2 b/bin/tests/system/rollover/ns6/kasp.conf.j2 index 38a1784acf3..73f0d5789a3 100644 --- a/bin/tests/system/rollover/ns6/kasp.conf.j2 +++ b/bin/tests/system/rollover/ns6/kasp.conf.j2 @@ -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; diff --git a/bin/tests/system/rollover/ns6/named.conf.j2 b/bin/tests/system/rollover/ns6/named.conf.j2 index 03432878fe5..917281ea847 100644 --- a/bin/tests/system/rollover/ns6/named.conf.j2 +++ b/bin/tests/system/rollover/ns6/named.conf.j2 @@ -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; }; +}; diff --git a/bin/tests/system/rollover/ns6/named2.conf.j2 b/bin/tests/system/rollover/ns6/named2.conf.j2 index b0cb1b69490..21b721508ef 100644 --- a/bin/tests/system/rollover/ns6/named2.conf.j2 +++ b/bin/tests/system/rollover/ns6/named2.conf.j2 @@ -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; }; +}; diff --git a/bin/tests/system/rollover/ns6/setup.sh b/bin/tests/system/rollover/ns6/setup.sh index f8ab6048e91..0f58df983c9 100644 --- a/bin/tests/system/rollover/ns6/setup.sh +++ b/bin/tests/system/rollover/ns6/setup.sh @@ -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 diff --git a/bin/tests/system/rollover/tests_rollover.py b/bin/tests/system/rollover/tests_rollover.py index 2d9f8275bb9..1331591f533 100644 --- a/bin/tests/system/rollover/tests_rollover.py +++ b/bin/tests/system/rollover/tests_rollover.py @@ -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)