nixos/tests/knot: migrate to runTest

Part of #386873
This commit is contained in:
Martin Weinelt 2025-03-10 20:30:17 +01:00
parent 5d3d6037d8
commit 41ff93c1d8
No known key found for this signature in database
GPG key ID: 87C1E9888F856759
2 changed files with 230 additions and 228 deletions

View file

@ -589,7 +589,7 @@ in {
keymap = handleTest ./keymap.nix {}; keymap = handleTest ./keymap.nix {};
kimai = handleTest ./kimai.nix {}; kimai = handleTest ./kimai.nix {};
kmonad = runTest ./kmonad.nix; kmonad = runTest ./kmonad.nix;
knot = handleTest ./knot.nix {}; knot = runTest ./knot.nix;
komga = handleTest ./komga.nix {}; komga = handleTest ./komga.nix {};
krb5 = discoverTests (import ./krb5); krb5 = discoverTests (import ./krb5);
ksm = handleTest ./ksm.nix {}; ksm = handleTest ./ksm.nix {};

View file

@ -1,254 +1,256 @@
import ./make-test-python.nix ( {
{ pkgs, lib, ... }: pkgs,
let lib,
common = { ...
networking.firewall.enable = false; }:
networking.useDHCP = false; let
}; common = {
exampleZone = pkgs.writeTextDir "example.com.zone" '' networking.firewall.enable = false;
@ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800 networking.useDHCP = false;
@ NS ns1 };
@ NS ns2 exampleZone = pkgs.writeTextDir "example.com.zone" ''
ns1 A 192.168.0.1 @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
ns1 AAAA fd00::1 @ NS ns1
ns2 A 192.168.0.2 @ NS ns2
ns2 AAAA fd00::2 ns1 A 192.168.0.1
www A 192.0.2.1 ns1 AAAA fd00::1
www AAAA 2001:DB8::1 ns2 A 192.168.0.2
sub NS ns.example.com. ns2 AAAA fd00::2
''; www A 192.0.2.1
delegatedZone = pkgs.writeTextDir "sub.example.com.zone" '' www AAAA 2001:DB8::1
@ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800 sub NS ns.example.com.
@ NS ns1.example.com. '';
@ NS ns2.example.com. delegatedZone = pkgs.writeTextDir "sub.example.com.zone" ''
@ A 192.0.2.2 @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
@ AAAA 2001:DB8::2 @ NS ns1.example.com.
''; @ NS ns2.example.com.
@ A 192.0.2.2
@ AAAA 2001:DB8::2
'';
knotZonesEnv = pkgs.buildEnv { knotZonesEnv = pkgs.buildEnv {
name = "knot-zones"; name = "knot-zones";
paths = [ paths = [
exampleZone exampleZone
delegatedZone delegatedZone
]; ];
}; };
# DO NOT USE pkgs.writeText IN PRODUCTION. This put secrets in the nix store! # DO NOT USE pkgs.writeText IN PRODUCTION. This put secrets in the nix store!
tsigFile = pkgs.writeText "tsig.conf" '' tsigFile = pkgs.writeText "tsig.conf" ''
key: key:
- id: xfr_key - id: xfr_key
algorithm: hmac-sha256 algorithm: hmac-sha256
secret: zOYgOgnzx3TGe5J5I/0kxd7gTcxXhLYMEq3Ek3fY37s= secret: zOYgOgnzx3TGe5J5I/0kxd7gTcxXhLYMEq3Ek3fY37s=
''; '';
in in
{ {
name = "knot"; name = "knot";
meta = with pkgs.lib.maintainers; { meta = with pkgs.lib.maintainers; {
maintainers = [ hexa ]; maintainers = [ hexa ];
}; };
nodes = { nodes = {
primary = primary =
{ lib, ... }: { lib, ... }:
{ {
imports = [ common ]; imports = [ common ];
# trigger sched_setaffinity syscall # trigger sched_setaffinity syscall
virtualisation.cores = 2; virtualisation.cores = 2;
networking.interfaces.eth1 = { networking.interfaces.eth1 = {
ipv4.addresses = lib.mkForce [ ipv4.addresses = lib.mkForce [
{ {
address = "192.168.0.1"; address = "192.168.0.1";
prefixLength = 24; prefixLength = 24;
} }
];
ipv6.addresses = lib.mkForce [
{
address = "fd00::1";
prefixLength = 64;
}
];
};
services.knot.enable = true;
services.knot.extraArgs = [ "-v" ];
services.knot.keyFiles = [ tsigFile ];
services.knot.settings = {
server = {
listen = [
"0.0.0.0@53"
"::@53"
]; ];
ipv6.addresses = lib.mkForce [ listen-quic = [
{ "0.0.0.0@853"
address = "fd00::1"; "::@853"
prefixLength = 64;
}
]; ];
automatic-acl = true;
}; };
services.knot.enable = true;
services.knot.extraArgs = [ "-v" ];
services.knot.keyFiles = [ tsigFile ];
services.knot.settings = {
server = {
listen = [
"0.0.0.0@53"
"::@53"
];
listen-quic = [
"0.0.0.0@853"
"::@853"
];
automatic-acl = true;
};
acl.secondary_acl = { acl.secondary_acl = {
address = "192.168.0.2";
key = "xfr_key";
action = "transfer";
};
remote.secondary.address = "192.168.0.2@53";
template.default = {
storage = knotZonesEnv;
notify = [ "secondary" ];
acl = [ "secondary_acl" ];
dnssec-signing = true;
# Input-only zone files
# https://www.knot-dns.cz/docs/2.8/html/operation.html#example-3
# prevents modification of the zonefiles, since the zonefiles are immutable
zonefile-sync = -1;
zonefile-load = "difference";
journal-content = "changes";
};
zone = {
"example.com".file = "example.com.zone";
"sub.example.com".file = "sub.example.com.zone";
};
log.syslog.any = "info";
};
};
secondary =
{ lib, ... }:
{
imports = [ common ];
networking.interfaces.eth1 = {
ipv4.addresses = lib.mkForce [
{
address = "192.168.0.2"; address = "192.168.0.2";
key = "xfr_key"; prefixLength = 24;
action = "transfer"; }
}; ];
ipv6.addresses = lib.mkForce [
remote.secondary.address = "192.168.0.2@53"; {
address = "fd00::2";
template.default = { prefixLength = 64;
storage = knotZonesEnv; }
notify = [ "secondary" ]; ];
acl = [ "secondary_acl" ];
dnssec-signing = true;
# Input-only zone files
# https://www.knot-dns.cz/docs/2.8/html/operation.html#example-3
# prevents modification of the zonefiles, since the zonefiles are immutable
zonefile-sync = -1;
zonefile-load = "difference";
journal-content = "changes";
};
zone = {
"example.com".file = "example.com.zone";
"sub.example.com".file = "sub.example.com.zone";
};
log.syslog.any = "info";
};
}; };
services.knot.enable = true;
secondary = services.knot.keyFiles = [ tsigFile ];
{ lib, ... }: services.knot.extraArgs = [ "-v" ];
{ services.knot.settings = {
imports = [ common ]; server = {
networking.interfaces.eth1 = { automatic-acl = true;
ipv4.addresses = lib.mkForce [
{
address = "192.168.0.2";
prefixLength = 24;
}
];
ipv6.addresses = lib.mkForce [
{
address = "fd00::2";
prefixLength = 64;
}
];
}; };
services.knot.enable = true;
services.knot.keyFiles = [ tsigFile ];
services.knot.extraArgs = [ "-v" ];
services.knot.settings = {
server = {
automatic-acl = true;
};
xdp = { xdp = {
listen = [ listen = [
"eth1" "eth1"
]; ];
tcp = true; tcp = true;
};
remote.primary = {
address = "192.168.0.1@53";
key = "xfr_key";
};
remote.primary-quic = {
address = "192.168.0.1@853";
key = "xfr_key";
quic = true;
};
template.default = {
# zonefileless setup
# https://www.knot-dns.cz/docs/2.8/html/operation.html#example-2
zonefile-sync = "-1";
zonefile-load = "none";
journal-content = "all";
};
zone = {
"example.com" = {
master = "primary";
file = "example.com.zone";
};
"sub.example.com" = {
master = "primary-quic";
file = "sub.example.com.zone";
};
};
log.syslog.any = "debug";
}; };
remote.primary = {
address = "192.168.0.1@53";
key = "xfr_key";
};
remote.primary-quic = {
address = "192.168.0.1@853";
key = "xfr_key";
quic = true;
};
template.default = {
# zonefileless setup
# https://www.knot-dns.cz/docs/2.8/html/operation.html#example-2
zonefile-sync = "-1";
zonefile-load = "none";
journal-content = "all";
};
zone = {
"example.com" = {
master = "primary";
file = "example.com.zone";
};
"sub.example.com" = {
master = "primary-quic";
file = "sub.example.com.zone";
};
};
log.syslog.any = "debug";
}; };
client = };
{ lib, nodes, ... }: client =
{ { lib, nodes, ... }:
imports = [ common ]; {
networking.interfaces.eth1 = { imports = [ common ];
ipv4.addresses = [ networking.interfaces.eth1 = {
{ ipv4.addresses = [
address = "192.168.0.3"; {
prefixLength = 24; address = "192.168.0.3";
} prefixLength = 24;
]; }
ipv6.addresses = [ ];
{ ipv6.addresses = [
address = "fd00::3"; {
prefixLength = 64; address = "fd00::3";
} prefixLength = 64;
]; }
}; ];
environment.systemPackages = [ pkgs.knot-dns ];
}; };
}; environment.systemPackages = [ pkgs.knot-dns ];
};
};
testScript = testScript =
{ nodes, ... }: { nodes, ... }:
let let
primary4 = (lib.head nodes.primary.config.networking.interfaces.eth1.ipv4.addresses).address; primary4 = (lib.head nodes.primary.config.networking.interfaces.eth1.ipv4.addresses).address;
primary6 = (lib.head nodes.primary.config.networking.interfaces.eth1.ipv6.addresses).address; primary6 = (lib.head nodes.primary.config.networking.interfaces.eth1.ipv6.addresses).address;
secondary4 = (lib.head nodes.secondary.config.networking.interfaces.eth1.ipv4.addresses).address; secondary4 = (lib.head nodes.secondary.config.networking.interfaces.eth1.ipv4.addresses).address;
secondary6 = (lib.head nodes.secondary.config.networking.interfaces.eth1.ipv6.addresses).address; secondary6 = (lib.head nodes.secondary.config.networking.interfaces.eth1.ipv6.addresses).address;
in in
'' ''
import re import re
start_all() start_all()
client.wait_for_unit("network.target") client.wait_for_unit("network.target")
primary.wait_for_unit("knot.service") primary.wait_for_unit("knot.service")
secondary.wait_for_unit("knot.service") secondary.wait_for_unit("knot.service")
for zone in ("example.com.", "sub.example.com."): for zone in ("example.com.", "sub.example.com."):
secondary.wait_until_succeeds( secondary.wait_until_succeeds(
f"knotc zone-status {zone} | grep -q 'serial: 2019031302'" f"knotc zone-status {zone} | grep -q 'serial: 2019031302'"
) )
def test(host, query_type, query, pattern): def test(host, query_type, query, pattern):
out = client.succeed(f"khost -t {query_type} {query} {host}").strip() out = client.succeed(f"khost -t {query_type} {query} {host}").strip()
client.log(f"{host} replied with: {out}") client.log(f"{host} replied with: {out}")
assert re.search(pattern, out), f'Did not match "{pattern}"' assert re.search(pattern, out), f'Did not match "{pattern}"'
for host in ("${primary4}", "${primary6}", "${secondary4}", "${secondary6}"): for host in ("${primary4}", "${primary6}", "${secondary4}", "${secondary6}"):
with subtest(f"Interrogate {host}"): with subtest(f"Interrogate {host}"):
test(host, "SOA", "example.com", r"start of authority.*noc\.example\.com\.") test(host, "SOA", "example.com", r"start of authority.*noc\.example\.com\.")
test(host, "A", "example.com", r"has no [^ ]+ record") test(host, "A", "example.com", r"has no [^ ]+ record")
test(host, "AAAA", "example.com", r"has no [^ ]+ record") test(host, "AAAA", "example.com", r"has no [^ ]+ record")
test(host, "A", "www.example.com", r"address 192.0.2.1$") test(host, "A", "www.example.com", r"address 192.0.2.1$")
test(host, "AAAA", "www.example.com", r"address 2001:db8::1$") test(host, "AAAA", "www.example.com", r"address 2001:db8::1$")
test(host, "NS", "sub.example.com", r"nameserver is ns\d\.example\.com.$") test(host, "NS", "sub.example.com", r"nameserver is ns\d\.example\.com.$")
test(host, "A", "sub.example.com", r"address 192.0.2.2$") test(host, "A", "sub.example.com", r"address 192.0.2.2$")
test(host, "AAAA", "sub.example.com", r"address 2001:db8::2$") test(host, "AAAA", "sub.example.com", r"address 2001:db8::2$")
test(host, "RRSIG", "www.example.com", r"RR set signature is") test(host, "RRSIG", "www.example.com", r"RR set signature is")
test(host, "DNSKEY", "example.com", r"DNSSEC key is") test(host, "DNSKEY", "example.com", r"DNSSEC key is")
primary.log(primary.succeed("systemd-analyze security knot.service | grep -v ''")) primary.log(primary.succeed("systemd-analyze security knot.service | grep -v ''"))
''; '';
} }
)