]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Convert kasp zsk retired test case
authorMatthijs Mekking <matthijs@isc.org>
Mon, 17 Mar 2025 15:49:26 +0000 (16:49 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 25 Apr 2025 08:20:46 +0000 (10:20 +0200)
This test case does not easily fit in the standard test case framework,
so it goes into its own suite.

bin/tests/system/kasp/ns3/setup.sh
bin/tests/system/kasp/tests.sh
bin/tests/system/kasp/tests_kasp.py

index b6062dad222b6908823ec2d4be734022f51d0c49..731ba065241711fa477bfc86860a1177eae638b8 100644 (file)
@@ -306,9 +306,8 @@ $SIGNER -S -x -s now-1w -e now+1w -o $zone -O raw -f "${zonefile}.signed" $infil
 
 # These signatures are already expired, and the private ZSK is retired.
 setup zsk-retired.autosign
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T -I now"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
+zsktimes="$keytimes -I now"
+KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
 ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $zone 2>keygen.out.$zone.2)
 $SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" >settime.out.$zone.1 2>&1
 $SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1
index d8497a40fdb0385c5e6672642da0a35fbc9d28ea..d71c16eef4cacb7394def22b55d1582e956c74f4 100644 (file)
@@ -437,96 +437,6 @@ set_keytimes_autosign_policy() {
   set_addkeytime "KEY2" "REMOVED" "${retired}" 695100
 }
 
-#
-# Zone: zsk-retired.autosign.
-#
-set_zone "zsk-retired.autosign"
-set_policy "autosign" "3" "300"
-set_server "ns3" "10.53.0.3"
-# Key properties.
-key_clear "KEY1"
-set_keyrole "KEY1" "ksk"
-set_keylifetime "KEY1" "63072000"
-set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY1" "yes"
-set_zonesigning "KEY1" "no"
-
-key_clear "KEY2"
-set_keyrole "KEY2" "zsk"
-set_keylifetime "KEY2" "31536000"
-set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY2" "no"
-set_zonesigning "KEY2" "yes"
-
-# Both KSK and ZSK stay OMNIPRESENT.
-set_keystate "KEY1" "GOAL" "omnipresent"
-set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
-set_keystate "KEY1" "STATE_DS" "omnipresent"
-
-set_keystate "KEY2" "GOAL" "omnipresent"
-set_keystate "KEY2" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent"
-# Expect only two keys.
-key_clear "KEY3"
-key_clear "KEY4"
-# The third key is not yet expected to be signing.
-set_keyrole "KEY3" "zsk"
-set_keylifetime "KEY3" "31536000"
-set_keyalgorithm "KEY3" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY3" "no"
-set_zonesigning "KEY3" "no"
-# The ZSK goal is set to HIDDEN but records stay OMNIPRESENT until the new ZSK
-# is active.
-set_keystate "KEY2" "GOAL" "hidden"
-set_keystate "KEY2" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent"
-# A new ZSK should be introduced, so expect a key with goal OMNIPRESENT,
-# the DNSKEY introduced (RUMOURED) and the signatures HIDDEN.
-set_keystate "KEY3" "GOAL" "omnipresent"
-set_keystate "KEY3" "STATE_DNSKEY" "rumoured"
-set_keystate "KEY3" "STATE_ZRRSIG" "hidden"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_autosign_policy
-
-# The old ZSK is retired.
-created=$(key_get KEY2 CREATED)
-set_keytime "KEY2" "RETIRED" "${created}"
-set_addkeytime "KEY2" "REMOVED" "${created}" 695100
-# The new ZSK is immediately published.
-created=$(key_get KEY3 CREATED)
-set_keytime "KEY3" "PUBLISHED" "${created}"
-# And becomes active after Ipub:
-# DNSKEY TTL:            300 seconds
-# zone-propagation-delay 5 minutes (300 seconds)
-# publish-safety:        1 hour (3600 seconds)
-# Ipub:                  4200 seconds
-published=$(key_get KEY3 PUBLISHED)
-set_addkeytime "KEY3" "ACTIVE" "${published}" 4200
-# Lzsk:                  1 year (31536000 seconds)
-active=$(key_get KEY3 ACTIVE)
-set_addkeytime "KEY3" "RETIRED" "${active}" 31536000
-# Iret:                  695100 seconds.
-retired=$(key_get KEY3 RETIRED)
-set_addkeytime "KEY3" "REMOVED" "${retired}" 695100
-
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-#check_rrsig_refresh
-
-# Load again, make sure the purged key is not an issue when verifying keys.
-echo_i "load keys for $ZONE, making sure a recently purged key is not an issue when verifying keys ($n)"
-ret=0
-rndccmd 10.53.0.3 loadkeys "$ZONE" >/dev/null || log_error "rndc loadkeys zone ${ZONE} failed"
-wait_for_log 3 "keymgr: $ZONE done" $DIR/named.run || ret=1
-grep "zone $ZONE/IN (signed): zone_rekey:zone_verifykeys failed: some key files are missing" $DIR/named.run && ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
 #
 # Test dnssec-policy inheritance.
 #
index 63efab4dd5bcf52b7c709346abe24f33a68a1467..5fe82fa4037a21c2f1d2528f3cb9bead23cfeffc 100644 (file)
@@ -1151,3 +1151,138 @@ def test_kasp_dnssec_keygen():
     out = isctest.run.cmd(settime, log_stdout=True).stdout.decode("utf-8")
     isctest.kasp.check_keys("kasp", keys, expected)
     isctest.kasp.check_keytimes(keys, expected)
+
+
+def test_kasp_zsk_retired(servers):
+    server = servers["ns3"]
+
+    config = {
+        "dnskey-ttl": timedelta(seconds=300),
+        "ds-ttl": timedelta(days=1),
+        "max-zone-ttl": timedelta(days=1),
+        "parent-propagation-delay": timedelta(hours=1),
+        "publish-safety": timedelta(hours=1),
+        "retire-safety": timedelta(hours=1),
+        "signatures-refresh": timedelta(days=7),
+        "signatures-validity": timedelta(days=14),
+        "zone-propagation-delay": timedelta(minutes=5),
+    }
+
+    zone = "zsk-retired.autosign"
+    policy = "autosign"
+    alg = os.environ["DEFAULT_ALGORITHM_NUMBER"]
+    size = os.environ["DEFAULT_BITS"]
+    key_properties = [
+        f"ksk 63072000 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
+        # zsk predecessor
+        f"zsk 31536000 {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent",
+        # zsk successor
+        f"zsk 31536000 {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:hidden",
+    ]
+    expected = isctest.kasp.policy_to_properties(300, key_properties)
+    keys = isctest.kasp.keydir_to_keylist(zone, "ns3")
+    ksks = [k for k in keys if k.is_ksk()]
+    zsks = [k for k in keys if not k.is_ksk()]
+    isctest.kasp.check_zone_is_signed(server, zone)
+    isctest.kasp.check_keys(zone, keys, expected)
+
+    offset = -timedelta(days=30 * 6)
+    sign_delay = config["signatures-validity"] - config["signatures-refresh"]
+
+    def sumvars(variables):
+        result = timedelta(0)
+        for var in variables:
+            result = result + config[var]
+        return result
+
+    # KSK Key Timings:
+    # IpubC = DprpC + TTLkey
+    # Note: Also need to wait until the signatures are omnipresent.
+    # That's why we use max-zone-ttl instead of dnskey-ttl here.
+    Ipub_KSK = sumvars(["zone-propagation-delay", "max-zone-ttl"])
+    # Iret = DprpP + TTLds
+    Iret_KSK = sumvars(["parent-propagation-delay", "retire-safety", "ds-ttl"])
+
+    # ZSK Key Timings:
+    # Ipub = Dprp + TTLkey
+    Ipub_ZSK = sumvars(["zone-propagation-delay", "publish-safety", "dnskey-ttl"])
+    # Iret = Dsgn + Dprp + TTLsig
+    Iret_ZSK = sumvars(["zone-propagation-delay", "retire-safety", "max-zone-ttl"])
+    Iret_ZSK = Iret_ZSK + sign_delay
+
+    # KSK
+    expected[0].timing["Generated"] = expected[0].key.get_timing("Created")
+    expected[0].timing["Published"] = expected[0].timing["Generated"]
+    expected[0].timing["Published"] = expected[0].timing["Published"] + offset
+    expected[0].timing["Active"] = expected[0].timing["Published"]
+    expected[0].timing["Retired"] = expected[0].timing["Published"] + int(
+        expected[0].metadata["Lifetime"]
+    )
+    # Trdy(N) = Tpub(N) + IpubC
+    expected[0].timing["PublishCDS"] = expected[0].timing["Published"] + Ipub_KSK
+    # Tdea(N) = Tret(N) + Iret
+    expected[0].timing["Removed"] = expected[0].timing["Retired"] + Iret_KSK
+    expected[0].timing["DNSKEYChange"] = None
+    expected[0].timing["DSChange"] = None
+    expected[0].timing["KRRSIGChange"] = None
+
+    # ZSK (predecessor)
+    expected[1].timing["Generated"] = expected[1].key.get_timing("Created")
+    expected[1].timing["Published"] = expected[1].timing["Generated"] + offset
+    expected[1].timing["Active"] = expected[1].timing["Published"]
+    expected[1].timing["Retired"] = expected[1].timing["Generated"]
+    # Tdea(N) = Tret(N) + Iret
+    expected[1].timing["Removed"] = expected[1].timing["Retired"] + Iret_ZSK
+    expected[1].timing["DNSKEYChange"] = None
+    expected[1].timing["ZRRSIGChange"] = None
+
+    # ZSK (successor)
+    expected[2].timing["Generated"] = expected[2].key.get_timing("Created")
+    expected[2].timing["Published"] = expected[2].timing["Generated"]
+    # Trdy(N) = Tpub(N) + Ipub
+    expected[2].timing["Active"] = expected[2].timing["Published"] + Ipub_ZSK
+    # Tret(N) = Tact(N) + Lzsk
+    expected[2].timing["Retired"] = expected[2].timing["Active"] + int(
+        expected[2].metadata["Lifetime"]
+    )
+    # Tdea(N) = Tret(N) + Iret
+    expected[2].timing["Removed"] = expected[2].timing["Retired"] + Iret_ZSK
+    expected[2].timing["DNSKEYChange"] = None
+    expected[2].timing["ZRRSIGChange"] = None
+
+    isctest.kasp.check_keytimes(keys, expected)
+    check_all(server, zone, policy, ksks, zsks)
+
+    queries = [
+        f"{zone} DNSKEY",
+        f"{zone} SOA",
+        f"{zone} NS",
+        f"{zone} NSEC",
+        f"a.{zone} A",
+        f"a.{zone} NSEC",
+        f"b.{zone} A",
+        f"b.{zone} NSEC",
+        f"c.{zone} A",
+        f"c.{zone} NSEC",
+        f"ns3.{zone} A",
+        f"ns3.{zone} NSEC",
+    ]
+
+    def rrsig_is_refreshed():
+        parts = query.split()
+        qname = parts[0]
+        qtype = dns.rdatatype.from_text(parts[1])
+        return isctest.kasp.verify_rrsig_is_refreshed(
+            server, zone, f"ns3/{zone}.db.signed", qname, qtype, ksks, zsks
+        )
+
+    for query in queries:
+        isctest.run.retry_with_timeout(rrsig_is_refreshed, timeout=5)
+
+    # Load again, make sure the purged key is not an issue when verifying keys.
+    with server.watch_log_from_here() as watcher:
+        server.rndc(f"loadkeys {zone}", log=False)
+        watcher.wait_for_line(f"keymgr: {zone} done")
+
+    msg = f"zone {zone}/IN (signed): zone_rekey:zone_verifykeys failed: some key files are missing"
+    server.log.prohibit(msg)