summary refs log blame commit diff
path: root/nixos/tests/knot.nix
blob: e46159836ccc0d354fcf9393518edfb49a62102d (plain) (tree)




































































































































































































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