python3Packages.xknx: 1.1.0 -> 1.2.0
[NixPkgs.git] / nixos / tests / knot.nix
blob203fd03fac26f20dd86fed268e8171fe77133d3d
1 import ./make-test-python.nix ({ pkgs, lib, ...} :
2 let
3   common = {
4     networking.firewall.enable = false;
5     networking.useDHCP = false;
6   };
7   exampleZone = pkgs.writeTextDir "example.com.zone" ''
8       @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
9       @       NS      ns1
10       @       NS      ns2
11       ns1     A       192.168.0.1
12       ns1     AAAA    fd00::1
13       ns2     A       192.168.0.2
14       ns2     AAAA    fd00::2
15       www     A       192.0.2.1
16       www     AAAA    2001:DB8::1
17       sub     NS      ns.example.com.
18   '';
19   delegatedZone = pkgs.writeTextDir "sub.example.com.zone" ''
20       @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
21       @       NS      ns1.example.com.
22       @       NS      ns2.example.com.
23       @       A       192.0.2.2
24       @       AAAA    2001:DB8::2
25   '';
27   knotZonesEnv = pkgs.buildEnv {
28     name = "knot-zones";
29     paths = [ exampleZone delegatedZone ];
30   };
31   # DO NOT USE pkgs.writeText IN PRODUCTION. This put secrets in the nix store!
32   tsigFile = pkgs.writeText "tsig.conf" ''
33     key:
34       - id: slave_key
35         algorithm: hmac-sha256
36         secret: zOYgOgnzx3TGe5J5I/0kxd7gTcxXhLYMEq3Ek3fY37s=
37   '';
38 in {
39   name = "knot";
40   meta = with pkgs.lib.maintainers; {
41     maintainers = [ hexa ];
42   };
45   nodes = {
46     master = { lib, ... }: {
47       imports = [ common ];
49       # trigger sched_setaffinity syscall
50       virtualisation.cores = 2;
52       networking.interfaces.eth1 = {
53         ipv4.addresses = lib.mkForce [
54           { address = "192.168.0.1"; prefixLength = 24; }
55         ];
56         ipv6.addresses = lib.mkForce [
57           { address = "fd00::1"; prefixLength = 64; }
58         ];
59       };
60       services.knot.enable = true;
61       services.knot.extraArgs = [ "-v" ];
62       services.knot.keyFiles = [ tsigFile ];
63       services.knot.extraConfig = ''
64         server:
65             listen: 0.0.0.0@53
66             listen: ::@53
68         acl:
69           - id: slave_acl
70             address: 192.168.0.2
71             key: slave_key
72             action: transfer
74         remote:
75           - id: slave
76             address: 192.168.0.2@53
78         template:
79           - id: default
80             storage: ${knotZonesEnv}
81             notify: [slave]
82             acl: [slave_acl]
83             dnssec-signing: on
84             # Input-only zone files
85             # https://www.knot-dns.cz/docs/2.8/html/operation.html#example-3
86             # prevents modification of the zonefiles, since the zonefiles are immutable
87             zonefile-sync: -1
88             zonefile-load: difference
89             journal-content: changes
90             # move databases below the state directory, because they need to be writable
91             journal-db: /var/lib/knot/journal
92             kasp-db: /var/lib/knot/kasp
93             timer-db: /var/lib/knot/timer
95         zone:
96           - domain: example.com
97             file: example.com.zone
99           - domain: sub.example.com
100             file: sub.example.com.zone
102         log:
103           - target: syslog
104             any: info
105       '';
106     };
108     slave = { lib, ... }: {
109       imports = [ common ];
110       networking.interfaces.eth1 = {
111         ipv4.addresses = lib.mkForce [
112           { address = "192.168.0.2"; prefixLength = 24; }
113         ];
114         ipv6.addresses = lib.mkForce [
115           { address = "fd00::2"; prefixLength = 64; }
116         ];
117       };
118       services.knot.enable = true;
119       services.knot.keyFiles = [ tsigFile ];
120       services.knot.extraArgs = [ "-v" ];
121       services.knot.extraConfig = ''
122         server:
123             listen: 0.0.0.0@53
124             listen: ::@53
126         acl:
127           - id: notify_from_master
128             address: 192.168.0.1
129             action: notify
131         remote:
132           - id: master
133             address: 192.168.0.1@53
134             key: slave_key
136         template:
137           - id: default
138             master: master
139             acl: [notify_from_master]
140             # zonefileless setup
141             # https://www.knot-dns.cz/docs/2.8/html/operation.html#example-2
142             zonefile-sync: -1
143             zonefile-load: none
144             journal-content: all
145             # move databases below the state directory, because they need to be writable
146             journal-db: /var/lib/knot/journal
147             kasp-db: /var/lib/knot/kasp
148             timer-db: /var/lib/knot/timer
150         zone:
151           - domain: example.com
152             file: example.com.zone
154           - domain: sub.example.com
155             file: sub.example.com.zone
157         log:
158           - target: syslog
159             any: info
160       '';
161     };
162     client = { lib, nodes, ... }: {
163       imports = [ common ];
164       networking.interfaces.eth1 = {
165         ipv4.addresses = [
166           { address = "192.168.0.3"; prefixLength = 24; }
167         ];
168         ipv6.addresses = [
169           { address = "fd00::3"; prefixLength = 64; }
170         ];
171       };
172       environment.systemPackages = [ pkgs.knot-dns ];
173     };
174   };
176   testScript = { nodes, ... }: let
177     master4 = (lib.head nodes.master.config.networking.interfaces.eth1.ipv4.addresses).address;
178     master6 = (lib.head nodes.master.config.networking.interfaces.eth1.ipv6.addresses).address;
180     slave4 = (lib.head nodes.slave.config.networking.interfaces.eth1.ipv4.addresses).address;
181     slave6 = (lib.head nodes.slave.config.networking.interfaces.eth1.ipv6.addresses).address;
182   in ''
183     import re
185     start_all()
187     client.wait_for_unit("network.target")
188     master.wait_for_unit("knot.service")
189     slave.wait_for_unit("knot.service")
192     def test(host, query_type, query, pattern):
193         out = client.succeed(f"khost -t {query_type} {query} {host}").strip()
194         client.log(f"{host} replied with: {out}")
195         assert re.search(pattern, out), f'Did not match "{pattern}"'
198     for host in ("${master4}", "${master6}", "${slave4}", "${slave6}"):
199         with subtest(f"Interrogate {host}"):
200             test(host, "SOA", "example.com", r"start of authority.*noc\.example\.com\.")
201             test(host, "A", "example.com", r"has no [^ ]+ record")
202             test(host, "AAAA", "example.com", r"has no [^ ]+ record")
204             test(host, "A", "www.example.com", r"address 192.0.2.1$")
205             test(host, "AAAA", "www.example.com", r"address 2001:db8::1$")
207             test(host, "NS", "sub.example.com", r"nameserver is ns\d\.example\.com.$")
208             test(host, "A", "sub.example.com", r"address 192.0.2.2$")
209             test(host, "AAAA", "sub.example.com", r"address 2001:db8::2$")
211             test(host, "RRSIG", "www.example.com", r"RR set signature is")
212             test(host, "DNSKEY", "example.com", r"DNSSEC key is")
214     master.log(master.succeed("systemd-analyze security knot.service | grep -v '✓'"))
215   '';