From 613f48ebcbd2a7202d3c81eafcb0e54c1d0fb253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Wed, 1 Apr 2026 17:04:22 +0200 Subject: [PATCH 1/8] Improve isctest.template dataclasses' defaults Extend the Nameserver to generate the default IPv4/IPv6 values, add NSX values for the predefined nameservers (there are 11 of them, as per bin/tests/system/ifconfig.sh.in max value). Add the missing ns11 fixture. Extend the Zone to derive the zone filename by default, unless specified. Adjust the existing uses of these classes to utilize the simplified defaults. (cherry picked from commit fa3a59e70c97a24090a24d46135f672a004bf9ca) --- bin/tests/system/conftest.py | 5 + bin/tests/system/isctest/template.py | 37 +++++- bin/tests/system/rollover/setup.py | 106 +++++++++--------- .../system/rollover/tests_rollover_manual.py | 4 +- 4 files changed, 93 insertions(+), 59 deletions(-) diff --git a/bin/tests/system/conftest.py b/bin/tests/system/conftest.py index 2b59002ee6..ab462e32f2 100644 --- a/bin/tests/system/conftest.py +++ b/bin/tests/system/conftest.py @@ -649,3 +649,8 @@ def ns9(servers): @pytest.fixture(scope="module") def ns10(servers): return servers["ns10"] + + +@pytest.fixture(scope="module") +def ns11(servers): + return servers["ns11"] diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index 118565e3f7..d25e2926d0 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -15,6 +15,8 @@ from dataclasses import dataclass from pathlib import Path from typing import Any +import re + import jinja2 from .log import debug @@ -84,16 +86,47 @@ class TemplateEngine: @dataclass class Nameserver: + name: str - ip: str + num: int | None = None + ip: str | None = None + ip6: str | None = None + + def __post_init__(self): + if self.num is None: + match = re.search(r"\d+", self.name) + assert match + self.num = int(match.group(0)) + if self.ip is None: + self.ip = f"10.53.0.{self.num}" + if self.ip6 is None: + self.ip6 = f"fd92:7065:b8e:ffff::{self.num}" + + +NS1 = Nameserver("ns1") +NS2 = Nameserver("ns2") +NS3 = Nameserver("ns3") +NS4 = Nameserver("ns4") +NS5 = Nameserver("ns5") +NS6 = Nameserver("ns6") +NS7 = Nameserver("ns7") +NS8 = Nameserver("ns8") +NS9 = Nameserver("ns9") +NS10 = Nameserver("ns10") +NS11 = Nameserver("ns11") @dataclass class Zone: + name: str - filename: str ns: Nameserver type: str = "primary" + filename: str | None = None + + def __post_init__(self): + if self.filename is None: + self.filename = f"{self.name}.db" @dataclass diff --git a/bin/tests/system/rollover/setup.py b/bin/tests/system/rollover/setup.py index 840e7aa937..91b15006d7 100644 --- a/bin/tests/system/rollover/setup.py +++ b/bin/tests/system/rollover/setup.py @@ -13,7 +13,7 @@ import shutil from isctest.kasp import private_type_record from isctest.run import EnvCmd -from isctest.template import Nameserver, TrustAnchor, Zone +from isctest.template import NS2, NS3, TrustAnchor, Zone from isctest.vars.algorithms import Algorithm import isctest @@ -50,7 +50,7 @@ def configure_tld(zonename: str, delegations: list[Zone]) -> Zone: templates.render(f"ns2/{outfile}", tdata, template=f"ns2/{template}") signer(f"-P -x -O full -o {zonename} -f {outfile}.signed {outfile}", cwd="ns2") - return Zone(zonename, f"{outfile}.signed", Nameserver("ns2", "10.53.0.2")) + return Zone(zonename, NS2, filename=f"{outfile}.signed") def configure_root(delegations: list[Zone]) -> TrustAnchor: @@ -146,7 +146,7 @@ def configure_algo_csk(tld: str, policy: str, reconfig: bool = False) -> list[Zo # Step 1: # Introduce the first key. This will immediately be active. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") TactN = "now-7d" TsbmN = "now-161h" @@ -164,7 +164,7 @@ def configure_algo_csk(tld: str, policy: str, reconfig: bool = False) -> list[Zo # Step 2: # After the publication interval has passed the DNSKEY is OMNIPRESENT. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the new algorithm keys have been introduced is 3 hours. TpubN1 = "now-3h" @@ -187,7 +187,7 @@ def configure_algo_csk(tld: str, policy: str, reconfig: bool = False) -> list[Zo # Step 3: # The zone signatures are also OMNIPRESENT. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the new algorithm keys have been introduced is 7 hours. TpubN1 = "now-7h" @@ -211,7 +211,7 @@ def configure_algo_csk(tld: str, policy: str, reconfig: bool = False) -> list[Zo # Step 4: # The DS is swapped and can become OMNIPRESENT. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the DS has been swapped is 3 hours. TpubN1 = "now-10h" @@ -235,7 +235,7 @@ def configure_algo_csk(tld: str, policy: str, reconfig: bool = False) -> list[Zo # Step 5: # The DNSKEY is removed long enough to be HIDDEN. zonename = f"step5.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the DNSKEY has been removed is 2 hours. TpubN1 = "now-12h" @@ -259,7 +259,7 @@ def configure_algo_csk(tld: str, policy: str, reconfig: bool = False) -> list[Zo # Step 6: # The RRSIGs have been removed long enough to be HIDDEN. zonename = f"step6.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Additional time passed: 7h. TpubN1 = "now-19h" @@ -294,7 +294,7 @@ def configure_algo_ksk_zsk(tld: str, reconfig: bool = False) -> list[Zone]: # Step 1: # Introduce the first key. This will immediately be active. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") TactN = "now-7d" TsbmN = "now-161h" @@ -319,7 +319,7 @@ def configure_algo_ksk_zsk(tld: str, reconfig: bool = False) -> list[Zone]: # Step 2: # After the publication interval has passed the DNSKEY is OMNIPRESENT. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the new algorithm keys have been introduced is 3 hours. # Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h @@ -364,7 +364,7 @@ def configure_algo_ksk_zsk(tld: str, reconfig: bool = False) -> list[Zone]: # Step 3: # The zone signatures are also OMNIPRESENT. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the new algorithm keys have been introduced is 7 hours. TpubN1 = "now-7h" @@ -408,7 +408,7 @@ def configure_algo_ksk_zsk(tld: str, reconfig: bool = False) -> list[Zone]: # Step 4: # The DS is swapped and can become OMNIPRESENT. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the DS has been swapped is 3 hours. TpubN1 = "now-10h" @@ -452,7 +452,7 @@ def configure_algo_ksk_zsk(tld: str, reconfig: bool = False) -> list[Zone]: # Step 5: # The DNSKEY is removed long enough to be HIDDEN. zonename = f"step5.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The time passed since the DNSKEY has been removed is 2 hours. TpubN1 = "now-12h" @@ -496,7 +496,7 @@ def configure_algo_ksk_zsk(tld: str, reconfig: bool = False) -> list[Zone]: # Step 6: # The RRSIGs have been removed long enough to be HIDDEN. zonename = f"step6.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Additional time passed: 7h. TpubN1 = "now-19h" @@ -551,7 +551,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # Step 1: # Introduce the first key. This will immediately be active. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") TactN = "now-7d" keytimes = f"-P {TactN} -A {TactN}" @@ -567,7 +567,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # Step 2: # It is time to introduce the new CSK. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC @@ -599,7 +599,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # Step 3: # It is time to submit the DS and to roll signatures. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # @@ -664,7 +664,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # (which is 26d3h). The DS is swapped after Iret (which is 4h). # In other words, the DS is swapped before all zone signatures are replaced. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # Trem(N) = Tret(N) - Iret + IretZ @@ -715,7 +715,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # After the DS is swapped in step 4, also the KRRSIG records can be removed. # At this time these have all become hidden. zonename = f"step5.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract DNSKEY TTL plus zone propagation delay from all the times (2h). TpubN = "now-4470h" @@ -750,7 +750,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # After the retire interval has passed the predecessor DNSKEY can be # removed from the zone. zonename = f"step6.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # Trem(N) = Tret(N) + IretZ @@ -800,7 +800,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # Step 7: # Some time later the predecessor DNSKEY enters the HIDDEN state. zonename = f"step7.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract DNSKEY TTL plus zone propagation delay from all the times (2h). TpubN = "now-5093h" @@ -834,7 +834,7 @@ def configure_cskroll1(tld: str, policy: str) -> list[Zone]: # Step 8: # The predecessor DNSKEY can be purged. zonename = f"step8.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract purge-keys interval from all the times (1h). TpubN = "now-5094h" @@ -882,7 +882,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Step 1: # Introduce the first key. This will immediately be active. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") TactN = "now-7d" keytimes = f"-P {TactN} -A {TactN}" @@ -898,7 +898,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Step 2: # It is time to introduce the new CSK. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC @@ -930,7 +930,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Step 3: # It is time to submit the DS and to roll signatures. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # @@ -995,7 +995,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # The DS is swapped after Dreg + Iret (1w3h). In other words, the zone # signatures are replaced before the DS is swapped. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # Trem(N) = Tret(N) + IretZ @@ -1048,7 +1048,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Some time later the DS can be swapped and the old DNSKEY can be removed from # the zone. zonename = f"step5.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract Iret (170h) - IretZ (38h) = 132h. # @@ -1091,7 +1091,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Step 6: # Some time later the predecessor DNSKEY enters the HIDDEN state. zonename = f"step6.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract DNSKEY TTL plus zone propagation delay (2h). # @@ -1134,7 +1134,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Step 7: # The predecessor DNSKEY can be purged, but purge-keys is disabled. zonename = f"step7.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract 90 days (default, 2160h) from all the times. # @@ -1178,7 +1178,7 @@ def configure_cskroll2(tld: str, policy: str) -> list[Zone]: # Step 8: # The predecessor DNSKEY can be purged. zonename = f"step8.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract purge-keys interval from all the times (1h). TpubN = "now-5094h" @@ -1224,14 +1224,14 @@ def configure_enable_dnssec(tld: str, policy: str) -> list[Zone]: # This is an unsigned zone and named should perform the initial steps of # introducing the DNSSEC records in the right order. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") render_and_sign_zone(zonename, [], signing=False) # Step 2: # The DNSKEY has been published long enough to become OMNIPRESENT. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # DNSKEY TTL: 300 seconds # zone-propagation-delay: 5 minutes (300 seconds) @@ -1251,7 +1251,7 @@ def configure_enable_dnssec(tld: str, policy: str) -> list[Zone]: # Step 3: # The zone signatures have been published long enough to become OMNIPRESENT. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Passed time since publication: # max-zone-ttl: 12 hours (43200 seconds) @@ -1271,7 +1271,7 @@ def configure_enable_dnssec(tld: str, policy: str) -> list[Zone]: # Step 4: # The DS has been submitted long enough ago to become OMNIPRESENT. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # DS TTL: 2 hour (7200 seconds) # parent-propagation-delay: 1 hour (3600 seconds) @@ -1304,7 +1304,7 @@ def configure_going_insecure(tld: str, reconfig: bool = False) -> list[Zone]: # Step 1: zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Timing metadata. TpubN = "now-10d" @@ -1330,9 +1330,7 @@ def configure_going_insecure(tld: str, reconfig: bool = False) -> list[Zone]: if reconfig: # Step 2: zonename = f"step2.{zone}" - zones.append( - Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3")) - ) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # The DS was withdrawn from the parent zone 26 hours ago. TremN = "now-26h" @@ -1371,7 +1369,7 @@ def configure_straight2none(tld: str) -> list[Zone]: keytimes = f"-P {TpubN} -A {TpubN} -P sync {TsbmN}" zonename = f"going-straight-to-none.{tld}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Key generation. csk_name = keygen(f"-f KSK {keytimes} {zonename}", cwd="ns3").out.strip() @@ -1383,9 +1381,7 @@ def configure_straight2none(tld: str) -> list[Zone]: render_and_sign_zone(zonename, [csk_name], extra_options="-z") zonename = f"going-straight-to-none-dynamic.{tld}" - zones.append( - Zone(zonename, f"{zonename}.db.signed", Nameserver("ns3", "10.53.0.3")) - ) + zones.append(Zone(zonename, NS3, filename=f"{zonename}.db.signed")) isctest.log.info(f"setup {zonename}") # Key generation. csk_name = keygen(f"-f KSK {keytimes} {zonename}", cwd="ns3").out.strip() @@ -1411,7 +1407,7 @@ def configure_ksk_doubleksk(tld: str) -> list[Zone]: # Step 1: # Introduce the first key. This will immediately be active. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Timing metadata. TactN = "now-7d" @@ -1433,7 +1429,7 @@ def configure_ksk_doubleksk(tld: str) -> list[Zone]: # Step 2: # It is time to introduce the new KSK. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Lksk: 60d # Dreg: n/a @@ -1469,7 +1465,7 @@ def configure_ksk_doubleksk(tld: str) -> list[Zone]: # Step 3: # It is time to submit the DS. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # Iret = DprpP + TTLds (+retire-safety) @@ -1523,7 +1519,7 @@ def configure_ksk_doubleksk(tld: str) -> list[Zone]: # Step 4: # The DS should be swapped now. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Tpub(N) = now - Lksk - Iret = now - 60d - 50h # = now - 1440h - 50h = now - 1490h @@ -1574,7 +1570,7 @@ def configure_ksk_doubleksk(tld: str) -> list[Zone]: # Step 5: # The predecessor DNSKEY is removed long enough that is has become HIDDEN. zonename = f"step5.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract DNSKEY TTL + zone-propagation-delay from all the times (3h). # Tpub(N) = now - 1490h - 3h = now - 1493h @@ -1624,7 +1620,7 @@ def configure_ksk_doubleksk(tld: str) -> list[Zone]: # Step 6: # The predecessor DNSKEY can be purged. zonename = f"step6.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract purge-keys interval from all the times (1h). TpubN = "now-1494h" @@ -1685,7 +1681,7 @@ def configure_ksk_3crowd(tld: str) -> list[Zone]: # Set up a zone that has a KSK (KEY1) and have the successor key (KEY2) # published as well. zonename = f"three-is-a-crowd.{tld}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # These times are the same as step3.ksk-doubleksk.autosign. TpubN = "now-60d" @@ -1738,7 +1734,7 @@ def configure_zsk_prepub(tld: str) -> list[Zone]: # Step 1: # Introduce the first key. This will immediately be active. zonename = f"step1.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Timing metadata. TactN = "now-7d" @@ -1760,7 +1756,7 @@ def configure_zsk_prepub(tld: str) -> list[Zone]: # Step 2: # It is time to pre-publish the successor ZSK. zonename = f"step2.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # Tact(N) = now + Ipub - Lzsk = now + 26h - 30d @@ -1785,7 +1781,7 @@ def configure_zsk_prepub(tld: str) -> list[Zone]: # After the publication interval has passed the DNSKEY of the successor ZSK # is OMNIPRESENT and the zone can thus be signed with the successor ZSK. zonename = f"step3.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # According to RFC 7583: # Tpub(N+1) <= Tact(N) + Lzsk - Ipub @@ -1827,7 +1823,7 @@ def configure_zsk_prepub(tld: str) -> list[Zone]: # After the retire interval has passed the predecessor DNSKEY can be # removed from the zone. zonename = f"step4.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Lzsk: 30d # Ipub: 26h @@ -1878,7 +1874,7 @@ def configure_zsk_prepub(tld: str) -> list[Zone]: # Step 5: # The predecessor DNSKEY is removed long enough that is has become HIDDEN. zonename = f"step5.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract DNSKEY TTL + zone-propagation-delay from all the times (2h). # Tact(N) = now - 961h - 2h = now - 963h @@ -1916,7 +1912,7 @@ def configure_zsk_prepub(tld: str) -> list[Zone]: # Step 6: # The predecessor DNSKEY can be purged. zonename = f"step6.{zone}" - zones.append(Zone(zonename, f"{zonename}.db", Nameserver("ns3", "10.53.0.3"))) + zones.append(Zone(zonename, NS3)) isctest.log.info(f"setup {zonename}") # Subtract purge-keys interval from all the times (1h). TactN = "now-964h" diff --git a/bin/tests/system/rollover/tests_rollover_manual.py b/bin/tests/system/rollover/tests_rollover_manual.py index 80a00ada30..17d446fcc9 100644 --- a/bin/tests/system/rollover/tests_rollover_manual.py +++ b/bin/tests/system/rollover/tests_rollover_manual.py @@ -13,7 +13,7 @@ from datetime import timedelta from isctest.kasp import Ipub, Iret, KeyTimingMetadata, private_type_record from isctest.run import EnvCmd -from isctest.template import Nameserver, Zone +from isctest.template import NS3, Zone from isctest.vars.algorithms import Algorithm from rollover.setup import configure_root, configure_tld @@ -61,7 +61,7 @@ def setup_zone(zone, ksk_time, ksk_settime, zsk_time, zsk_settime) -> Zone: templates.render(f"ns3/{outfile}", tdata, template=f"ns3/{template}") signer(f"-P -x -O raw -o {zone} -f {outfile}.signed {outfile}", cwd="ns3") - return Zone(zone, outfile, Nameserver("ns3", "10.53.0.3")) + return Zone(zone, NS3) def bootstrap(): From 6e31e36a0e78a7aa1b12f0482d5a98cf59179372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Wed, 1 Apr 2026 17:10:08 +0200 Subject: [PATCH 2/8] Add a directory-specific nameserver data to templates If a template is being rendered into a directory that represents a nameserver (e.g. "ns1"), include a nameserver-specific information in the data - variable called "ns" which has information about the nameserver this file belongs to. Ensure the "ns" variable is only exposed to the template when rendered, without affecting the environment variables (always work with a copy of the env_vars). (cherry picked from commit aa435b2e036a668d87579495cae1e31524a97f31) --- bin/tests/system/isctest/template.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index d25e2926d0..f81b423f10 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -13,6 +13,7 @@ from dataclasses import dataclass from pathlib import Path +from re import compile as Re from typing import Any import re @@ -22,6 +23,8 @@ import jinja2 from .log import debug from .vars import ALL +NS_DIR_RE = Re(r"^(a?ns([0-9]+))/") + class TemplateEngine: """ @@ -63,10 +66,16 @@ class TemplateEngine: raise RuntimeError('No jinja2 template found for "{output}"') if data is None: - data = self.env_vars + data = {**self.env_vars} else: data = {**self.env_vars, **data} + # directory-specific "ns" var + assert "ns" not in data, '"ns" variable is reserved for nameserver data' + match = NS_DIR_RE.search(output) + if match: + data["ns"] = Nameserver(match.group(1)) + debug("rendering template `%s` to file `%s`", template, output) stream = self.j2env.get_template(template).stream(data) stream.dump(output, encoding="utf-8") From 8cda065d496d97fa4107e593a68dff9d8dfb0c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Wed, 1 Apr 2026 19:42:38 +0200 Subject: [PATCH 3/8] Allow instantiating template dataclasses in jinja2 templates In some cases, the template data might need to be set directly in the jinja2 templates using `{% set %}`. Expose the template dataclasses to the templates so we can use these existing classes, rather than creating ad-hoc data containers. (cherry picked from commit dddb0673517ccbba50941d91dec3c55e1f7f52b4) --- bin/tests/system/isctest/template.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index f81b423f10..8065f4acbb 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -45,6 +45,11 @@ class TemplateEngine: variable_start_string="@", variable_end_string="@", ) + # allow instantiating the template dataclasses in jinja2 templates when + # using {% set %} + self.j2env.globals["Nameserver"] = Nameserver + self.j2env.globals["TrustAnchor"] = TrustAnchor + self.j2env.globals["Zone"] = Zone def render( self, From 2cdaaf9d0e811891429c3518326ed81522111628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Tue, 7 Apr 2026 14:40:33 +0200 Subject: [PATCH 4/8] Reduce whitespace in jinja2 templates Omit extra newlines when combining and including templates. Adjust the xfer/ns8/small.db.j2 so it doesn't trim the endline twice (as that would join the two subsequent records on the same line). (cherry picked from commit 4f8e3774bba0f63d98e651ef044d6703792921a2) --- bin/tests/system/isctest/template.py | 2 ++ bin/tests/system/xfer/ns8/small.db.j2 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index 8065f4acbb..21cc811dfb 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -44,6 +44,8 @@ class TemplateEngine: undefined=jinja2.StrictUndefined, variable_start_string="@", variable_end_string="@", + trim_blocks=True, + keep_trailing_newline=True, ) # allow instantiating the template dataclasses in jinja2 templates when # using {% set %} diff --git a/bin/tests/system/xfer/ns8/small.db.j2 b/bin/tests/system/xfer/ns8/small.db.j2 index 2c90a2577a..b51aff5692 100644 --- a/bin/tests/system/xfer/ns8/small.db.j2 +++ b/bin/tests/system/xfer/ns8/small.db.j2 @@ -12,4 +12,4 @@ {% for i in range(4096) %} name@i@ 259200 A 1.2.3.4 name@i@ 259200 TXT "Hello World @i@" -{%- endfor %} \ No newline at end of file +{% endfor %} From ff6a91438c091f0834371f016d12f2cf7576f978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Tue, 7 Apr 2026 14:43:11 +0200 Subject: [PATCH 5/8] Add _common dir to jinja2 template loader This allows include of template snippets from _common/ directory. (cherry picked from commit e34c3252d973ca6e1eb62b66ca74599568e8d33c) --- bin/tests/system/isctest/template.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index 21cc811dfb..f0668880c6 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -40,7 +40,12 @@ class TemplateEngine: self.directory = Path(directory) self.env_vars = dict(env_vars) self.j2env = jinja2.Environment( - loader=jinja2.FileSystemLoader(str(self.directory)), + loader=jinja2.FileSystemLoader( + [ + str(self.directory), + str(ALL["srcdir"]), # to allow _common/ includes + ] + ), undefined=jinja2.StrictUndefined, variable_start_string="@", variable_end_string="@", @@ -65,12 +70,13 @@ class TemplateEngine: variables which the engine was initialized with are also filled in. In case of a variable name clash, `data` has precedence. """ + available = self.j2env.list_templates() if template is None: template = f"{output}.j2.manual" - if not Path(template).is_file(): + if template not in available: template = f"{output}.j2" - if not Path(template).is_file(): - raise RuntimeError('No jinja2 template found for "{output}"') + if template not in available: + raise RuntimeError(f'No jinja2 template found for "{output}"') if data is None: data = {**self.env_vars} From cefbed18714f518fc715bcc9f5cfd0e2dbd8c4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Wed, 1 Apr 2026 18:25:28 +0200 Subject: [PATCH 6/8] Include controls.conf as jinja2 template Rather than using named.conf include, render the controls directly into the config using jinja2 template include. (cherry picked from commit f4ca352bc88ab1c766fb4862de118e494fddfb5b) --- bin/tests/system/_common/controls.conf.in | 9 --------- bin/tests/system/_common/controls.conf.j2 | 4 ++++ bin/tests/system/allow_query/.gitignore | 1 - bin/tests/system/allow_query/ns2/controls.conf.j2 | 1 - bin/tests/system/allow_query/ns2/named.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named02.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named03.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named04.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named05.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named06.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named07.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named08.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named09.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named10.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named11.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named12.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named21.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named22.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named23.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named24.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named25.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named26.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named27.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named28.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named29.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named30.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named31.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named32.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named33.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named34.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named40.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named53.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named54.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named55.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named56.conf.j2 | 2 +- bin/tests/system/allow_query/ns2/named57.conf.j2 | 2 +- bin/tests/system/allow_query/tests_sh_allow_query.py | 1 - bin/tests/system/optout/ns2/controls.conf.j2 | 1 - bin/tests/system/optout/ns2/named.conf.j2 | 2 +- bin/tests/system/optout/tests_optout.py | 1 - 40 files changed, 37 insertions(+), 47 deletions(-) delete mode 100644 bin/tests/system/_common/controls.conf.in create mode 100644 bin/tests/system/_common/controls.conf.j2 delete mode 120000 bin/tests/system/allow_query/ns2/controls.conf.j2 delete mode 120000 bin/tests/system/optout/ns2/controls.conf.j2 diff --git a/bin/tests/system/_common/controls.conf.in b/bin/tests/system/_common/controls.conf.in deleted file mode 100644 index 429a2d32be..0000000000 --- a/bin/tests/system/_common/controls.conf.in +++ /dev/null @@ -1,9 +0,0 @@ -key rndc_key { - secret "1234abcd8765"; - algorithm @DEFAULT_HMAC@; -}; - -controls { - inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; -}; - diff --git a/bin/tests/system/_common/controls.conf.j2 b/bin/tests/system/_common/controls.conf.j2 new file mode 100644 index 0000000000..975299f63f --- /dev/null +++ b/bin/tests/system/_common/controls.conf.j2 @@ -0,0 +1,4 @@ +{% include "_common/rndc.key" %} +controls { + inet @ns.ip@ port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/allow_query/.gitignore b/bin/tests/system/allow_query/.gitignore index f928597443..f40b21b45f 100644 --- a/bin/tests/system/allow_query/.gitignore +++ b/bin/tests/system/allow_query/.gitignore @@ -1,2 +1 @@ /ns2/named.conf -/ns2/controls.conf diff --git a/bin/tests/system/allow_query/ns2/controls.conf.j2 b/bin/tests/system/allow_query/ns2/controls.conf.j2 deleted file mode 120000 index f1371a044e..0000000000 --- a/bin/tests/system/allow_query/ns2/controls.conf.j2 +++ /dev/null @@ -1 +0,0 @@ -../../_common/controls.conf.in \ No newline at end of file diff --git a/bin/tests/system/allow_query/ns2/named.conf.j2 b/bin/tests/system/allow_query/ns2/named.conf.j2 index fec3ddd66f..74ce1291dd 100644 --- a/bin/tests/system/allow_query/ns2/named.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named02.conf.j2 b/bin/tests/system/allow_query/ns2/named02.conf.j2 index 95a78b2ff0..4d999000d4 100644 --- a/bin/tests/system/allow_query/ns2/named02.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named02.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named03.conf.j2 b/bin/tests/system/allow_query/ns2/named03.conf.j2 index fd6add2b19..d1265a2e93 100644 --- a/bin/tests/system/allow_query/ns2/named03.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named03.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named04.conf.j2 b/bin/tests/system/allow_query/ns2/named04.conf.j2 index f6d231dc6a..c6e9c4c7e8 100644 --- a/bin/tests/system/allow_query/ns2/named04.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named04.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named05.conf.j2 b/bin/tests/system/allow_query/ns2/named05.conf.j2 index fe6cc0c6c0..535ff1a5d2 100644 --- a/bin/tests/system/allow_query/ns2/named05.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named05.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named06.conf.j2 b/bin/tests/system/allow_query/ns2/named06.conf.j2 index 1f78f131ce..958eaac888 100644 --- a/bin/tests/system/allow_query/ns2/named06.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named06.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named07.conf.j2 b/bin/tests/system/allow_query/ns2/named07.conf.j2 index 15dca8f101..9e9d871267 100644 --- a/bin/tests/system/allow_query/ns2/named07.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named07.conf.j2 @@ -23,7 +23,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named08.conf.j2 b/bin/tests/system/allow_query/ns2/named08.conf.j2 index c2eed2dbf2..ab59d75bec 100644 --- a/bin/tests/system/allow_query/ns2/named08.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named08.conf.j2 @@ -23,7 +23,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named09.conf.j2 b/bin/tests/system/allow_query/ns2/named09.conf.j2 index 248da9e7bb..88277e8909 100644 --- a/bin/tests/system/allow_query/ns2/named09.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named09.conf.j2 @@ -23,7 +23,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named10.conf.j2 b/bin/tests/system/allow_query/ns2/named10.conf.j2 index 1dbafbd773..0425b951ca 100644 --- a/bin/tests/system/allow_query/ns2/named10.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named10.conf.j2 @@ -26,7 +26,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named11.conf.j2 b/bin/tests/system/allow_query/ns2/named11.conf.j2 index 4d961a5776..62ba892898 100644 --- a/bin/tests/system/allow_query/ns2/named11.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named11.conf.j2 @@ -32,7 +32,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named12.conf.j2 b/bin/tests/system/allow_query/ns2/named12.conf.j2 index 75ab352676..867d983655 100644 --- a/bin/tests/system/allow_query/ns2/named12.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named12.conf.j2 @@ -26,7 +26,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named21.conf.j2 b/bin/tests/system/allow_query/ns2/named21.conf.j2 index b120f2aaa5..173eff84bd 100644 --- a/bin/tests/system/allow_query/ns2/named21.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named21.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named22.conf.j2 b/bin/tests/system/allow_query/ns2/named22.conf.j2 index 37ccedcc8d..b589c0a538 100644 --- a/bin/tests/system/allow_query/ns2/named22.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named22.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named23.conf.j2 b/bin/tests/system/allow_query/ns2/named23.conf.j2 index c43e232f16..cca3f2b055 100644 --- a/bin/tests/system/allow_query/ns2/named23.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named23.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named24.conf.j2 b/bin/tests/system/allow_query/ns2/named24.conf.j2 index 6cdbeec18b..3441ca5ad9 100644 --- a/bin/tests/system/allow_query/ns2/named24.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named24.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named25.conf.j2 b/bin/tests/system/allow_query/ns2/named25.conf.j2 index 51e247ca92..ecdc77cc30 100644 --- a/bin/tests/system/allow_query/ns2/named25.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named25.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named26.conf.j2 b/bin/tests/system/allow_query/ns2/named26.conf.j2 index a72136c879..5f20c6d62a 100644 --- a/bin/tests/system/allow_query/ns2/named26.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named26.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named27.conf.j2 b/bin/tests/system/allow_query/ns2/named27.conf.j2 index 4c3f958d0b..d1a414385e 100644 --- a/bin/tests/system/allow_query/ns2/named27.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named27.conf.j2 @@ -22,7 +22,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named28.conf.j2 b/bin/tests/system/allow_query/ns2/named28.conf.j2 index 8db4555f8d..618643faad 100644 --- a/bin/tests/system/allow_query/ns2/named28.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named28.conf.j2 @@ -22,7 +22,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named29.conf.j2 b/bin/tests/system/allow_query/ns2/named29.conf.j2 index 2031a6ea51..67d2745f82 100644 --- a/bin/tests/system/allow_query/ns2/named29.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named29.conf.j2 @@ -22,7 +22,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named30.conf.j2 b/bin/tests/system/allow_query/ns2/named30.conf.j2 index 0a90219efa..3e64bd36e8 100644 --- a/bin/tests/system/allow_query/ns2/named30.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named30.conf.j2 @@ -25,7 +25,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named31.conf.j2 b/bin/tests/system/allow_query/ns2/named31.conf.j2 index faec50e6a2..6636da1ddf 100644 --- a/bin/tests/system/allow_query/ns2/named31.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named31.conf.j2 @@ -32,7 +32,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named32.conf.j2 b/bin/tests/system/allow_query/ns2/named32.conf.j2 index dcd663ba0a..94ff36dac5 100644 --- a/bin/tests/system/allow_query/ns2/named32.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named32.conf.j2 @@ -25,7 +25,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named33.conf.j2 b/bin/tests/system/allow_query/ns2/named33.conf.j2 index 3721e62f38..e2b49900b7 100644 --- a/bin/tests/system/allow_query/ns2/named33.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named33.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named34.conf.j2 b/bin/tests/system/allow_query/ns2/named34.conf.j2 index 09125ef021..96d1f99207 100644 --- a/bin/tests/system/allow_query/ns2/named34.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named34.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named40.conf.j2 b/bin/tests/system/allow_query/ns2/named40.conf.j2 index 2349d92357..388de212ff 100644 --- a/bin/tests/system/allow_query/ns2/named40.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named40.conf.j2 @@ -34,7 +34,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named53.conf.j2 b/bin/tests/system/allow_query/ns2/named53.conf.j2 index d3463b2796..4a5f19fde1 100644 --- a/bin/tests/system/allow_query/ns2/named53.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named53.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named54.conf.j2 b/bin/tests/system/allow_query/ns2/named54.conf.j2 index 5dddf8998e..57fc8f9f16 100644 --- a/bin/tests/system/allow_query/ns2/named54.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named54.conf.j2 @@ -21,7 +21,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} zone "." { type hint; diff --git a/bin/tests/system/allow_query/ns2/named55.conf.j2 b/bin/tests/system/allow_query/ns2/named55.conf.j2 index 4a0880fed3..8793c1e39a 100644 --- a/bin/tests/system/allow_query/ns2/named55.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named55.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named56.conf.j2 b/bin/tests/system/allow_query/ns2/named56.conf.j2 index 564063b17e..4f8c1d7fd5 100644 --- a/bin/tests/system/allow_query/ns2/named56.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named56.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { diff --git a/bin/tests/system/allow_query/ns2/named57.conf.j2 b/bin/tests/system/allow_query/ns2/named57.conf.j2 index db5f3d0800..6bb685b9a8 100644 --- a/bin/tests/system/allow_query/ns2/named57.conf.j2 +++ b/bin/tests/system/allow_query/ns2/named57.conf.j2 @@ -20,7 +20,7 @@ options { dnssec-validation no; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} view "internal" { allow-query-on { any; }; diff --git a/bin/tests/system/allow_query/tests_sh_allow_query.py b/bin/tests/system/allow_query/tests_sh_allow_query.py index 0a17c7eea5..bcc67ed23a 100644 --- a/bin/tests/system/allow_query/tests_sh_allow_query.py +++ b/bin/tests/system/allow_query/tests_sh_allow_query.py @@ -14,7 +14,6 @@ import pytest pytestmark = pytest.mark.extra_artifacts( [ "dig.out.*", - "ns2/controls.conf", ] ) diff --git a/bin/tests/system/optout/ns2/controls.conf.j2 b/bin/tests/system/optout/ns2/controls.conf.j2 deleted file mode 120000 index f1371a044e..0000000000 --- a/bin/tests/system/optout/ns2/controls.conf.j2 +++ /dev/null @@ -1 +0,0 @@ -../../_common/controls.conf.in \ No newline at end of file diff --git a/bin/tests/system/optout/ns2/named.conf.j2 b/bin/tests/system/optout/ns2/named.conf.j2 index 6bfe881451..ee069605f2 100644 --- a/bin/tests/system/optout/ns2/named.conf.j2 +++ b/bin/tests/system/optout/ns2/named.conf.j2 @@ -27,7 +27,7 @@ options { sig-signing-signatures 900; }; -include "controls.conf"; +{% include "_common/controls.conf.j2" %} dnssec-policy "optout" { keys { diff --git a/bin/tests/system/optout/tests_optout.py b/bin/tests/system/optout/tests_optout.py index ef157b3e9e..f7be26d6e9 100755 --- a/bin/tests/system/optout/tests_optout.py +++ b/bin/tests/system/optout/tests_optout.py @@ -34,7 +34,6 @@ pytestmark = [ "ns2/*.signed", "ns2/*.jnl", "ns2/*.jbk", - "ns2/controls.conf", "ns2/dsset-*", "ns2/K*", ] From 23ad1bf27b771f5474b793d72e31ab97f52c56c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Fri, 20 Mar 2026 10:09:55 +0100 Subject: [PATCH 7/8] Create common templates for test zones Add commonly used zone-related data (config snippet and zone file snippets) as templates which can be reused by filling in different data. Adjust the isctest.template.Zone to use filepath argument rather than filename for clarity. (cherry picked from commit 317cd1277910a2c680a043434ac7f9fa6d8df131) --- bin/tests/system/_common/root.hint.conf | 4 ++++ bin/tests/system/_common/zones.conf.j2 | 10 ++++++++++ .../system/_common/zones/delegations.partial.db.j2 | 5 +++++ bin/tests/system/_common/zones/ns.partial.db.j2 | 2 ++ bin/tests/system/_common/zones/root.db.j2.manual | 13 +++++++++++++ bin/tests/system/_common/zones/soa.partial.db.j2 | 9 +++++++++ .../system/_common/zones/template.db.j2.manual | 7 +++++++ bin/tests/system/isctest/template.py | 11 ++++++----- bin/tests/system/rollover/setup.py | 6 ++++-- 9 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 bin/tests/system/_common/root.hint.conf create mode 100644 bin/tests/system/_common/zones.conf.j2 create mode 100644 bin/tests/system/_common/zones/delegations.partial.db.j2 create mode 100644 bin/tests/system/_common/zones/ns.partial.db.j2 create mode 100644 bin/tests/system/_common/zones/root.db.j2.manual create mode 100644 bin/tests/system/_common/zones/soa.partial.db.j2 create mode 100644 bin/tests/system/_common/zones/template.db.j2.manual diff --git a/bin/tests/system/_common/root.hint.conf b/bin/tests/system/_common/root.hint.conf new file mode 100644 index 0000000000..465c5ad783 --- /dev/null +++ b/bin/tests/system/_common/root.hint.conf @@ -0,0 +1,4 @@ +zone "." { + type hint; + file "../../_common/root.hint"; +}; diff --git a/bin/tests/system/_common/zones.conf.j2 b/bin/tests/system/_common/zones.conf.j2 new file mode 100644 index 0000000000..27bcb942be --- /dev/null +++ b/bin/tests/system/_common/zones.conf.j2 @@ -0,0 +1,10 @@ +{% if zones is defined and zones %} +{% for zone in zones.values() %} +{% if zone.ns.name == ns.name %} +zone "@zone.name@" { + type @zone.type@; + file "@zone.filepath@"; +}; +{% endif %} +{% endfor %} +{% endif %} diff --git a/bin/tests/system/_common/zones/delegations.partial.db.j2 b/bin/tests/system/_common/zones/delegations.partial.db.j2 new file mode 100644 index 0000000000..a44d2adf06 --- /dev/null +++ b/bin/tests/system/_common/zones/delegations.partial.db.j2 @@ -0,0 +1,5 @@ +{% if delegations is defined and delegations %} +{% for zone in delegations %} +{% include '_common/zones/ns.partial.db.j2' %} +{% endfor %} +{% endif %} diff --git a/bin/tests/system/_common/zones/ns.partial.db.j2 b/bin/tests/system/_common/zones/ns.partial.db.j2 new file mode 100644 index 0000000000..27a2c2ecbb --- /dev/null +++ b/bin/tests/system/_common/zones/ns.partial.db.j2 @@ -0,0 +1,2 @@ +@zone.name@. NS @zone.ns.name@.@zone.name@. +@zone.ns.name@.@zone.name@. A @zone.ns.ip@ diff --git a/bin/tests/system/_common/zones/root.db.j2.manual b/bin/tests/system/_common/zones/root.db.j2.manual new file mode 100644 index 0000000000..9f4de112b4 --- /dev/null +++ b/bin/tests/system/_common/zones/root.db.j2.manual @@ -0,0 +1,13 @@ +$TTL 300 +. IN SOA . a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum +) + +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +{% include '_common/zones/delegations.partial.db.j2' %} diff --git a/bin/tests/system/_common/zones/soa.partial.db.j2 b/bin/tests/system/_common/zones/soa.partial.db.j2 new file mode 100644 index 0000000000..3fae403539 --- /dev/null +++ b/bin/tests/system/_common/zones/soa.partial.db.j2 @@ -0,0 +1,9 @@ +$ORIGIN @zone.name@. +$TTL 300 +{% raw %}@{% endraw %} IN SOA @zone.ns.name@.@zone.name@. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) +) diff --git a/bin/tests/system/_common/zones/template.db.j2.manual b/bin/tests/system/_common/zones/template.db.j2.manual new file mode 100644 index 0000000000..600ffbbc4b --- /dev/null +++ b/bin/tests/system/_common/zones/template.db.j2.manual @@ -0,0 +1,7 @@ +{% include '_common/zones/soa.partial.db.j2' %} +{% include '_common/zones/ns.partial.db.j2' %} +{% include '_common/zones/delegations.partial.db.j2' %} + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index f0668880c6..6c6b628b94 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -11,7 +11,7 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from re import compile as Re from typing import Any @@ -144,11 +144,12 @@ class Zone: name: str ns: Nameserver type: str = "primary" - filename: str | None = None + filepath: Path | None = field(default=None) - def __post_init__(self): - if self.filename is None: - self.filename = f"{self.name}.db" + def __post_init__(self) -> None: + if self.filepath is None: + base = "root" if self.name == "." else self.name + self.filepath = Path(f"zones/{base}.db") @dataclass diff --git a/bin/tests/system/rollover/setup.py b/bin/tests/system/rollover/setup.py index 91b15006d7..c71b871562 100644 --- a/bin/tests/system/rollover/setup.py +++ b/bin/tests/system/rollover/setup.py @@ -9,6 +9,8 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. +from pathlib import Path + import shutil from isctest.kasp import private_type_record @@ -50,7 +52,7 @@ def configure_tld(zonename: str, delegations: list[Zone]) -> Zone: templates.render(f"ns2/{outfile}", tdata, template=f"ns2/{template}") signer(f"-P -x -O full -o {zonename} -f {outfile}.signed {outfile}", cwd="ns2") - return Zone(zonename, NS2, filename=f"{outfile}.signed") + return Zone(zonename, NS2, filepath=Path(f"{outfile}.signed")) def configure_root(delegations: list[Zone]) -> TrustAnchor: @@ -1381,7 +1383,7 @@ def configure_straight2none(tld: str) -> list[Zone]: render_and_sign_zone(zonename, [csk_name], extra_options="-z") zonename = f"going-straight-to-none-dynamic.{tld}" - zones.append(Zone(zonename, NS3, filename=f"{zonename}.db.signed")) + zones.append(Zone(zonename, NS3, filepath=Path(f"{zonename}.db.signed"))) isctest.log.info(f"setup {zonename}") # Key generation. csk_name = keygen(f"-f KSK {keytimes} {zonename}", cwd="ns3").out.strip() From 02de870059ede936f9120f311e643c81529c19c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= Date: Wed, 20 May 2026 14:34:02 +0000 Subject: [PATCH 8/8] Restrict cross-test jinja2 includes to _common/ The previous loader was a FileSystemLoader rooted at $srcdir, which allowed any system test to include any other test's templates -- a wider scope than intended. Every existing cross-test include already targets _common/, so make that the only path. ChoiceLoader + PrefixLoader keeps the existing '_common/foo.j2' path convention working without changes to call sites. The '_common/' prefix is deliberately kept rather than dropping it by rooting the FileSystemLoader at _common/ directly: - It signals at the include site that the file is a shared template, not a sibling of the current test; readers don't need to know the loader configuration to understand where the file lives. - It prevents shadowing: a test-local 'controls.conf.j2' would not collide with the shared one, and the unqualified name keeps its test-local meaning. - It makes the dependency greppable: 'grep -rl _common/' identifies every test that consumes shared snippets. Assisted-by: Claude:claude-opus-4-7 (cherry picked from commit c2c2be9be0481eb8966884bb33c0153e58e1800f) --- bin/tests/system/isctest/template.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bin/tests/system/isctest/template.py b/bin/tests/system/isctest/template.py index 6c6b628b94..83e6ce8691 100644 --- a/bin/tests/system/isctest/template.py +++ b/bin/tests/system/isctest/template.py @@ -40,10 +40,16 @@ class TemplateEngine: self.directory = Path(directory) self.env_vars = dict(env_vars) self.j2env = jinja2.Environment( - loader=jinja2.FileSystemLoader( + loader=jinja2.ChoiceLoader( [ - str(self.directory), - str(ALL["srcdir"]), # to allow _common/ includes + jinja2.FileSystemLoader(self.directory), + jinja2.PrefixLoader( + { + "_common": jinja2.FileSystemLoader( + Path(ALL["srcdir"]) / "_common" + ), + } + ), ] ), undefined=jinja2.StrictUndefined,