198 lines
6.1 KiB
Nix
198 lines
6.1 KiB
Nix
import ./make-test.nix ({ pkgs, lib, ...} :
|
|
let
|
|
common = {
|
|
networking.firewall.enable = false;
|
|
networking.useDHCP = false;
|
|
};
|
|
exampleZone = pkgs.writeTextDir "example.com.zone" ''
|
|
@ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
|
|
@ NS ns1
|
|
@ NS ns2
|
|
ns1 A 192.168.0.1
|
|
ns1 AAAA fd00::1
|
|
ns2 A 192.168.0.2
|
|
ns2 AAAA fd00::2
|
|
www A 192.0.2.1
|
|
www AAAA 2001:DB8::1
|
|
sub NS ns.example.com.
|
|
'';
|
|
delegatedZone = pkgs.writeTextDir "sub.example.com.zone" ''
|
|
@ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
|
|
@ NS ns1.example.com.
|
|
@ NS ns2.example.com.
|
|
@ A 192.0.2.2
|
|
@ AAAA 2001:DB8::2
|
|
'';
|
|
|
|
knotZonesEnv = pkgs.buildEnv {
|
|
name = "knot-zones";
|
|
paths = [ exampleZone delegatedZone ];
|
|
};
|
|
in {
|
|
name = "knot";
|
|
|
|
nodes = {
|
|
master = { lib, ... }: {
|
|
imports = [ common ];
|
|
networking.interfaces.eth1 = {
|
|
ipv4.addresses = lib.mkForce [
|
|
{ address = "192.168.0.1"; prefixLength = 24; }
|
|
];
|
|
ipv6.addresses = lib.mkForce [
|
|
{ address = "fd00::1"; prefixLength = 64; }
|
|
];
|
|
};
|
|
services.knot.enable = true;
|
|
services.knot.extraArgs = [ "-v" ];
|
|
services.knot.extraConfig = ''
|
|
server:
|
|
listen: 0.0.0.0@53
|
|
listen: ::@53
|
|
|
|
acl:
|
|
- id: slave_acl
|
|
address: 192.168.0.2
|
|
action: transfer
|
|
|
|
remote:
|
|
- id: slave
|
|
address: 192.168.0.2@53
|
|
|
|
template:
|
|
- id: default
|
|
storage: ${knotZonesEnv}
|
|
notify: [slave]
|
|
acl: [slave_acl]
|
|
dnssec-signing: on
|
|
# 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
|
|
# move databases below the state directory, because they need to be writable
|
|
journal-db: /var/lib/knot/journal
|
|
kasp-db: /var/lib/knot/kasp
|
|
timer-db: /var/lib/knot/timer
|
|
|
|
zone:
|
|
- domain: example.com
|
|
file: example.com.zone
|
|
|
|
- domain: sub.example.com
|
|
file: sub.example.com.zone
|
|
|
|
log:
|
|
- target: syslog
|
|
any: info
|
|
'';
|
|
};
|
|
|
|
slave = { lib, ... }: {
|
|
imports = [ common ];
|
|
networking.interfaces.eth1 = {
|
|
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.extraArgs = [ "-v" ];
|
|
services.knot.extraConfig = ''
|
|
server:
|
|
listen: 0.0.0.0@53
|
|
listen: ::@53
|
|
|
|
acl:
|
|
- id: notify_from_master
|
|
address: 192.168.0.1
|
|
action: notify
|
|
|
|
remote:
|
|
- id: master
|
|
address: 192.168.0.1@53
|
|
|
|
template:
|
|
- id: default
|
|
master: master
|
|
acl: [notify_from_master]
|
|
# zonefileless setup
|
|
# https://www.knot-dns.cz/docs/2.8/html/operation.html#example-2
|
|
zonefile-sync: -1
|
|
zonefile-load: none
|
|
journal-content: all
|
|
# move databases below the state directory, because they need to be writable
|
|
journal-db: /var/lib/knot/journal
|
|
kasp-db: /var/lib/knot/kasp
|
|
timer-db: /var/lib/knot/timer
|
|
|
|
zone:
|
|
- domain: example.com
|
|
file: example.com.zone
|
|
|
|
- domain: sub.example.com
|
|
file: sub.example.com.zone
|
|
|
|
log:
|
|
- target: syslog
|
|
any: info
|
|
'';
|
|
};
|
|
client = { lib, nodes, ... }: {
|
|
imports = [ common ];
|
|
networking.interfaces.eth1 = {
|
|
ipv4.addresses = [
|
|
{ address = "192.168.0.3"; prefixLength = 24; }
|
|
];
|
|
ipv6.addresses = [
|
|
{ address = "fd00::3"; prefixLength = 64; }
|
|
];
|
|
};
|
|
environment.systemPackages = [ pkgs.knot-dns ];
|
|
};
|
|
};
|
|
|
|
testScript = { nodes, ... }: let
|
|
master4 = (lib.head nodes.master.config.networking.interfaces.eth1.ipv4.addresses).address;
|
|
master6 = (lib.head nodes.master.config.networking.interfaces.eth1.ipv6.addresses).address;
|
|
|
|
slave4 = (lib.head nodes.slave.config.networking.interfaces.eth1.ipv4.addresses).address;
|
|
slave6 = (lib.head nodes.slave.config.networking.interfaces.eth1.ipv6.addresses).address;
|
|
in ''
|
|
startAll;
|
|
|
|
$client->waitForUnit("network.target");
|
|
$master->waitForUnit("knot.service");
|
|
$slave->waitForUnit("knot.service");
|
|
|
|
sub assertResponse {
|
|
my ($knot, $query_type, $query, $expected) = @_;
|
|
my $out = $client->succeed("khost -t $query_type $query $knot");
|
|
$client->log("$knot replies with: $out");
|
|
chomp $out;
|
|
die "DNS query for $query ($query_type) against $knot gave '$out' instead of '$expected'"
|
|
if ($out !~ $expected);
|
|
}
|
|
|
|
foreach ("${master4}", "${master6}", "${slave4}", "${slave6}") {
|
|
subtest $_, sub {
|
|
assertResponse($_, "SOA", "example.com", qr/start of authority.*?noc\.example\.com/);
|
|
assertResponse($_, "A", "example.com", qr/has no [^ ]+ record/);
|
|
assertResponse($_, "AAAA", "example.com", qr/has no [^ ]+ record/);
|
|
|
|
assertResponse($_, "A", "www.example.com", qr/address 192.0.2.1$/);
|
|
assertResponse($_, "AAAA", "www.example.com", qr/address 2001:db8::1$/);
|
|
|
|
assertResponse($_, "NS", "sub.example.com", qr/nameserver is ns\d\.example\.com.$/);
|
|
assertResponse($_, "A", "sub.example.com", qr/address 192.0.2.2$/);
|
|
assertResponse($_, "AAAA", "sub.example.com", qr/address 2001:db8::2$/);
|
|
|
|
assertResponse($_, "RRSIG", "www.example.com", qr/RR set signature is/);
|
|
assertResponse($_, "DNSKEY", "example.com", qr/DNSSEC key is/);
|
|
};
|
|
}
|
|
'';
|
|
})
|