From 2e4cc706267d08a515adaca1f1bb863600a4e938 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 17 Mar 2025 17:16:29 +0100 Subject: [PATCH] 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. --- 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 2bad425b39..c1b33ddb08 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"]