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/);
 | |
|       };
 | |
|     }
 | |
|   '';
 | |
| })
 | 
