]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Convert the 'three is a crowd' test case to pytest
authorMatthijs Mekking <matthijs@isc.org>
Tue, 18 Mar 2025 14:13:17 +0000 (15:13 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Mon, 2 Jun 2025 09:21:06 +0000 (09:21 +0000)
This test shows similarities with the Double KSK rollover method, so
put the test in there.

bin/tests/system/kasp/ns3/named-fips.conf.in
bin/tests/system/kasp/ns3/policies/autosign.conf.in
bin/tests/system/kasp/ns3/setup.sh
bin/tests/system/kasp/tests.sh
bin/tests/system/rollover/ns3/named.conf.j2
bin/tests/system/rollover/ns3/setup.sh
bin/tests/system/rollover/tests_rollover.py

index 85636adbb47a925a56fe276b71272b4d46ee6ae1..b93efa4ae06d97afcf3cc3afd3b7673f9dd99c81 100644 (file)
@@ -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.
  */
index 29fabc40c8727dee2ba35f442b1048ab835fd1f3..5d1b0e47506484046eef4ae1ef5ae58a9e43aef7 100644 (file)
@@ -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;
index cf4d0a98e8f5e16d7d2336e6c5dd573ef79436a9..86c16e4293b1a0d03700ec1fd8b83f5a46eb944d 100644 (file)
@@ -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
index b8de25dd80fe86bb88e5d05a0b02d9c847ff0cb9..573e8c640491a1e3245e192f473930668d394dbd 100644 (file)
@@ -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"
index 141d75c0e2495416fd8c54ee42a5bfbdfdc20cef..c183ac8975988d61220c22b6b5e54fdbb39ed05c 100644 (file)
@@ -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";
+};
index dd13005a3d700472695318e2234e9b801c010254..881e3bd565202e0bd039f3d5c2e4ddad10084da1 100644 (file)
@@ -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
index 2fc796b1ad8b83c0b3570fa10e700ed4fa86dec5..27c6ee458c77d468ecf7a771787e5d4a20060539 100644 (file)
@@ -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)