From: Matthijs Mekking Date: Tue, 18 Mar 2025 14:13:17 +0000 (+0100) Subject: Convert the 'three is a crowd' test case to pytest X-Git-Tag: v9.21.9~15^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=46800e407e14617dcd84084f433a47743ac3ebc7;p=thirdparty%2Fbind9.git Convert the 'three is a crowd' test case to pytest This test shows similarities with the Double KSK rollover method, so put the test in there. --- diff --git a/bin/tests/system/kasp/ns3/named-fips.conf.in b/bin/tests/system/kasp/ns3/named-fips.conf.in index 85636adbb47..b93efa4ae06 100644 --- a/bin/tests/system/kasp/ns3/named-fips.conf.in +++ b/bin/tests/system/kasp/ns3/named-fips.conf.in @@ -237,17 +237,6 @@ zone "max-zone-ttl.kasp" { dnssec-policy "ttl"; }; -/* - * Zone for testing GL #2375: Three is a crowd. - */ -zone "three-is-a-crowd.kasp" { - type primary; - file "three-is-a-crowd.kasp.db"; - inline-signing yes; - /* Use same policy as KSK rollover test zones. */ - dnssec-policy "ksk-doubleksk"; -}; - /* * Zones in different signing states. */ diff --git a/bin/tests/system/kasp/ns3/policies/autosign.conf.in b/bin/tests/system/kasp/ns3/policies/autosign.conf.in index 29fabc40c87..5d1b0e47506 100644 --- a/bin/tests/system/kasp/ns3/policies/autosign.conf.in +++ b/bin/tests/system/kasp/ns3/policies/autosign.conf.in @@ -25,30 +25,6 @@ dnssec-policy "autosign" { }; }; -dnssec-policy "ksk-doubleksk" { - - signatures-refresh P1W; - signatures-validity P2W; - signatures-validity-dnskey P2W; - - dnskey-ttl 2h; - publish-safety P1D; - retire-safety P2D; - purge-keys PT1H; - - cdnskey no; - keys { - ksk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@; - zsk key-directory lifetime P1Y algorithm @DEFAULT_ALGORITHM@; - }; - - zone-propagation-delay PT1H; - max-zone-ttl 1d; - - parent-ds-ttl 3600; - parent-propagation-delay PT1H; -}; - dnssec-policy "csk-roll" { signatures-refresh P5D; diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index cf4d0a98e8f..86c16e4293b 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -835,44 +835,3 @@ private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" cp $infile $zonefile $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 - -# Test #2375, the "three is a crowd" bug, where a new key is introduced but the -# previous rollover has not finished yet. In other words, we have a key KEY2 -# that is the successor of key KEY1, and we introduce a new key KEY3 that is -# the successor of key KEY2: -# -# KEY1 < KEY2 < KEY3. -# -# The expected behavior is that all three keys remain in the zone, and not -# the bug behavior where KEY2 is removed and immediately replaced with KEY3. -# -# Set up a zone that has a KSK (KEY1) and have the successor key (KEY2) -# published as well. -setup three-is-a-crowd.kasp -# These times are the same as step3.ksk-doubleksk.autosign. -TactN="now-60d" -TretN="now" -TremN="now+50h" -TpubN1="now-27h" -TsbmN1="now" -TactN1="${TretN}" -TretN1="now+60d" -TremN1="now+1490h" -ksktimes="-P ${TactN} -A ${TactN} -P sync ${TactN} -I ${TretN} -D ${TremN}" -newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TsbmN1} -I ${TretN1} -D ${TremN1}" -zsktimes="-P ${TactN} -A ${TactN}" -KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $KSK1 $KSK2 -# Sign zone. -cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index b8de25dd80f..573e8c64049 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -939,82 +939,6 @@ check_apex check_subdomain dnssec_verify -# -# Test #2375: Scheduled rollovers are happening faster than they can finish -# -set_zone "three-is-a-crowd.kasp" -set_policy "ksk-doubleksk" "3" "7200" -set_server "ns3" "10.53.0.3" -CDNSKEY="no" -# These are the same time values as calculated for ksk-doubleksk. -Lksk=5184000 -Lzsk=31536000 -IretKSK=180000 -IretZSK=867600 -# KSK (KEY1) is outgoing. -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" "yes" -set_keystate "KEY1" "GOAL" "hidden" -set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" -set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" -set_keystate "KEY1" "STATE_DS" "unretentive" -# KSK (KEY2) is incoming. -key_clear "KEY2" -set_keyrole "KEY2" "ksk" -set_keylifetime "KEY2" "${Lksk}" -set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" -set_keysigning "KEY2" "yes" -set_zonesigning "KEY2" "no" -set_keystate "KEY2" "GOAL" "omnipresent" -set_keystate "KEY2" "STATE_DNSKEY" "omnipresent" -set_keystate "KEY2" "STATE_KRRSIG" "omnipresent" -set_keystate "KEY2" "STATE_DS" "rumoured" -# We will introduce the third KSK shortly. -key_clear "KEY3" -# ZSK (KEY4). -key_clear "KEY4" -set_keyrole "KEY4" "zsk" -set_keylifetime "KEY4" "${Lzsk}" -set_keyalgorithm "KEY4" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" -set_keysigning "KEY4" "no" -set_zonesigning "KEY4" "yes" -set_keystate "KEY4" "GOAL" "omnipresent" -set_keystate "KEY4" "STATE_DNSKEY" "omnipresent" -set_keystate "KEY4" "STATE_ZRRSIG" "omnipresent" -# Run preliminary tests. -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain -dnssec_verify -# Roll over KEY2. -created=$(key_get KEY2 CREATED) -rndc_rollover "$SERVER" "$DIR" $(key_get KEY2 ID) "${created}" "$ZONE" -# Update expected number of keys and key states. -set_keystate "KEY2" "GOAL" "hidden" -set_policy "ksk-doubleksk" "4" "7200" -CDNSKEY="no" -# New KSK (KEY3) is introduced. -set_keyrole "KEY3" "ksk" -set_keylifetime "KEY3" "${Lksk}" -set_keyalgorithm "KEY3" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS" -set_keysigning "KEY3" "yes" -set_zonesigning "KEY3" "no" -set_keystate "KEY3" "GOAL" "omnipresent" -set_keystate "KEY3" "STATE_DNSKEY" "rumoured" -set_keystate "KEY3" "STATE_KRRSIG" "rumoured" -set_keystate "KEY3" "STATE_DS" "hidden" -# Run tests again. We now expect four keys (3x KSK, 1x ZSK). -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain -dnssec_verify - # Test dynamic zones that switch to inline-signing. set_zone "dynamic2inline.kasp" set_policy "default" "1" "3600" diff --git a/bin/tests/system/rollover/ns3/named.conf.j2 b/bin/tests/system/rollover/ns3/named.conf.j2 index 141d75c0e24..c183ac89759 100644 --- a/bin/tests/system/rollover/ns3/named.conf.j2 +++ b/bin/tests/system/rollover/ns3/named.conf.j2 @@ -159,3 +159,14 @@ zone "step6.ksk-doubleksk.autosign" { file "step6.ksk-doubleksk.autosign.db"; dnssec-policy "ksk-doubleksk"; }; + +/* + * Zone for testing GL #2375: Three is a crowd. + */ +zone "three-is-a-crowd.kasp" { + type primary; + file "three-is-a-crowd.kasp.db"; + inline-signing yes; + /* Use same policy as KSK rollover test zones. */ + dnssec-policy "ksk-doubleksk"; +}; diff --git a/bin/tests/system/rollover/ns3/setup.sh b/bin/tests/system/rollover/ns3/setup.sh index dd13005a3d7..881e3bd5652 100644 --- a/bin/tests/system/rollover/ns3/setup.sh +++ b/bin/tests/system/rollover/ns3/setup.sh @@ -516,3 +516,45 @@ private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" cp $infile $zonefile $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + +# Test #2375, the "three is a crowd" bug, where a new key is introduced but the +# previous rollover has not finished yet. In other words, we have a key KEY2 +# that is the successor of key KEY1, and we introduce a new key KEY3 that is +# the successor of key KEY2: +# +# KEY1 < KEY2 < KEY3. +# +# The expected behavior is that all three keys remain in the zone, and not +# the bug behavior where KEY2 is removed and immediately replaced with KEY3. +# +# Set up a zone that has a KSK (KEY1) and have the successor key (KEY2) +# published as well. +setup three-is-a-crowd.kasp +# These times are the same as step3.ksk-doubleksk.autosign. +TpubN="now-60d" +TactN="now-1413h" +TretN="now" +TremN="now+50h" +TpubN1="now-27h" +TsbmN1="now" +TactN1="${TretN}" +TretN1="now+60d" +TremN1="now+1490h" +ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" +newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TsbmN1} -I ${TretN1} -D ${TremN1}" +zsktimes="-P ${TpubN} -A ${TpubN}" +KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) +KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) +$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1 +$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 +# Set key rollover relationship. +key_successor $KSK1 $KSK2 +# Sign zone. +cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" +cp $infile $zonefile +$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -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 2fc796b1ad8..27c6ee458c7 100644 --- a/bin/tests/system/rollover/tests_rollover.py +++ b/bin/tests/system/rollover/tests_rollover.py @@ -388,7 +388,7 @@ def check_rollover_step(server, config, policy, step): zone = step["zone"] keyprops = step["keyprops"] nextev = step["nextev"] - cdss = [] + cdss = None if step.get("cdss"): cdss = step["cdss"] keyrelationships = None @@ -445,7 +445,7 @@ def check_rollover_step(server, config, policy, step): isctest.kasp.check_keytimes(keys, expected) isctest.kasp.check_dnssecstatus(server, zone, keys, policy=policy) - isctest.kasp.check_apex(server, zone, ksks, zsks) + isctest.kasp.check_apex(server, zone, ksks, zsks, cdss=cdss) isctest.kasp.check_subdomain(server, zone, ksks, zsks, smooth=smooth) isctest.kasp.check_dnssec_verify(server, zone) @@ -831,3 +831,65 @@ def test_rollover_ksk_doubleksk(servers): for step in steps: check_rollover_step(server, config, policy, step) + + # Test #2375: Scheduled rollovers are happening faster than they can finish. + zone = "three-is-a-crowd.kasp" + isctest.log.info( + "check that fast rollovers do not remove dependent keys from zone (#2375)" + ) + offset1 = -int(timedelta(days=60).total_seconds()) + offset2 = -int(timedelta(hours=27).total_seconds()) + keyprops = [ + f"ksk {lifetime_policy} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{offset1}", + f"ksk {lifetime_policy} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{offset2}", + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{offset1}", + ] + expected = isctest.kasp.policy_to_properties(ttl, keyprops) + 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) + expected[0].metadata["Successor"] = expected[1].key.tag + expected[1].metadata["Predecessor"] = expected[0].key.tag + isctest.kasp.check_keyrelationships(keys, expected) + for kp in expected: + kp.set_expected_keytimes(config, offset=None) + 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_subdomain(server, zone, ksks, zsks) + isctest.kasp.check_dnssec_verify(server, zone) + # Rollover successor KSK (with DS in rumoured state). + key = expected[1].key + now = KeyTimingMetadata.now() + with server.watch_log_from_here() as watcher: + server.rndc(f"dnssec -rollover -key {key.tag} -when {now} {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + # We now expect four keys (3x KSK, 1x ZSK). + keyprops = [ + f"ksk {lifetime_policy} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{offset1}", + f"ksk {lifetime_policy} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{offset2}", + f"ksk {lifetime_policy} {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden offset:0", + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{offset1}", + ] + expected = isctest.kasp.policy_to_properties(ttl, keyprops) + 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) + expected[0].metadata["Successor"] = expected[1].key.tag + expected[1].metadata["Predecessor"] = expected[0].key.tag + # Three is a crowd scenario. + expected[1].metadata["Successor"] = expected[2].key.tag + expected[2].metadata["Predecessor"] = expected[1].key.tag + isctest.kasp.check_keyrelationships(keys, expected) + for kp in expected: + kp.set_expected_keytimes(config, offset=None) + # The first successor KSK is already being retired. + expected[1].timing["Retired"] = now + ipub + expected[1].timing["Removed"] = now + ipub + iret + 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_subdomain(server, zone, ksks, zsks) + isctest.kasp.check_dnssec_verify(server, zone)