1let
2 common =
3 { pkgs, ... }:
4 {
5 networking.firewall.enable = false;
6 networking.useDHCP = false;
7 # for a host utility with IPv6 support
8 environment.systemPackages = [ pkgs.bind ];
9 };
10in
11import ./make-test-python.nix (
12 { pkgs, ... }:
13 {
14 name = "nsd";
15 meta = with pkgs.lib.maintainers; {
16 maintainers = [ aszlig ];
17 };
18
19 nodes = {
20 clientv4 =
21 { lib, nodes, ... }:
22 {
23 imports = [ common ];
24 networking.nameservers = lib.mkForce [
25 (lib.head nodes.server.config.networking.interfaces.eth1.ipv4.addresses).address
26 ];
27 networking.interfaces.eth1.ipv4.addresses = [
28 {
29 address = "192.168.0.2";
30 prefixLength = 24;
31 }
32 ];
33 };
34
35 clientv6 =
36 { lib, nodes, ... }:
37 {
38 imports = [ common ];
39 networking.nameservers = lib.mkForce [
40 (lib.head nodes.server.config.networking.interfaces.eth1.ipv6.addresses).address
41 ];
42 networking.interfaces.eth1.ipv4.addresses = [
43 {
44 address = "dead:beef::2";
45 prefixLength = 24;
46 }
47 ];
48 };
49
50 server =
51 { lib, ... }:
52 {
53 imports = [ common ];
54 networking.interfaces.eth1.ipv4.addresses = [
55 {
56 address = "192.168.0.1";
57 prefixLength = 24;
58 }
59 ];
60 networking.interfaces.eth1.ipv6.addresses = [
61 {
62 address = "dead:beef::1";
63 prefixLength = 64;
64 }
65 ];
66 services.nsd.enable = true;
67 services.nsd.rootServer = true;
68 services.nsd.interfaces = lib.mkForce [ ];
69 services.nsd.keys."tsig.example.com." = {
70 algorithm = "hmac-sha256";
71 keyFile = pkgs.writeTextFile {
72 name = "tsig.example.com.";
73 text = "aR3FJA92+bxRSyosadsJ8Aeeav5TngQW/H/EF9veXbc=";
74 };
75 };
76 services.nsd.zones."example.com.".data = ''
77 @ SOA ns.example.com noc.example.com 666 7200 3600 1209600 3600
78 ipv4 A 1.2.3.4
79 ipv6 AAAA abcd::eeff
80 deleg NS ns.example.com
81 ns A 192.168.0.1
82 ns AAAA dead:beef::1
83 '';
84 services.nsd.zones."example.com.".provideXFR = [ "0.0.0.0 tsig.example.com." ];
85 services.nsd.zones."deleg.example.com.".data = ''
86 @ SOA ns.example.com noc.example.com 666 7200 3600 1209600 3600
87 @ A 9.8.7.6
88 @ AAAA fedc::bbaa
89 '';
90 services.nsd.zones.".".data = ''
91 @ SOA ns.example.com noc.example.com 666 7200 3600 1209600 3600
92 root A 1.8.7.4
93 root AAAA acbd::4
94 '';
95 };
96 };
97
98 testScript = ''
99 start_all()
100
101 clientv4.wait_for_unit("network.target")
102 clientv6.wait_for_unit("network.target")
103 server.wait_for_unit("nsd.service")
104
105 with subtest("server tsig.example.com."):
106 expected_tsig = " secret: \"aR3FJA92+bxRSyosadsJ8Aeeav5TngQW/H/EF9veXbc=\"\n"
107 tsig=server.succeed("cat /var/lib/nsd/private/tsig.example.com.")
108 assert expected_tsig == tsig, f"Expected /var/lib/nsd/private/tsig.example.com. to contain '{expected_tsig}', but found '{tsig}'"
109
110 def assert_host(type, rr, query, expected):
111 self = clientv4 if type == 4 else clientv6
112 out = self.succeed(f"host -{type} -t {rr} {query}").rstrip()
113 self.log(f"output: {out}")
114 import re
115 assert re.search(
116 expected, out
117 ), f"DNS IPv{type} query on {query} gave '{out}' instead of '{expected}'"
118
119
120 for ipv in 4, 6:
121 with subtest(f"IPv{ipv}"):
122 assert_host(ipv, "a", "example.com", "has no [^ ]+ record")
123 assert_host(ipv, "aaaa", "example.com", "has no [^ ]+ record")
124
125 assert_host(ipv, "soa", "example.com", "SOA.*?noc\.example\.com")
126 assert_host(ipv, "a", "ipv4.example.com", "address 1.2.3.4$")
127 assert_host(ipv, "aaaa", "ipv6.example.com", "address abcd::eeff$")
128
129 assert_host(ipv, "a", "deleg.example.com", "address 9.8.7.6$")
130 assert_host(ipv, "aaaa", "deleg.example.com", "address fedc::bbaa$")
131
132 assert_host(ipv, "a", "root", "address 1.8.7.4$")
133 assert_host(ipv, "aaaa", "root", "address acbd::4$")
134 '';
135 }
136)