From 9272219a53e1cc85fb943c5da2f26da4e06288af Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 17 Mar 2025 16:49:26 +0100 Subject: [PATCH 1/4] Convert kasp zsk retired test case This test case does not easily fit in the standard test case framework, so it goes into its own suite. (cherry picked from commit 1940aa1d0b4a1c3b5820a1513bccee74bc53d6f1) --- bin/tests/system/kasp/ns3/setup.sh | 5 +- bin/tests/system/kasp/tests.sh | 90 ------------------- bin/tests/system/kasp/tests_kasp.py | 135 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 93 deletions(-) diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index b6062dad22..731ba06524 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -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 diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 11335fdfa4..53950cb543 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -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. # diff --git a/bin/tests/system/kasp/tests_kasp.py b/bin/tests/system/kasp/tests_kasp.py index 63efab4dd5..5fe82fa403 100644 --- a/bin/tests/system/kasp/tests_kasp.py +++ b/bin/tests/system/kasp/tests_kasp.py @@ -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) From 2c10acafa438887f9a2b34f7b48e22fdbe2316ea Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 17 Mar 2025 16:39:52 +0100 Subject: [PATCH 2/4] Convert kasp checkds test cases to pytest This converts the checkds test cases that deal with the 'rndc checkds' command and setting the 'DSPublish' and 'DSRemoved' metadata. (cherry picked from commit 44b4d5ebd66163b97b3ba0a08e01b8d99523a58e) --- bin/tests/system/kasp/tests.sh | 231 ---------------------------- bin/tests/system/kasp/tests_kasp.py | 160 +++++++++++++++++++ 2 files changed, 160 insertions(+), 231 deletions(-) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 53950cb543..228ef50cba 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -97,237 +97,6 @@ set_keytimes_csk_policy() { # Key lifetime is unlimited, so not setting RETIRED and REMOVED. } -# 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" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_ZRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -# -# Zone: checkds-ksk.kasp. -# -key_clear "KEY1" -key_clear "KEY2" -key_clear "KEY3" -key_clear "KEY4" - -set_zone "checkds-ksk.kasp" -set_policy "checkds-ksk" "2" "303" -set_server "ns3" "10.53.0.3" - -# Key properties. -set_keyrole "KEY1" "ksk" -set_keylifetime "KEY1" "0" -set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY1" "yes" -set_zonesigning "KEY1" "no" - -set_keyrole "KEY2" "zsk" -set_keylifetime "KEY2" "0" -set_keyalgorithm "KEY2" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY2" "no" -set_zonesigning "KEY2" "yes" -# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. -set_keystate "KEY1" "GOAL" "omnipresent" -set_keystate "KEY1" "STATE_DNSKEY" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -set_keystate "KEY2" "GOAL" "omnipresent" -set_keystate "KEY2" "STATE_DNSKEY" "rumoured" -set_keystate "KEY2" "STATE_ZRRSIG" "rumoured" - -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain -dnssec_verify - -basefile=$(key_get KEY1 BASEFILE) - -_wait_for_metadata() { - _expr=$1 - _file=$2 - grep "$_expr" $_file >/dev/null || return 1 - return 0 -} - -n=$((n + 1)) -echo_i "checkds publish correctly sets DSPublish for zone $ZONE ($n)" -now=$(date +%Y%m%d%H%M%S) -rndc_checkds "$SERVER" "$DIR" "-" "$now" "published" "$ZONE" -retry_quiet 3 _wait_for_metadata "DSPublish: $now" "${basefile}.state" || log_error "bad DSPublish in ${basefile}.state" -# DS State should be forced into RUMOURED. -set_keystate "KEY1" "STATE_DS" "rumoured" -check_keys -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds withdraw correctly sets DSRemoved for zone $ZONE ($n)" -now=$(date +%Y%m%d%H%M%S) -rndc_checkds "$SERVER" "$DIR" "-" "$now" "withdrawn" "$ZONE" -retry_quiet 3 _wait_for_metadata "DSRemoved: $now" "${basefile}.state" || log_error "bad DSRemoved in ${basefile}.state" -# DS State should be forced into UNRETENTIVE. -set_keystate "KEY1" "STATE_DS" "unretentive" -check_keys -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -# -# Zone: checkds-doubleksk.kasp. -# -key_clear "KEY1" -key_clear "KEY2" -key_clear "KEY3" -key_clear "KEY4" - -set_zone "checkds-doubleksk.kasp" -set_policy "checkds-doubleksk" "3" "303" -set_server "ns3" "10.53.0.3" -# Key properties. -set_keyrole "KEY1" "ksk" -set_keylifetime "KEY1" "0" -set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY1" "yes" -set_zonesigning "KEY1" "no" - -set_keyrole "KEY2" "ksk" -set_keylifetime "KEY2" "0" -set_keyalgorithm "KEY2" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY2" "yes" -set_zonesigning "KEY2" "no" - -set_keyrole "KEY3" "zsk" -set_keylifetime "KEY3" "0" -set_keyalgorithm "KEY3" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY3" "no" -set_zonesigning "KEY3" "yes" -# DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. -set_keystate "KEY1" "GOAL" "omnipresent" -set_keystate "KEY1" "STATE_DNSKEY" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -set_keystate "KEY2" "GOAL" "omnipresent" -set_keystate "KEY2" "STATE_DNSKEY" "rumoured" -set_keystate "KEY2" "STATE_KRRSIG" "rumoured" -set_keystate "KEY2" "STATE_DS" "hidden" - -set_keystate "KEY3" "GOAL" "omnipresent" -set_keystate "KEY3" "STATE_DNSKEY" "rumoured" -set_keystate "KEY3" "STATE_ZRRSIG" "rumoured" - -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain -dnssec_verify - -basefile1=$(key_get KEY1 BASEFILE) -basefile2=$(key_get KEY2 BASEFILE) - -n=$((n + 1)) -echo_i "checkds published does not set DSPublish for zone $ZONE (multiple KSK) ($n)" -rndc_checkds "$SERVER" "$DIR" "-" "20200102121314" "published" "$ZONE" -grep "DSPublish:" "${basefile1}.state" >/dev/null && log_error "DSPublish incorrectly set in ${basefile1}" -grep "DSPublish:" "${basefile2}.state" >/dev/null && log_error "DSPublish incorrectly set in ${basefile2}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds withdrawn does not set DSRemoved for zone $ZONE (multiple KSK) ($n)" -rndc_checkds "$SERVER" "$DIR" "-" "20190102121314" "withdrawn" "$ZONE" -grep "DSRemoved:" "${basefile1}.state" >/dev/null && log_error "DSRemoved incorrectly set in ${basefile1}" -grep "DSRemoved:" "${basefile2}.state" >/dev/null && log_error "DSRemoved incorrectly set in ${basefile2}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds published does not set DSPublish for zone $ZONE (wrong algorithm) ($n)" -rndccmd "$SERVER" dnssec -checkds -key $(key_get KEY1 ID) -alg 8 "published" "$ZONE" >rndc.dnssec.checkds.out.$ZONE.$n -grep "DSPublish:" "${basefile1}.state" >/dev/null && log_error "DSPublish incorrectly set in ${basefile1}" -grep "DSPublish:" "${basefile2}.state" >/dev/null && log_error "DSPublish incorrectly set in ${basefile2}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds withdrawn does not set DSRemoved for zone $ZONE (wrong algorithm) ($n)" -rndccmd "$SERVER" dnssec -checkds -key $(key_get KEY1 ID) -alg RSASHA256 "withdrawn" "$ZONE" >rndc.dnssec.checkds.out.$ZONE.$n -grep "DSRemoved:" "${basefile1}.state" >/dev/null && log_error "DSRemoved incorrectly set in ${basefile1}" -grep "DSRemoved:" "${basefile2}.state" >/dev/null && log_error "DSRemoved incorrectly set in ${basefile2}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds published -key correctly sets DSPublish for key $(key_get KEY1 ID) zone $ZONE (multiple KSK) ($n)" -rndc_checkds "$SERVER" "$DIR" KEY1 "20190102121314" "published" "$ZONE" -retry_quiet 3 _wait_for_metadata "DSPublish: 20190102121314" "${basefile1}.state" || log_error "bad DSPublish in ${basefile1}.state" -grep "DSPublish:" "${basefile2}.state" >/dev/null && log_error "DSPublish incorrectly set in ${basefile2}" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds withdrawn -key correctly sets DSRemoved for key $(key_get KEY2 ID) zone $ZONE (multiple KSK) ($n)" -rndc_checkds "$SERVER" "$DIR" KEY2 "20200102121314" "withdrawn" "$ZONE" -grep "DSRemoved:" "${basefile1}.state" >/dev/null && log_error "DSPublish incorrectly set in ${basefile1}" -retry_quiet 3 _wait_for_metadata "DSRemoved: 20200102121314" "${basefile2}.state" || log_error "bad DSRemoved in ${basefile2}.state" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -# -# Zone: checkds-csk.kasp. -# -key_clear "KEY1" -key_clear "KEY2" -key_clear "KEY3" -key_clear "KEY4" - -set_zone "checkds-csk.kasp" -set_policy "checkds-csk" "1" "303" -set_server "ns3" "10.53.0.3" -# 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" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_ZRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain -dnssec_verify - -basefile=$(key_get KEY1 BASEFILE) - -n=$((n + 1)) -echo_i "checkds publish correctly sets DSPublish for zone $ZONE ($n)" -rndc_checkds "$SERVER" "$DIR" "-" "20190102121314" "published" "$ZONE" -retry_quiet 3 _wait_for_metadata "DSPublish: 20190102121314" "${basefile}.state" || log_error "bad DSPublish in ${basefile}.state" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "checkds withdraw correctly sets DSRemoved for zone $ZONE ($n)" -rndc_checkds "$SERVER" "$DIR" "-" "20200102121314" "withdrawn" "$ZONE" -retry_quiet 3 _wait_for_metadata "DSRemoved: 20200102121314" "${basefile}.state" || log_error "bad DSRemoved in ${basefile}.state" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - # Set keytimes for dnssec-policy with various algorithms. # These all use the same time values. set_keytimes_algorithm_policy() { diff --git a/bin/tests/system/kasp/tests_kasp.py b/bin/tests/system/kasp/tests_kasp.py index 5fe82fa403..d3406b746a 100644 --- a/bin/tests/system/kasp/tests_kasp.py +++ b/bin/tests/system/kasp/tests_kasp.py @@ -921,6 +921,166 @@ def test_kasp_dynamic(servers): assert f"zone_resigninc: zone {zone}/IN (unsigned): enter" not in "ns3/named.run" +def test_kasp_checkds(servers): + server = servers["ns3"] + + def wait_for_metadata(): + return isctest.util.file_contents_contain(ksk.statefile, metadata) + + # Zone: checkds-ksk.kasp. + zone = "checkds-ksk.kasp" + policy = "checkds-ksk" + alg = os.environ["DEFAULT_ALGORITHM_NUMBER"] + size = os.environ["DEFAULT_BITS"] + policy_keys = [ + f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden", + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:rumoured", + ] + expected = isctest.kasp.policy_to_properties(ttl=303, keys=policy_keys) + 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 k.is_zsk()] + isctest.kasp.check_zone_is_signed(server, zone) + isctest.kasp.check_keys(zone, keys, expected) + check_all(server, zone, policy, ksks, zsks) + + now = KeyTimingMetadata.now() + ksk = ksks[0] + + isctest.log.info("check if checkds -publish correctly sets DSPublish") + server.rndc(f"dnssec -checkds -when {now} published {zone}", log=False) + metadata = f"DSPublish: {now}" + isctest.run.retry_with_timeout(wait_for_metadata, timeout=3) + expected[0].metadata["DSState"] = "rumoured" + expected[0].timing["DSPublish"] = now + isctest.kasp.check_keys(zone, keys, expected) + + isctest.log.info("check if checkds -withdrawn correctly sets DSRemoved") + server.rndc(f"dnssec -checkds -when {now} withdrawn {zone}", log=False) + metadata = f"DSRemoved: {now}" + isctest.run.retry_with_timeout(wait_for_metadata, timeout=3) + expected[0].metadata["DSState"] = "unretentive" + expected[0].timing["DSRemoved"] = now + isctest.kasp.check_keys(zone, keys, expected) + + +def test_kasp_checkds_doubleksk(servers): + server = servers["ns3"] + + def wait_for_metadata(): + return isctest.util.file_contents_contain(ksk.statefile, metadata) + + # Zone: checkds-doubleksk.kasp. + zone = "checkds-doubleksk.kasp" + policy = "checkds-doubleksk" + alg = os.environ["DEFAULT_ALGORITHM_NUMBER"] + size = os.environ["DEFAULT_BITS"] + policy_keys = [ + f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden", + f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden", + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:rumoured", + ] + expected = isctest.kasp.policy_to_properties(ttl=303, keys=policy_keys) + 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 k.is_zsk()] + isctest.kasp.check_zone_is_signed(server, zone) + isctest.kasp.check_keys(zone, keys, expected) + check_all(server, zone, policy, ksks, zsks) + + now = KeyTimingMetadata.now() + ksk = ksks[0] + + badalg = os.environ["ALTERNATIVE_ALGORITHM_NUMBER"] + isctest.log.info("check invalid checkds commands") + + def check_error(): + response = server.rndc(test["command"], log=False) + assert test["error"] in response + + test_cases = [ + { + "command": f"dnssec -checkds -when {now} published {zone}", + "error": "multiple possible keys found, retry command with -key id", + }, + { + "command": f"dnssec -checkds -when {now} withdrawn {zone}", + "error": "multiple possible keys found, retry command with -key id", + }, + { + "command": f"dnssec -checkds -when {now} -key {ksks[0].tag} -alg {badalg} published {zone}", + "error": "Error executing checkds command: no matching key found", + }, + { + "command": f"dnssec -checkds -when {now} -key {ksks[0].tag} -alg {badalg} withdrawn {zone}", + "error": "Error executing checkds command: no matching key found", + }, + ] + for test in test_cases: + check_error() + + isctest.log.info("check if checkds -publish -key correctly sets DSPublish") + server.rndc( + f"dnssec -checkds -when {now} -key {ksk.tag} published {zone}", log=False + ) + metadata = f"DSPublish: {now}" + isctest.run.retry_with_timeout(wait_for_metadata, timeout=3) + expected[0].metadata["DSState"] = "rumoured" + expected[0].timing["DSPublish"] = now + isctest.kasp.check_keys(zone, keys, expected) + + isctest.log.info("check if checkds -withdrawn -key correctly sets DSRemoved") + ksk = ksks[1] + server.rndc( + f"dnssec -checkds -when {now} -key {ksk.tag} withdrawn {zone}", log=False + ) + metadata = f"DSRemoved: {now}" + isctest.run.retry_with_timeout(wait_for_metadata, timeout=3) + expected[1].metadata["DSState"] = "unretentive" + expected[1].timing["DSRemoved"] = now + isctest.kasp.check_keys(zone, keys, expected) + + +def test_kasp_checkds_csk(servers): + server = servers["ns3"] + + def wait_for_metadata(): + return isctest.util.file_contents_contain(ksk.statefile, metadata) + + # Zone: checkds-csk.kasp. + zone = "checkds-csk.kasp" + policy = "checkds-csk" + alg = os.environ["DEFAULT_ALGORITHM_NUMBER"] + size = os.environ["DEFAULT_BITS"] + policy_keys = [ + f"csk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden", + ] + expected = isctest.kasp.policy_to_properties(ttl=303, keys=policy_keys) + keys = isctest.kasp.keydir_to_keylist(zone, "ns3") + isctest.kasp.check_zone_is_signed(server, zone) + isctest.kasp.check_keys(zone, keys, expected) + check_all(server, zone, policy, keys, []) + + now = KeyTimingMetadata.now() + ksk = keys[0] + + isctest.log.info("check if checkds -publish csk correctly sets DSPublish") + server.rndc(f"dnssec -checkds -when {now} published {zone}", log=False) + metadata = f"DSPublish: {now}" + isctest.run.retry_with_timeout(wait_for_metadata, timeout=3) + expected[0].metadata["DSState"] = "rumoured" + expected[0].timing["DSPublish"] = now + isctest.kasp.check_keys(zone, keys, expected) + + isctest.log.info("check if checkds -withdrawn csk correctly sets DSRemoved") + server.rndc(f"dnssec -checkds -when {now} withdrawn {zone}", log=False) + metadata = f"DSRemoved: {now}" + isctest.run.retry_with_timeout(wait_for_metadata, timeout=3) + expected[0].metadata["DSState"] = "unretentive" + expected[0].timing["DSRemoved"] = now + isctest.kasp.check_keys(zone, keys, expected) + + def test_kasp_special_characters(servers): server = servers["ns3"] From dc74bc8051b81085d4040f4dddb3b79b86027a99 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 17 Mar 2025 17:09:08 +0100 Subject: [PATCH 3/4] Convert reload/restart kasp test case This test checks that the SOA SERIAL and TTL are adjusted correctly after a reload/restart. (cherry picked from commit bff7453e50b38ce1adf556beac2dcb1a80665f6b) --- bin/tests/system/kasp/tests.sh | 44 --------------------- bin/tests/system/kasp/tests_kasp.py | 59 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 44 deletions(-) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 228ef50cba..0ec26f3eff 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -3661,49 +3661,5 @@ dnssec_verify # an unlimited lifetime. Fallback to the default loadkeys interval. check_next_key_event 3600 -_check_soa_ttl() { - dig_with_opts @10.53.0.6 example SOA >dig.out.ns6.test$n.soa2 || return 1 - soa1=$(awk '$4 == "SOA" { print $7 }' dig.out.ns6.test$n.soa1) - soa2=$(awk '$4 == "SOA" { print $7 }' dig.out.ns6.test$n.soa2) - ttl1=$(awk '$4 == "SOA" { print $2 }' dig.out.ns6.test$n.soa1) - ttl2=$(awk '$4 == "SOA" { print $2 }' dig.out.ns6.test$n.soa2) - test ${soa1:-1000} -lt ${soa2:-0} || return 1 - test ${ttl1:-0} -eq $1 || return 1 - test ${ttl2:-0} -eq $2 || return 1 -} - -n=$((n + 1)) -echo_i "Check that 'rndc reload' of just the serial updates the signed instance ($n)" -TSIG= -ret=0 -dig_with_opts @10.53.0.6 example SOA >dig.out.ns6.test$n.soa1 || ret=1 -cp ns6/example2.db.in ns6/example.db || ret=1 -nextpart ns6/named.run >/dev/null -rndccmd 10.53.0.6 reload || ret=1 -wait_for_log 3 "all zones loaded" ns6/named.run || ret=1 -# Check that the SOA SERIAL increases and check the TTLs (should be 300 as -# defined in ns6/example2.db.in). -retry_quiet 10 _check_soa_ttl 300 300 || ret=1 -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -n=$((n + 1)) -echo_i "Check that restart with zone changes and deleted journal works ($n)" -TSIG= -ret=0 -dig_with_opts @10.53.0.6 example SOA >dig.out.ns6.test$n.soa1 || ret=1 -stop_server --use-rndc --port ${CONTROLPORT} ns6 -# TTL of all records change from 300 to 400 -cp ns6/example3.db.in ns6/example.db || ret=1 -rm ns6/example.db.jnl -nextpart ns6/named.run >/dev/null -start_server --noclean --restart --port ${PORT} ns6 -wait_for_log 3 "all zones loaded" ns6/named.run || ret=1 -# Check that the SOA SERIAL increases and check the TTLs (should be changed -# from 300 to 400 as defined in ns6/example3.db.in). -retry_quiet 10 _check_soa_ttl 300 400 || ret=1 -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/kasp/tests_kasp.py b/bin/tests/system/kasp/tests_kasp.py index d3406b746a..aafc589449 100644 --- a/bin/tests/system/kasp/tests_kasp.py +++ b/bin/tests/system/kasp/tests_kasp.py @@ -1446,3 +1446,62 @@ def test_kasp_zsk_retired(servers): msg = f"zone {zone}/IN (signed): zone_rekey:zone_verifykeys failed: some key files are missing" server.log.prohibit(msg) + + +def test_kasp_reload_restart(servers): + server = servers["ns6"] + zone = "example" + + def query_soa(qname): + fqdn = dns.name.from_text(qname) + qtype = dns.rdatatype.SOA + query = dns.message.make_query(fqdn, qtype, use_edns=True, want_dnssec=True) + try: + response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3) + except dns.exception.Timeout: + isctest.log.debug(f"query timeout for query {qname} SOA to {server.ip}") + return 0, 0 + + assert response.rcode() == dns.rcode.NOERROR + + for rr in response.answer: + if rr.match(fqdn, dns.rdataclass.IN, dns.rdatatype.RRSIG, qtype): + continue + + assert rr.match(fqdn, dns.rdataclass.IN, qtype, dns.rdatatype.NONE) + assert len(rr) == 1 + return rr[0].serial, rr.ttl + + return 0, 0 + + def check_soa_ttl(): + soa2, ttl2 = query_soa(zone) + return soa1 < soa2 and ttl2 == newttl + + # Check that the SOA SERIAL increases and check the TTLs (should be 300 as + # defined in ns6/example2.db.in). + soa1, ttl1 = query_soa(zone) + assert ttl1 == 300 + + shutil.copyfile(f"ns6/{zone}2.db.in", f"ns6/{zone}.db") + with server.watch_log_from_here() as watcher: + server.rndc("reload", log=False) + watcher.wait_for_line("all zones loaded") + + newttl = 300 + isctest.run.retry_with_timeout(check_soa_ttl, timeout=10) + + # Check that the SOA SERIAL increases and check the TTLs (should be changed + # from 300 to 400 as defined in ns6/example3.db.in). + soa1, ttl1 = query_soa(zone) + assert ttl1 == 300 + + server.stop() + shutil.copyfile(f"ns6/{zone}3.db.in", f"ns6/{zone}.db") + os.unlink(f"ns6/{zone}.db.jnl") + with server.watch_log_from_here() as watcher: + server.start(["--noclean", "--restart", "--port", os.environ["PORT"]]) + watcher.wait_for_line("all zones loaded") + + newttl = 400 + isctest.run.retry_with_timeout(check_soa_ttl, timeout=10) From ca0ae034882068631b358d55fbc5877cf40bebfd Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 17 Mar 2025 17:16:29 +0100 Subject: [PATCH 4/4] Convert kasp inheritance tests These tests ensure that if dnssec-policy is set on a higher level, the zone is still signed (or unsigned) as expected. Or if a higher level has an override, the new policy is honored as expected. (cherry picked from commit 2e4cc706267d08a515adaca1f1bb863600a4e938) --- bin/tests/system/kasp/tests.sh | 349 ---------------------------- bin/tests/system/kasp/tests_kasp.py | 139 +++++++++++ 2 files changed, 139 insertions(+), 349 deletions(-) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 0ec26f3eff..441faaad3d 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -206,355 +206,6 @@ set_keytimes_autosign_policy() { set_addkeytime "KEY2" "REMOVED" "${retired}" 695100 } -# -# Test dnssec-policy inheritance. -# - -# These zones should be unsigned: -# ns2/unsigned.tld -# ns4/none.inherit.signed -# ns4/none.override.signed -# ns4/inherit.none.signed -# ns4/none.none.signed -# ns5/inherit.inherit.unsigned -# ns5/none.inherit.unsigned -# ns5/none.override.unsigned -# ns5/inherit.none.unsigned -# ns5/none.none.unsigned -key_clear "KEY1" -key_clear "KEY2" -key_clear "KEY3" -key_clear "KEY4" - -set_zone "unsigned.tld" -set_policy "none" "0" "0" -set_server "ns2" "10.53.0.2" -TSIG="" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "none.inherit.signed" -set_policy "none" "0" "0" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha1:sha1:$SHA1" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "none.override.signed" -set_policy "none" "0" "0" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha224:sha224:$SHA224" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "inherit.none.signed" -set_policy "none" "0" "0" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha256:sha256:$SHA256" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "none.none.signed" -set_policy "none" "0" "0" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha256:sha256:$SHA256" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "inherit.inherit.unsigned" -set_policy "none" "0" "0" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha1:sha1:$SHA1" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "none.inherit.unsigned" -set_policy "none" "0" "0" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha1:sha1:$SHA1" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "none.override.unsigned" -set_policy "none" "0" "0" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha224:sha224:$SHA224" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "inherit.none.unsigned" -set_policy "none" "0" "0" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha256:sha256:$SHA256" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -set_zone "none.none.unsigned" -set_policy "none" "0" "0" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha256:sha256:$SHA256" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -check_apex -check_subdomain - -# These zones should be signed with the default policy: -# ns2/signed.tld -# ns4/override.inherit.signed -# ns4/inherit.override.signed -# ns5/override.inherit.signed -# ns5/inherit.override.signed -set_keyrole "KEY1" "csk" -set_keylifetime "KEY1" "0" -set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" -set_keysigning "KEY1" "yes" -set_zonesigning "KEY1" "yes" - -set_keystate "KEY1" "GOAL" "omnipresent" -set_keystate "KEY1" "STATE_DNSKEY" "rumoured" -set_keystate "KEY1" "STATE_KRRSIG" "rumoured" -set_keystate "KEY1" "STATE_ZRRSIG" "rumoured" -set_keystate "KEY1" "STATE_DS" "hidden" - -set_zone "signed.tld" -set_policy "default" "1" "3600" -set_server "ns2" "10.53.0.2" -TSIG="" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "override.inherit.signed" -set_policy "default" "1" "3600" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha1:sha1:$SHA1" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "inherit.override.signed" -set_policy "default" "1" "3600" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha224:sha224:$SHA224" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "override.inherit.unsigned" -set_policy "default" "1" "3600" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha1:sha1:$SHA1" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "inherit.override.unsigned" -set_policy "default" "1" "3600" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha224:sha224:$SHA224" -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -# These zones should be signed with the test policy: -# ns4/inherit.inherit.signed -# ns4/override.override.signed -# ns4/override.none.signed -# ns5/override.override.unsigned -# ns5/override.none.unsigned -# ns4/example.net (both views) -set_keyrole "KEY1" "csk" -set_keylifetime "KEY1" "0" -set_keyalgorithm "KEY1" "14" "ECDSAP384SHA384" "384" -set_keysigning "KEY1" "yes" -set_zonesigning "KEY1" "yes" - -set_zone "inherit.inherit.signed" -set_policy "test" "1" "3600" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha1:sha1:$SHA1" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "override.override.signed" -set_policy "test" "1" "3600" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha224:sha224:$SHA224" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "override.none.signed" -set_policy "test" "1" "3600" -set_server "ns4" "10.53.0.4" -TSIG="hmac-sha256:sha256:$SHA256" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "override.override.unsigned" -set_policy "test" "1" "3600" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha224:sha224:$SHA224" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -set_zone "override.none.unsigned" -set_policy "test" "1" "3600" -set_server "ns5" "10.53.0.5" -TSIG="hmac-sha256:sha256:$SHA256" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" -set_keytimes_csk_policy -check_keytimes -check_apex -check_subdomain -dnssec_verify - -# Test with views. -set_zone "example.net" -set_server "ns4" "10.53.0.4" -TSIG="$DEFAULT_HMAC:keyforview1:$VIEW1" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" "example1" -set_keytimes_csk_policy -check_keytimes -check_apex -dnssec_verify -# check zonestatus -n=$((n + 1)) -echo_i "check $ZONE (view example1) zonestatus ($n)" -ret=0 -check_isdynamic "$SERVER" "$ZONE" "example1" || log_error "zone not dynamic" -check_inlinesigning "$SERVER" "$ZONE" "example1" && log_error "inline-signing enabled, expected disabled" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) -# check subdomain -n=$((n + 1)) -echo_i "check TXT example.net (view example1) rrset is signed correctly ($n)" -ret=0 -dig_with_opts "view.${ZONE}" "@${SERVER}" TXT >"dig.out.$DIR.test$n.txt" || log_error "dig view.${ZONE} TXT failed" -grep "status: NOERROR" "dig.out.$DIR.test$n.txt" >/dev/null || log_error "mismatch status in DNS response" -grep "view.${ZONE}\..*${DEFAULT_TTL}.*IN.*TXT.*view1" "dig.out.$DIR.test$n.txt" >/dev/null || log_error "missing view.${ZONE} TXT record in response" -check_signatures TXT "dig.out.$DIR.test$n.txt" "ZSK" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -TSIG="$DEFAULT_HMAC:keyforview2:$VIEW2" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" "example2" -check_apex -dnssec_verify -# check zonestatus -n=$((n + 1)) -echo_i "check $ZONE (view example2) zonestatus ($n)" -ret=0 -check_isdynamic "$SERVER" "$ZONE" "example2" && log_error "zone dynamic, but not expected" -check_inlinesigning "$SERVER" "$ZONE" "example2" || log_error "inline-signing disabled, expected enabled" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) -# check subdomain -n=$((n + 1)) -echo_i "check TXT example.net (view example2) rrset is signed correctly ($n)" -ret=0 -dig_with_opts "view.${ZONE}" "@${SERVER}" TXT >"dig.out.$DIR.test$n.txt" || log_error "dig view.${ZONE} TXT failed" -grep "status: NOERROR" "dig.out.$DIR.test$n.txt" >/dev/null || log_error "mismatch status in DNS response" -grep "view.${ZONE}\..*${DEFAULT_TTL}.*IN.*TXT.*view2" "dig.out.$DIR.test$n.txt" >/dev/null || log_error "missing view.${ZONE} TXT record in response" -check_signatures TXT "dig.out.$DIR.test$n.txt" "ZSK" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -TSIG="$DEFAULT_HMAC:keyforview3:$VIEW3" -wait_for_nsec -check_keys -check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" "example3" -check_apex -dnssec_verify -# check zonestatus -n=$((n + 1)) -echo_i "check $ZONE (view example3) zonestatus ($n)" -ret=0 -check_isdynamic "$SERVER" "$ZONE" "example3" && log_error "zone dynamic, but not expected" -check_inlinesigning "$SERVER" "$ZONE" "example3" || log_error "inline-signing disabled, expected enabled" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) -# check subdomain -n=$((n + 1)) -echo_i "check TXT example.net (view example3) rrset is signed correctly ($n)" -ret=0 -dig_with_opts "view.${ZONE}" "@${SERVER}" TXT >"dig.out.$DIR.test$n.txt" || log_error "dig view.${ZONE} TXT failed" -grep "status: NOERROR" "dig.out.$DIR.test$n.txt" >/dev/null || log_error "mismatch status in DNS response" -grep "view.${ZONE}\..*${DEFAULT_TTL}.*IN.*TXT.*view2" "dig.out.$DIR.test$n.txt" >/dev/null || log_error "missing view.${ZONE} TXT record in response" -check_signatures TXT "dig.out.$DIR.test$n.txt" "ZSK" -test "$ret" -eq 0 || echo_i "failed" -status=$((status + ret)) - -# Clear TSIG. -TSIG="" - # # Testing RFC 8901 Multi-Signer Model 2. # diff --git a/bin/tests/system/kasp/tests_kasp.py b/bin/tests/system/kasp/tests_kasp.py index aafc589449..178e602f48 100644 --- a/bin/tests/system/kasp/tests_kasp.py +++ b/bin/tests/system/kasp/tests_kasp.py @@ -26,6 +26,7 @@ from isctest.kasp import ( KeyProperties, KeyTimingMetadata, ) +from isctest.vars.algorithms import ECDSAP256SHA256, ECDSAP384SHA384 pytestmark = pytest.mark.extra_artifacts( [ @@ -116,6 +117,21 @@ lifetime = { "P6M": int(timedelta(days=31 * 6).total_seconds()), } +KASP_INHERIT_TSIG_SECRET = { + "sha1": "FrSt77yPTFx6hTs4i2tKLB9LmE0=", + "sha224": "hXfwwwiag2QGqblopofai9NuW28q/1rH4CaTnA==", + "sha256": "R16NojROxtxH/xbDl//ehDsHm5DjWTQ2YXV+hGC2iBY=", + "view1": "YPfMoAk6h+3iN8MDRQC004iSNHY=", + "view2": "4xILSZQnuO1UKubXHkYUsvBRPu8=", + "view3": "C1Azf+gGPMmxrUg/WQINP6eV9Y0=", +} + + +def param(*args, **kwargs): + if "id" not in kwargs: + kwargs["id"] = args[0] # use first argument as test ID + return pytest.param(*args, **kwargs) + def autosign_properties(alg, size): return [ @@ -685,6 +701,129 @@ def test_kasp_case(servers, params): callback(*arguments, params=params, ksks=ksks, zsks=zsks) +@pytest.mark.parametrize( + "zone, server_id, tsig_kind", + [ + param("unsigned.tld", "ns2", None), + param("none.inherit.signed", "ns4", "sha1"), + param("none.override.signed", "ns4", "sha224"), + param("inherit.none.signed", "ns4", "sha256"), + param("none.none.signed", "ns4", "sha256"), + param("inherit.inherit.unsigned", "ns5", "sha1"), + param("none.inherit.unsigned", "ns5", "sha1"), + param("none.override.unsigned", "ns5", "sha224"), + param("inherit.none.unsigned", "ns5", "sha256"), + param("none.none.unsigned", "ns5", "sha256"), + ], +) +def test_kasp_inherit_unsigned(zone, server_id, tsig_kind, servers): + server = servers[server_id] + tsig = ( + f"hmac-{tsig_kind}:{tsig_kind}:{KASP_INHERIT_TSIG_SECRET[tsig_kind]}" + if tsig_kind + else None + ) + + keys = isctest.kasp.keydir_to_keylist(zone, server.identifier) + isctest.kasp.check_keys(zone, keys, []) + isctest.kasp.check_dnssecstatus(server, zone, []) + isctest.kasp.check_apex(server, zone, [], [], tsig=tsig) + isctest.kasp.check_subdomain(server, zone, [], [], tsig=tsig) + + +@pytest.mark.parametrize( + "zone, policy, server_id, alg, tsig_kind", + [ + param("signed.tld", "default", "ns2", ECDSAP256SHA256, None), + param("override.inherit.signed", "default", "ns4", ECDSAP256SHA256, "sha1"), + param("inherit.override.signed", "default", "ns4", ECDSAP256SHA256, "sha224"), + param("override.inherit.unsigned", "default", "ns5", ECDSAP256SHA256, "sha1"), + param("inherit.override.unsigned", "default", "ns5", ECDSAP256SHA256, "sha224"), + param("inherit.inherit.signed", "test", "ns4", ECDSAP384SHA384, "sha1"), + param("override.override.signed", "test", "ns4", ECDSAP384SHA384, "sha224"), + param("override.none.signed", "test", "ns4", ECDSAP384SHA384, "sha256"), + param("override.override.unsigned", "test", "ns5", ECDSAP384SHA384, "sha224"), + param("override.none.unsigned", "test", "ns5", ECDSAP384SHA384, "sha256"), + ], +) +def test_kasp_inherit_signed(zone, policy, server_id, alg, tsig_kind, servers): + server = servers[server_id] + tsig = ( + f"hmac-{tsig_kind}:{tsig_kind}:{KASP_INHERIT_TSIG_SECRET[tsig_kind]}" + if tsig_kind + else None + ) + + key1 = KeyProperties.default() + key1.metadata["Algorithm"] = alg.number + key1.metadata["Length"] = alg.bits + keys = isctest.kasp.keydir_to_keylist(zone, server.identifier) + + isctest.kasp.check_zone_is_signed(server, zone, tsig=tsig) + isctest.kasp.check_keys(zone, keys, [key1]) + set_keytimes_default_policy(key1) + isctest.kasp.check_keytimes(keys, [key1]) + check_all(server, zone, policy, keys, [], tsig=tsig) + + +@pytest.mark.parametrize( + "number, dynamic, inline_signing, txt_rdata", + [ + param("1", "yes", "no", "view1"), + param("2", "no", "yes", "view2"), + param("3", "no", "yes", "view2"), + ], +) +def test_kasp_inherit_view(number, dynamic, inline_signing, txt_rdata, servers): + zone = "example.net" + policy = "test" + server = servers["ns4"] + view = f"example{number}" + tsig = f"{os.environ['DEFAULT_HMAC']}:keyforview{number}:{KASP_INHERIT_TSIG_SECRET[f'view{number}']}" + + key1 = KeyProperties.default() + key1.metadata["Algorithm"] = ECDSAP384SHA384.number + key1.metadata["Length"] = ECDSAP384SHA384.bits + keys = isctest.kasp.keydir_to_keylist(zone, server.identifier) + + isctest.kasp.check_zone_is_signed(server, zone, tsig=tsig) + isctest.kasp.check_keys(zone, keys, [key1]) + set_keytimes_default_policy(key1) + isctest.kasp.check_keytimes(keys, [key1]) + isctest.kasp.check_dnssecstatus(server, zone, keys, policy=policy, view=view) + isctest.kasp.check_apex(server, zone, keys, [], tsig=tsig) + # check zonestatus + response = server.rndc(f"zonestatus {zone} in {view}", log=False) + assert f"dynamic: {dynamic}" in response + assert f"inline signing: {inline_signing}" in response + # check subdomain + fqdn = f"{zone}." + qname = f"view.{zone}." + qtype = dns.rdatatype.TXT + rdata = txt_rdata + query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True) + tsigkey = tsig.split(":") + keyring = dns.tsig.Key(tsigkey[1], tsigkey[2], tsigkey[0]) + query.use_tsig(keyring) + try: + response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3) + except dns.exception.Timeout: + isctest.log.debug(f"query timeout for query {qname} {qtype} to {server.ip}") + response = None + assert response.rcode() == dns.rcode.NOERROR + match = f'{qname} 300 IN TXT "{rdata}"' + rrsigs = [] + for rrset in response.answer: + if rrset.match( + dns.name.from_text(qname), dns.rdataclass.IN, dns.rdatatype.RRSIG, qtype + ): + rrsigs.append(rrset) + else: + assert match in rrset.to_text() + assert len(rrsigs) > 0 + isctest.kasp.check_signatures(rrsigs, qtype, fqdn, keys, []) + + def test_kasp_default(servers): server = servers["ns3"]