]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Test retransfer with NSEC3 policy
authorMatthijs Mekking <matthijs@isc.org>
Thu, 6 Nov 2025 16:32:51 +0000 (17:32 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Mon, 24 Nov 2025 09:21:33 +0000 (10:21 +0100)
If the primary has been updated, but the secondary has not been
notified, the journal will go out of date. An 'rndc retransfer' causes
the zone to force an AXFR, removing and rebuilding zone and journal
files.

This test reproduces a bug that in such scenario, an NSEC3 signed zone
falls back to NSEC.

bin/tests/system/nsec3/common.py
bin/tests/system/nsec3/ns2/named.conf.j2
bin/tests/system/nsec3/ns2/retransfer.kasp.db.j2 [new file with mode: 0644]
bin/tests/system/nsec3/ns3/named-retransfer.conf.j2 [new file with mode: 0644]
bin/tests/system/nsec3/ns3/named.conf.j2
bin/tests/system/nsec3/ns4/named.conf.j2 [new file with mode: 0644]
bin/tests/system/nsec3/tests_nsec3_retransfer.py [new file with mode: 0644]

index 678cc4cbed53dc2dbf5d086289c809c6116d0fd4..c7312cd3246cd1ee48698582e9a10cf99549a776 100644 (file)
@@ -35,9 +35,7 @@ pytestmark = pytest.mark.extra_artifacts(
         "ns*/*.jnl",
         "ns*/*.signed",
         "ns*/keygen.out.*",
-        "ns3/named-common.conf",
-        "ns3/named-fips.conf",
-        "ns3/named-rsasha1.conf",
+        "ns3/named-*.conf",
     ]
 )
 
index 904abbf81d65daa421f12081aa22fdccef46022b..ec680db6776282624c04dde6e6c58b69977bfdb0 100644 (file)
@@ -46,3 +46,13 @@ zone "nsec3-xfr-inline.kasp" {
        dnssec-policy "nsec3";
 };
 {% endif %}{# nsec3-xfr-inline.kasp #}
+
+{% if "retransfer.kasp" in zones %}
+zone "retransfer.kasp" {
+       type primary;
+       file "retransfer.kasp.db";
+       notify explicit;
+       also-notify { 10.53.0.3; };
+       allow-transfer { any; };
+};
+{% endif %}{# nsec3-xfr-inline.kasp #}
diff --git a/bin/tests/system/nsec3/ns2/retransfer.kasp.db.j2 b/bin/tests/system/nsec3/ns2/retransfer.kasp.db.j2
new file mode 100644 (file)
index 0000000..f8a138c
--- /dev/null
@@ -0,0 +1,31 @@
+; 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 retransfer.kasp.
+$TTL 300
+
+retransfer.kasp.       IN      SOA  mname1. . (
+                       @serial@  ; serial
+                       20        ; refresh (20 seconds)
+                       20        ; retry (20 seconds)
+                       1814400   ; expire (3 weeks)
+                       3600      ; minimum (1 hour)
+                       )
+
+                       NS      ns2
+ns2                    A       10.53.0.2
+ns3                    A       10.53.0.3
+
+a                      A       10.0.0.1
+b                      A       10.0.0.2
+c                      A       10.0.0.3
diff --git a/bin/tests/system/nsec3/ns3/named-retransfer.conf.j2 b/bin/tests/system/nsec3/ns3/named-retransfer.conf.j2
new file mode 100644 (file)
index 0000000..e521f96
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+remote-servers "ns2" {
+       10.53.0.2 port @PORT@;
+};
+
+remote-servers "ns4" {
+       10.53.0.4 port @PORT@;
+};
+
+dnssec-policy "nsec3rsa256" {
+       cdnskey no;
+       keys {
+               ksk lifetime unlimited algorithm RSASHA256 2048;
+               zsk lifetime P90D algorithm RSASHA256 2048;
+       };
+       max-zone-ttl P2D;
+       signatures-refresh P8D;
+
+       nsec3param;
+};
+
+{% if "retransfer.kasp" in zones %}
+zone "retransfer.kasp" {
+       type secondary;
+       primaries { "ns2"; };
+       file "retransfer.kasp.db";
+       allow-transfer { any; };
+       allow-notify { any; };
+       also-notify { "ns4"; };
+       notify explicit;
+
+       dnssec-policy "nsec3rsa256";
+       inline-signing yes;
+       sig-signing-signatures 100;
+       checkds no;
+};
+{% endif %}{# retransfer.kasp #}
index 7dd06ad83cc52a8aa394efcdb3539e3d1949772e..7aa4f17194f3750cbfcd6317a187dffc58dbd63a 100644 (file)
@@ -15,6 +15,7 @@
 
 include "named-common.conf";
 include "named-fips.conf";
+include "named-retransfer.conf";
 
 {% if RSASHA1_SUPPORTED == "1" %}
 include "named-rsasha1.conf";
diff --git a/bin/tests/system/nsec3/ns4/named.conf.j2 b/bin/tests/system/nsec3/ns4/named.conf.j2
new file mode 100644 (file)
index 0000000..59a126b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+// NS4
+
+options {
+       query-source address 10.53.0.4;
+       notify-source 10.53.0.4;
+       transfer-source 10.53.0.4;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.4; };
+       listen-on-v6 { none; };
+       allow-transfer { any; };
+       recursion no;
+       dnssec-validation no;
+};
+
+remote-servers "ns3" {
+       10.53.0.3 port @PORT@;
+};
+
+zone "retransfer.kasp" {
+       type secondary;
+       file "retransfer.kasp.db";
+       primaries { "ns3"; };
+       allow-notify { any; };
+       notify no;
+};
diff --git a/bin/tests/system/nsec3/tests_nsec3_retransfer.py b/bin/tests/system/nsec3/tests_nsec3_retransfer.py
new file mode 100644 (file)
index 0000000..96a2376
--- /dev/null
@@ -0,0 +1,129 @@
+# 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.
+
+# pylint: disable=redefined-outer-name,unused-import
+
+import os
+import shutil
+
+from datetime import timedelta
+
+import dns.update
+import pytest
+
+pytest.importorskip("dns", minversion="2.0.0")
+import isctest
+import isctest.mark
+from isctest.vars.algorithms import RSASHA256
+from nsec3.common import (
+    pytestmark,
+    check_auth_nsec3,
+    check_nsec3param,
+)
+
+DNSKEY_TTL = int(timedelta(hours=1).total_seconds())
+ZSK_LIFETIME = int(timedelta(days=90).total_seconds())
+
+# include the following zones when rendering named configs
+ZONES = {
+    "retransfer.kasp",
+}
+
+
+def bootstrap():
+    return {
+        "zones": ZONES,
+    }
+
+
+def perform_nsec3_tests(server, params):
+    # Get test parameters.
+    zone = params["zone"]
+    fqdn = f"{zone}."
+    policy = params["policy"]
+    keydir = server.identifier
+    minimum = params.get("soa-minimum", 3600)
+    expected = isctest.kasp.policy_to_properties(
+        ttl=DNSKEY_TTL, keys=params["key-properties"]
+    )
+
+    iterations = 0
+    optout = 0
+    saltlen = 0
+
+    match = f"{fqdn} {minimum} IN NSEC3PARAM 1 0 {iterations}"
+
+    # 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(server, zone)
+
+    keys = isctest.kasp.keydir_to_keylist(zone, keydir)
+    ksks = [k for k in keys if k.is_ksk()]
+    zsks = [k for k in keys if k.is_zsk()]
+    isctest.kasp.check_keys(zone, keys, expected)
+    isctest.kasp.check_dnssec_verify(server, zone)
+    isctest.kasp.check_apex(server, zone, ksks, zsks)
+
+    query = isctest.query.create(fqdn, dns.rdatatype.NSEC3PARAM)
+    response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NOERROR
+
+    salt = check_nsec3param(response, match, saltlen)
+
+    query = isctest.query.create(f"nosuchname.{fqdn}", dns.rdatatype.A)
+    response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3)
+    assert response.rcode() == dns.rcode.NXDOMAIN
+    check_auth_nsec3(response, iterations, optout, salt)
+
+    return salt
+
+
+def test_nsec3_retransfer(servers, templates):
+    ns2 = servers["ns2"]
+    ns3 = servers["ns3"]
+
+    params = {
+        "zone": "retransfer.kasp",
+        "policy": "nsec3rsa256",
+        "key-properties": [
+            f"ksk 0 {RSASHA256.number} 2048 goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden",
+            f"zsk {ZSK_LIFETIME} {RSASHA256.number} 2048 goal:omnipresent dnskey:rumoured zrrsig:rumoured",
+        ],
+    }
+
+    zone = params["zone"]
+    salt = perform_nsec3_tests(ns3, params)
+
+    # Stop primary.
+    ns2.stop()
+
+    # Update the zone.
+    serial = 10
+    templates.render(f"{ns2.identifier}/{zone}.db", {"serial": serial})
+
+    with ns2.watch_log_from_here() as watcher:
+        ns2.start(["--noclean", "--restart", "--port", os.environ["PORT"]])
+        watcher.wait_for_line("all zones loaded")
+
+    # Test NSEC3 and NSEC3PARAM is the same after retransfer.
+    isctest.log.info(f"check zone {zone} after retransfer has salt {salt}")
+    prevsalt = salt
+
+    # Retransfer zone, NSEC3 should stay the same.
+    with ns3.watch_log_from_here() as watcher:
+        ns3.rndc(f"retransfer {zone}")
+        # When sending notifies, the zone should be up to date.
+        watcher.wait_for_line(f"zone {zone}/IN (signed): sending notify to 10.53.0.4")
+
+    salt = perform_nsec3_tests(ns3, params)
+    assert prevsalt == salt