]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Convert model2.secondary test to pytest
authorMatthijs Mekking <matthijs@isc.org>
Sun, 12 Oct 2025 09:49:00 +0000 (11:49 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 28 Nov 2025 14:30:31 +0000 (14:30 +0000)
This test is similar to model2.multisigner, but now the two providers
are both secondary, both using the same hidden primary. The DNSKEY,
CDNSKEY, and CDS records need to be published at the hidden primary,
ns5, the zone is transferred to both secondaries, ns3 and ns4.

To avoid intermittent test failures, we wait for the line
"zone {zone}/IN (signed): serial {serial2} (unsigned {serial1})" in
the secondary server logs. This is a signal that the unsigned zone
with serial <serial1> has a signed version ready with serial <serial2>.

To speed up the test, disable 'notify-delay'.

bin/tests/system/multisigner/ns5/named.conf.in
bin/tests/system/multisigner/tests.sh
bin/tests/system/multisigner/tests_multisigner.py

index 0a8e4f556c684aa664cbef9c83c208075b10f82d..eeae1309c5e5661e4f75d89f545d9920bdfb5876 100644 (file)
@@ -27,6 +27,7 @@ options {
        recursion no;
        key-directory ".";
        dnssec-validation no;
+       notify-delay 0;
 };
 
 key rndc_key {
index c0141e8d8533c29d7d08450776861d94250bd605..ee06afa7d4c4ae5c4a70d5824adc750af9bea530 100644 (file)
@@ -58,299 +58,5 @@ records_published() {
   test "$lines" -eq "$_expect" || return 1
 }
 
-#
-# Check secondary server behaviour.
-#
-set_zone "model2.secondary"
-set_policy "model2" "2" "3600"
-
-set_server "ns3" "10.53.0.3"
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_model2
-check_keytimes
-check_apex
-dnssec_verify
-
-set_server "ns4" "10.53.0.4"
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_model2
-check_keytimes
-check_apex
-dnssec_verify
-
-#
-# Update DNSKEY RRset.
-#
-n=$((n + 1))
-echo_i "add dnskey record: update zone ${ZONE} at ns5 with ZSKs from providers ns3 and ns4 ($n)"
-ret=0
-set_server "ns5" "10.53.0.5"
-(
-  echo zone "${ZONE}"
-  echo server "${SERVER}" "${PORT}"
-  echo update add $(cat "ns3/${ZONE}.zsk")
-  echo update add $(cat "ns4/${ZONE}.zsk")
-  echo send
-) | $NSUPDATE || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# NS3
-n=$((n + 1))
-set_server "ns3" "10.53.0.3"
-echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)"
-ret=0
-retry_quiet 10 zsks_are_published || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-grep "dns_zone_findkeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# NS4
-n=$((n + 1))
-set_server "ns4" "10.53.0.4"
-echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)"
-ret=0
-retry_quiet 10 zsks_are_published || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-grep "dns_zone_findkeys: error reading ./K${ZONE}.*\.private: file not found" "${DIR}/named.run" && ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "remove dnskey record: remove ns3 and ns4 DNSKEY records from primary ns5 ($n)"
-ret=0
-set_server "ns5" "10.53.0.5"
-(
-  echo zone "${ZONE}"
-  echo server "${SERVER}" "${PORT}"
-  echo update del $(cat "ns3/${ZONE}.zsk")
-  echo update del $(cat "ns4/${ZONE}.zsk")
-  echo send
-) | $NSUPDATE || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# Now there should be one DNSKEY record again.
-# While we did remove both DNSKEY records, the bump in the wire signer, i.e
-# the secondary inline-signing zone, should add back the DNSKEY belonging to
-# its own KSK when re-signing the zone.
-#
-# NS3
-n=$((n + 1))
-set_server "ns3" "10.53.0.3"
-echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)"
-ret=0
-check_keys
-check_apex
-dnssec_verify
-no_dnssec_in_journal
-# NS4
-n=$((n + 1))
-set_server "ns4" "10.53.0.4"
-echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)"
-ret=0
-check_keys
-check_apex
-dnssec_verify
-no_dnssec_in_journal
-
-#
-# Update CDNSKEY RRset.
-#
-
-# Retrieve CDNSKEY records from the providers.
-n=$((n + 1))
-echo_i "check initial CDSNKEY response for zone ${ZONE} at ns3 and ns4 ($n)"
-ret=0
-dig_with_opts ${ZONE} @10.53.0.3 CDNSKEY >dig.out.ns3.secondary.cdnskey
-awk '$4 == "CDNSKEY" {print}' dig.out.ns3.secondary.cdnskey >secondary.cdnskey.ns3
-dig_with_opts ${ZONE} @10.53.0.4 CDNSKEY >dig.out.ns4.secondary.cdnskey
-awk '$4 == "CDNSKEY" {print}' dig.out.ns4.secondary.cdnskey >secondary.cdnskey.ns4
-# Initially there should be one CDNSKEY.
-set_server "ns3" "10.53.0.3"
-retry_quiet 10 records_published CDNSKEY 1 || ret=1
-set_server "ns4" "10.53.0.4"
-retry_quiet 10 records_published CDNSKEY 1 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "add cdnskey record: update zone ${ZONE} at ns5 with CDNSKEY records from providers ns3 and ns4 ($n)"
-ret=0
-set_server "ns5" "10.53.0.5"
-(
-  echo zone "${ZONE}"
-  echo server "${SERVER}" "${PORT}"
-  echo update add $(cat "secondary.cdnskey.ns3")
-  echo update add $(cat "secondary.cdnskey.ns4")
-  echo send
-) | $NSUPDATE || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# Now there should be two CDNSKEY records (we test that BIND does not
-# skip it during DNSSEC maintenance).
-#
-# NS3
-n=$((n + 1))
-set_server "ns3" "10.53.0.3"
-echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDNSKEY 2 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-# NS4
-n=$((n + 1))
-set_server "ns4" "10.53.0.4"
-echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDNSKEY 2 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-
-n=$((n + 1))
-echo_i "remove cdnskey record: remove ns3 and ns4 CDNSKEY records from primary ns5 ($n)"
-ret=0
-set_server "ns5" "10.53.0.5"
-(
-  echo zone "${ZONE}"
-  echo server "${SERVER}" "${PORT}"
-  echo update del $(cat "secondary.cdnskey.ns3")
-  echo update del $(cat "secondary.cdnskey.ns4")
-  echo send
-) | $NSUPDATE || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# Now there should be one CDNSKEY record again.
-# While we did remove both CDNSKEY records, the bump in the wire signer, i.e
-# the secondary inline-signing zone, should add back the CDNSKEY belonging to
-# its own KSK when re-signing the zone.
-#
-# NS3
-n=$((n + 1))
-set_server "ns3" "10.53.0.3"
-echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDNSKEY 1 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-# NS4
-n=$((n + 1))
-set_server "ns4" "10.53.0.4"
-echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDNSKEY 1 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-
-#
-# Update CDS RRset.
-#
-
-# Retrieve CDS records from the other provider.
-n=$((n + 1))
-echo_i "check initial CDS response for zone ${ZONE} at ns3 and ns4 ($n)"
-ret=0
-dig_with_opts ${ZONE} @10.53.0.3 CDS >dig.out.ns3.secondary.cds
-awk '$4 == "CDS" {print}' dig.out.ns3.secondary.cds >secondary.cds.ns3
-dig_with_opts ${ZONE} @10.53.0.4 CDS >dig.out.ns4.secondary.cds
-awk '$4 == "CDS" {print}' dig.out.ns4.secondary.cds >secondary.cds.ns4
-# Initially there should be one CDS.
-set_server "ns3" "10.53.0.3"
-retry_quiet 10 records_published CDS 1 || ret=1
-set_server "ns4" "10.53.0.4"
-retry_quiet 10 records_published CDS 1 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "add cds record: update zone ${ZONE} at ns5 with CDS from provider ns4 ($n)"
-ret=0
-set_server "ns5" "10.53.0.5"
-(
-  echo zone "${ZONE}"
-  echo server "${SERVER}" "${PORT}"
-  echo update add $(cat "secondary.cds.ns3")
-  echo update add $(cat "secondary.cds.ns4")
-  echo send
-) | $NSUPDATE || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# Now there should be two CDS records (we test that BIND does not
-# skip it during DNSSEC maintenance).
-#
-# NS3
-n=$((n + 1))
-set_server "ns3" "10.53.0.3"
-echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDS 2 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-# NS4
-n=$((n + 1))
-set_server "ns4" "10.53.0.4"
-echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDS 2 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-
-n=$((n + 1))
-echo_i "remove cds record: remove ns3 and ns4 CDS records from primary ns5 ($n)"
-ret=0
-set_server "ns5" "10.53.0.5"
-(
-  echo zone "${ZONE}"
-  echo server "${SERVER}" "${PORT}"
-  echo update del $(cat "secondary.cds.ns3")
-  echo update del $(cat "secondary.cds.ns4")
-  echo send
-) | $NSUPDATE || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-# Now there should be one CDS record again.
-# While we did remove both CDS records, the bump in the wire signer, i.e
-# the secondary inline-signing zone, should add back the CDS belonging to
-# its own KSK when re-signing the zone.
-#
-# NS3
-n=$((n + 1))
-set_server "ns3" "10.53.0.3"
-echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDS 1 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-# NS4
-n=$((n + 1))
-set_server "ns4" "10.53.0.4"
-echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)"
-ret=0
-retry_quiet 10 records_published CDS 1 || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-dnssec_verify
-no_dnssec_in_journal
-
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1
index 2e9e5cd588b0a45d1dfebecd54bc5c26134b4af2..80288aee6da8c70b79166f8abf3f82cab85a3b37 100644 (file)
@@ -111,6 +111,47 @@ def check_no_dnssec_in_journal(server, zone):
     assert not match, f"{match.group(1)} record found in journal"
 
 
+def wait_for_serial(primary, server, zone):
+    if primary.identifier == server.identifier:
+        # No need to check if the transfer has been done.
+        return
+
+    def check_serial():
+        response = isctest.query.tcp(
+            query, primary.ip, primary.ports.dns, timeout=3, attempts=1
+        )
+        assert response.rcode() == dns.rcode.NOERROR
+        soa = response.get_rrset(
+            response.answer,
+            dns.name.from_text(fqdn),
+            dns.rdataclass.IN,
+            dns.rdatatype.SOA,
+        )
+        serial1 = soa[0].serial
+
+        response = isctest.query.tcp(
+            query, server.ip, server.ports.dns, timeout=3, attempts=1
+        )
+        assert response.rcode() == dns.rcode.NOERROR
+        soa = response.get_rrset(
+            response.answer,
+            dns.name.from_text(fqdn),
+            dns.rdataclass.IN,
+            dns.rdatatype.SOA,
+        )
+        serial2 = soa[0].serial
+
+        return (
+            f"zone {zone}/IN (signed): serial {serial2} (unsigned {serial1})"
+            in server.log
+        )
+
+    fqdn = f"{zone}."
+    query = isctest.query.create(fqdn, dns.rdatatype.SOA)
+
+    isctest.run.retry_with_timeout(check_serial, timeout=10)
+
+
 def check_add_zsk(server, zone, keys, expected, extra_keys, extra, primary=None):
     if primary is None:
         primary = server
@@ -128,6 +169,8 @@ def check_add_zsk(server, zone, keys, expected, extra_keys, extra, primary=None)
         update_msg.add(f"{zone}.", TTL, "DNSKEY", rdata)
     primary.nsupdate(update_msg)
 
+    wait_for_serial(primary, server, zone)
+
     # Check the new DNSKEY RRset.
     isctest.log.info(
         f"- zone {zone} {server.identifier}: check DNSKEY RRset after update add"
@@ -213,6 +256,8 @@ def check_remove_zsk(
         update_msg.delete(f"{zone}.", "DNSKEY", rdata)
     primary.nsupdate(update_msg)
 
+    wait_for_serial(primary, server, zone)
+
     # We should have only the KSK and ZSK from server.
     isctest.log.info(
         f"- zone {zone} {server.identifier}: check DNSKEY RRset after update remove"
@@ -247,6 +292,8 @@ def check_add_cdnskey(server, zone, keys, expected, extra_keys, extra, primary=N
         update_msg.add(f"{zone}.", TTL, "CDNSKEY", rdata)
     primary.nsupdate(update_msg)
 
+    wait_for_serial(primary, server, zone)
+
     # Now there should be two CDNSKEY records.
     isctest.log.info(
         f"- zone {zone} {server.identifier}: check CDNSKEY RRset after update add"
@@ -325,6 +372,8 @@ def check_remove_cdnskey(
         update_msg.delete(f"{zone}.", "CDNSKEY", rdata)
     primary.nsupdate(update_msg)
 
+    wait_for_serial(primary, server, zone)
+
     # Now there should be one CDNSKEY record again.
     isctest.log.info(
         f"- zone {zone} {server.identifier}: check CDNSKEY RRset after update remove"
@@ -359,6 +408,8 @@ def check_add_cds(server, zone, keys, expected, extra_keys, extra, primary=None)
         update_msg.add(f"{zone}.", TTL, "CDS", rdata)
     primary.nsupdate(update_msg)
 
+    wait_for_serial(primary, server, zone)
+
     # Now there should be two CDS records.
     isctest.log.info(
         f"- zone {zone} {server.identifier}: check CDS RRset after update add"
@@ -437,6 +488,8 @@ def check_remove_cds(
         update_msg.delete(f"{zone}.", "CDS", rdata)
     primary.nsupdate(update_msg)
 
+    wait_for_serial(primary, server, zone)
+
     # Now there should be one CDS record again.
     isctest.log.info(
         f"- zone {zone} {server.identifier}: check CDS RRset after update remove"
@@ -522,3 +575,76 @@ def test_multisigner(ns3, ns4):
     check_remove_cds(ns3, zone, keys3, expected3, [ksks4[0]], extra, check_fail=True)
     check_remove_cds(ns4, zone, keys4, expected4, [ksks3[0]], extra, check_fail=True)
     check_no_dnssec_in_journal(ns4, zone)
+
+
+def test_multisigner_secondary(ns3, ns4, ns5):
+    zone = "model2.secondary"
+    keyprops = [
+        f"ksk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
+        f"zsk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent",
+    ]
+
+    # First make sure the zone is properly signed.
+    isctest.log.info(f"basic DNSSEC tests for {zone}")
+    isctest.kasp.wait_keymgr_done(ns3, zone)
+    isctest.kasp.wait_keymgr_done(ns4, zone)
+
+    keys3 = isctest.kasp.keydir_to_keylist(zone, ns3.identifier)
+    ksks3 = [k for k in keys3 if k.is_ksk()]
+    zsks3 = [k for k in keys3 if not k.is_ksk()]
+    expected3 = isctest.kasp.policy_to_properties(ttl=TTL, keys=keyprops)
+
+    check_dnssec(ns3, zone, keys3, expected3)
+
+    keys4 = isctest.kasp.keydir_to_keylist(zone, ns4.identifier)
+    ksks4 = [k for k in keys4 if k.is_ksk()]
+    zsks4 = [k for k in keys4 if not k.is_ksk()]
+    expected4 = isctest.kasp.policy_to_properties(ttl=TTL, keys=keyprops)
+
+    check_dnssec(ns4, zone, keys4, expected4)
+
+    # Add DNSKEY to RRset.
+    newprops = [f"zsk unlimited {ALGORITHM} {SIZE}"]
+    extra = isctest.kasp.policy_to_properties(ttl=TTL, keys=newprops)
+    extra[0].private = False  # noqa
+    extra[0].legacy = True  # noqa
+
+    check_add_zsk(ns3, zone, keys3, expected3, [zsks4[0]], extra, primary=ns5)
+    check_add_zsk(ns4, zone, keys4, expected4, [zsks3[0]], extra, primary=ns5)
+    check_no_dnssec_in_journal(ns3, zone)
+    check_no_dnssec_in_journal(ns4, zone)
+
+    # Remove DNSKEY from RRset.
+    check_remove_zsk(ns3, zone, keys3, expected3, [zsks4[0]], extra, primary=ns5)
+    check_remove_zsk(ns4, zone, keys4, expected4, [zsks3[0]], extra, primary=ns5)
+    check_no_dnssec_in_journal(ns3, zone)
+    check_no_dnssec_in_journal(ns4, zone)
+
+    # Add CDNSKEY RRset.
+    newprops = [f"ksk unlimited {ALGORITHM} {SIZE}"]
+    extra = isctest.kasp.policy_to_properties(ttl=TTL, keys=newprops)
+    extra[0].private = False  # noqa
+    extra[0].legacy = True  # noqa
+
+    check_add_cdnskey(ns3, zone, keys3, expected3, [ksks4[0]], extra, primary=ns5)
+    check_add_cdnskey(ns4, zone, keys4, expected4, [ksks3[0]], extra, primary=ns5)
+    check_no_dnssec_in_journal(ns3, zone)
+    check_no_dnssec_in_journal(ns4, zone)
+
+    # Remove CDNSKEY RRset.
+    check_remove_cdnskey(ns3, zone, keys3, expected3, [ksks4[0]], extra, primary=ns5)
+    check_remove_cdnskey(ns4, zone, keys4, expected4, [ksks3[0]], extra, primary=ns5)
+    check_no_dnssec_in_journal(ns3, zone)
+    check_no_dnssec_in_journal(ns4, zone)
+
+    # Update CDS RRset.
+    check_add_cds(ns3, zone, keys3, expected3, [ksks4[0]], extra, primary=ns5)
+    check_add_cds(ns4, zone, keys4, expected4, [ksks3[0]], extra, primary=ns5)
+    check_no_dnssec_in_journal(ns3, zone)
+    check_no_dnssec_in_journal(ns4, zone)
+
+    # Remove CDS RRset.
+    check_remove_cds(ns3, zone, keys3, expected3, [ksks4[0]], extra, primary=ns5)
+    check_remove_cds(ns4, zone, keys4, expected4, [ksks3[0]], extra, primary=ns5)
+    check_no_dnssec_in_journal(ns3, zone)
+    check_no_dnssec_in_journal(ns4, zone)