From: Matthijs Mekking Date: Thu, 6 Nov 2025 16:32:51 +0000 (+0100) Subject: Test retransfer with NSEC3 policy X-Git-Tag: v9.21.16~35^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be3e4c83d0219d615953db53ba1bb5d63f67a054;p=thirdparty%2Fbind9.git Test retransfer with NSEC3 policy 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. --- diff --git a/bin/tests/system/nsec3/common.py b/bin/tests/system/nsec3/common.py index 678cc4cbed5..c7312cd3246 100644 --- a/bin/tests/system/nsec3/common.py +++ b/bin/tests/system/nsec3/common.py @@ -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", ] ) diff --git a/bin/tests/system/nsec3/ns2/named.conf.j2 b/bin/tests/system/nsec3/ns2/named.conf.j2 index 904abbf81d6..ec680db6776 100644 --- a/bin/tests/system/nsec3/ns2/named.conf.j2 +++ b/bin/tests/system/nsec3/ns2/named.conf.j2 @@ -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 index 00000000000..f8a138c6036 --- /dev/null +++ b/bin/tests/system/nsec3/ns2/retransfer.kasp.db.j2 @@ -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 index 00000000000..e521f966216 --- /dev/null +++ b/bin/tests/system/nsec3/ns3/named-retransfer.conf.j2 @@ -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 #} diff --git a/bin/tests/system/nsec3/ns3/named.conf.j2 b/bin/tests/system/nsec3/ns3/named.conf.j2 index 7dd06ad83cc..7aa4f17194f 100644 --- a/bin/tests/system/nsec3/ns3/named.conf.j2 +++ b/bin/tests/system/nsec3/ns3/named.conf.j2 @@ -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 index 00000000000..59a126b6f49 --- /dev/null +++ b/bin/tests/system/nsec3/ns4/named.conf.j2 @@ -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 index 00000000000..96a23761b5a --- /dev/null +++ b/bin/tests/system/nsec3/tests_nsec3_retransfer.py @@ -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