]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Rewrite nsec3 system test to pytest (4/4)
authorMatthijs Mekking <matthijs@isc.org>
Tue, 30 Sep 2025 13:11:22 +0000 (15:11 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 21 Nov 2025 12:50:13 +0000 (13:50 +0100)
Convert the final nsec3 system test case that deals with empty
non-terminals. This is a regression test case for GL #5108.

bin/tests/system/nsec3/ns3/nsec3-ent.kasp.db.j2 [new file with mode: 0644]
bin/tests/system/nsec3/ns3/setup.sh
bin/tests/system/nsec3/tests.sh
bin/tests/system/nsec3/tests_nsec3_reconfig.py

diff --git a/bin/tests/system/nsec3/ns3/nsec3-ent.kasp.db.j2 b/bin/tests/system/nsec3/ns3/nsec3-ent.kasp.db.j2
new file mode 100644 (file)
index 0000000..5348dcf
--- /dev/null
@@ -0,0 +1,41 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0.  If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+{% set serial = serial | default(1) %}
+
+$ORIGIN nsec3-ent.kasp.
+$TTL 300
+nsec3-ent.kasp.                IN      SOA  mname1. . (
+                       @serial@  ; serial
+                       20        ; refresh (20 seconds)
+                       20        ; retry (20 seconds)
+                       1814400   ; expire (3 weeks)
+                       3600      ; minimum (1 hour)
+                       )
+
+                       NS      ns3
+ns3                    A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+
+{% if serial == 1 %}
+c                      A       10.0.0.3
+{% endif %}
+
+{% if serial == 2 %}
+d                      A       10.0.0.3
+{% endif %}
+
+{% if serial == 3 %}
+c                      A       10.0.0.3
+x.y.z                  A       10.0.0.4
+{% endif %}
index 32ddf5e9c59c7e8e5ce480ded9b50cd4f784fb7a..5b7053e08dab7849d89db2c3739e760a1763b88d 100644 (file)
@@ -26,8 +26,7 @@ setup() {
 for zn in nsec-to-nsec3 nsec3 nsec3-other nsec3-change nsec3-to-nsec \
   nsec3-to-optout nsec3-from-optout nsec3-dynamic \
   nsec3-dynamic-change nsec3-dynamic-to-inline \
-  nsec3-inline-to-dynamic nsec3-dynamic-update-inline \
-  nsec3-ent; do
+  nsec3-inline-to-dynamic nsec3-dynamic-update-inline; do
   setup "${zn}.kasp"
 done
 
index d22b3fd65f2613063f568d0814a4ac3901e7c0cd..131daf052e857b4f5d57e4e7f3954bcaa4e9d99a 100644 (file)
@@ -235,40 +235,5 @@ key_clear "KEY2"
 key_clear "KEY3"
 key_clear "KEY4"
 
-# Zone: nsec3-ent.kasp (regression test for #5108)
-n=$((n + 1))
-echo_i "check query for newly empty name does not crash ($n)"
-set_zone_policy "nsec3-ent.kasp"
-set_server "ns3" "10.53.0.3"
-# confirm the pre-existing name still exists
-dig_with_opts +noquestion "@${SERVER}" c.$ZONE >"dig.out.$ZONE.test$n.1" || ret=1
-grep "c\.nsec3-ent\.kasp\..*IN.*A.*10\.0\.0\.3" "dig.out.$ZONE.test$n.1" >/dev/null || ret=1
-# remove a name, bump the SOA, and reload
-sed -e 's/1 *; serial/2/' -e '/^c/d' ns3/template.db.in >ns3/nsec3-ent.kasp.db
-rndc_reload ns3 10.53.0.3
-# try the query again
-dig_with_opts +noquestion "@${SERVER}" c.$ZONE >"dig.out.$ZONE.test$n.2" || ret=1
-grep "status: NXDOMAIN" "dig.out.$ZONE.test$n.2" >/dev/null || ret=1
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
-n=$((n + 1))
-echo_i "check queries for new names below ENT do not crash ($n)"
-set_zone_policy "nsec3-ent.kasp"
-set_server "ns3" "10.53.0.3"
-# confirm the ENT name does not exist yet
-dig_with_opts +noquestion "@${SERVER}" x.y.z.$ZONE >"dig.out.$ZONE.test$n.1" || ret=1
-grep "status: NXDOMAIN" "dig.out.$ZONE.test$n.1" >/dev/null || ret=1
-# add a name with an ENT, bump the SOA, and reload ensuring the time stamp changes
-sleep 1
-sed -e 's/1 *; serial/3/' ns3/template.db.in >ns3/nsec3-ent.kasp.db
-echo "x.y.z A 10.0.0.4" >>ns3/nsec3-ent.kasp.db
-rndc_reload ns3 10.53.0.3
-# try the query again
-dig_with_opts +noquestion "@${SERVER}" x.y.z.$ZONE >"dig.out.$ZONE.test$n.2" || ret=1
-grep "x\.y\.z\.nsec3-ent\.kasp\..*IN.*A.*10\.0\.0\.4" "dig.out.$ZONE.test$n.2" >/dev/null || ret=1
-if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
-status=$((status + ret))
-
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1
index 9178d9c716ebf7b27ad4356853d35170fbeb1e1a..5dd27e281a7febc72f2525fce04a4b2301288b9b 100644 (file)
@@ -354,3 +354,99 @@ def test_nsec3_case(ns3, params):
         )
         response = ns3.rndc(f"signing -nsec3param 1 1 12 ffff {zone}")
         assert "zone uses dnssec-policy, use rndc dnssec command instead" in response
+
+
+def test_nsec3_ent(ns3, templates):
+    # Zone: nsec3-ent.kasp (regression test for #5108)
+    zone = "nsec3-ent.kasp"
+    fqdn = f"{zone}."
+    policy = "nsec3"
+    keydir = ns3.identifier
+    config = default_config
+    ttl = int(config.get("dnskey-ttl", 3600).total_seconds())
+    minimum = 3600
+    keyprops = [
+        f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
+    ]
+    expected = isctest.kasp.policy_to_properties(ttl=ttl, keys=keyprops)
+
+    # Test case.
+    isctest.log.info(f"check nsec3 case zone {zone} policy {policy}")
+
+    # First make sure the zone is properly signed.
+    isctest.kasp.wait_keymgr_done(ns3, zone, reconfig=True)
+
+    keys = isctest.kasp.keydir_to_keylist(zone, keydir)
+    isctest.kasp.check_keys(zone, keys, expected)
+    isctest.kasp.check_dnssec_verify(ns3, zone)
+    isctest.kasp.check_apex(ns3, zone, keys, [])
+
+    query = isctest.query.create(fqdn, dns.rdatatype.NSEC3PARAM)
+    response = isctest.query.tcp(query, ns3.ip, ns3.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NOERROR
+
+    match = f"{fqdn} {minimum} IN NSEC3PARAM 1 0 0"
+    salt = check_nsec3param(response, match, 0)
+
+    query = isctest.query.create(f"nosuchname.{fqdn}", dns.rdatatype.A)
+    response = isctest.query.tcp(query, ns3.ip, ns3.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NXDOMAIN
+    check_auth_nsec3(response, 0, 0, salt)
+
+    isctest.log.info("check query for newly empty name does not crash")
+
+    # confirm the pre-existing name still exists
+    query = isctest.query.create(f"c.{fqdn}", dns.rdatatype.A)
+    response = isctest.query.tcp(query, ns3.ip, ns3.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NOERROR
+
+    match = "10.0.0.3"
+    rrset = response.get_rrset(
+        response.answer,
+        dns.name.from_text(f"c.{fqdn}"),
+        dns.rdataclass.IN,
+        dns.rdatatype.A,
+    )
+    assert rrset is not None, "no A records found in answer section"
+    assert match in str(rrset[0])
+
+    # remove a name, bump the SOA, and reload
+    templates.render(f"{ns3.identifier}/nsec3-ent.kasp.db", {"serial": 2})
+
+    with ns3.watch_log_from_here() as watcher:
+        ns3.rndc(f"reload {zone}")
+        watcher.wait_for_line(f"zone {zone}/IN (signed): sending notifies")
+
+    # try the query again
+    query = isctest.query.create(f"c.{fqdn}", dns.rdatatype.A)
+    response = isctest.query.tcp(query, ns3.ip, ns3.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NXDOMAIN
+
+    isctest.log.info("check queries for new names below ENT do not crash")
+
+    # confirm the ENT name does not exist yet
+    query = isctest.query.create(f"x.y.z.{fqdn}", dns.rdatatype.A)
+    response = isctest.query.tcp(query, ns3.ip, ns3.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NXDOMAIN
+
+    # add a name with an ENT, bump the SOA, and reload ensuring the time stamp changes
+    templates.render(f"{ns3.identifier}/nsec3-ent.kasp.db", {"serial": 3})
+
+    with ns3.watch_log_from_here() as watcher:
+        ns3.rndc(f"reload {zone}")
+        watcher.wait_for_line(f"zone {zone}/IN (signed): sending notifies")
+
+    # try the query again
+    query = isctest.query.create(f"x.y.z.{fqdn}", dns.rdatatype.A)
+    response = isctest.query.tcp(query, ns3.ip, ns3.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NOERROR
+
+    match = "10.0.0.4"
+    rrset = response.get_rrset(
+        response.answer,
+        dns.name.from_text(f"x.y.z.{fqdn}"),
+        dns.rdataclass.IN,
+        dns.rdatatype.A,
+    )
+    assert rrset is not None, "no A records found in answer section"
+    assert match in str(rrset[0])