{ config, lib, pkgs, ... }: let # Import nodes nodes = import ./../../nodes.nix; myNode = nodes."${config.hostName}"; # And mapping mapping = import ./../../mapping.nix; dkim = import ./dkim.nix; supportsIPv4 = nd: lib.hasAttr "ip4" nd; supportsIPv6 = nd: lib.hasAttr "ip6" nd; timestampDerivation = pkgs.runCommand "timestamp" {} '' echo -n $(date +%s) > $out ''; timestamp = builtins.readFile timestampDerivation; # Domain key domainkey = '' v=DKIM1; k=rsa; p=${dkim.dkim_pub}''; segments = ((lib.stringLength domainkey) / 255); domainkeySplitted = map (x: lib.substring (x*255) 255 domainkey) (lib.range 0 segments); ##### ## Knot specific ##### # Define remote based on current role remotes = if myNode == mapping.dns.master then map (hostname: { id = hostname; address = "172.19.${toString nodes.${hostname}.zone}.${toString nodes.${hostname}.id}@53"; }) mapping.dns.secondary else [{ id = mapping.dns.master; address = "172.19.${toString nodes.${mapping.dns.master}.zone}.${toString nodes.${mapping.dns.master}.id}@53"; }]; remotesNames = map (remote: remote.id) remotes; # Remotes ACL remotesACL = if myNode == mapping.dns.master then map (hostname: { id = "acl_${hostname}"; address = "172.19.${toString nodes.${hostname}.zone}.${toString nodes.${hostname}.id}@53"; action = "transfer"; }) mapping.dns.secondary else [{ id = "acl_${mapping.dns.master}"; address = "172.19.${toString nodes.${mapping.dns.master}.zone}.${toString nodes.${mapping.dns.master}.id}@53"; action = "notify"; }]; remotesACLNames = map (remote: remote.id) remotesACL; # Other ACL letsencryptACL = if myNode == mapping.dns.master then [{ id = "acl_le_challenge"; address = [ "172.19.0.0/16" "fc00::/96" ]; action = "update"; update-type = "TXT"; key = "letsencrypt"; }] else []; ##### ## Zone specific ##### # host to dn hostToDomain = hostname: builtins.replaceStrings ["-"] ["."] hostname; # Gen NS toNSRecord = host: "IN NS ${hostToDomain host}.net."; nsRecords = map toNSRecord mapping.dns.secondary; dnsSecondaryConfigs = lib.filterAttrs (peerName: _peerConfig: lib.elem peerName mapping.dns.secondary) nodes; # Gen A NS nsARecords = lib.flatten (lib.mapAttrsToList (hostname: node: lib.optional (supportsIPv4 node) "${hostToDomain hostname}.net. IN A ${node.ip4}" ) dnsSecondaryConfigs); # Gen AAAA NS nsAAAARecords = lib.flatten (lib.mapAttrsToList (hostname: node: lib.optional (supportsIPv6 node) "${hostToDomain hostname}.net. IN AAAA ${node.ip6}" ) dnsSecondaryConfigs); # Zone conf zoneFilePath = "/var/lib/knot/zones/zone-lasuite-federez-net"; zone-lasuite-federez-net = pkgs.writeText "zone-lasuite-federez-net" '' $ORIGIN lasuite.federez.net. $TTL 60 @ IN SOA ${builtins.head nsRecords}. monitoring.lasuite.federez.net. ( ${timestamp} ; serial 60 ; refresh 60 ; retry 60 ; expire 60 ) ; minimum TTL IN TXT "v=spf1 a:lasuite.federez.net ~all" ${builtins.concatStringsSep "\n" nsRecords} ${builtins.concatStringsSep "\n" nsARecords} ${builtins.concatStringsSep "\n" nsAAAARecords} _dmarc IN TXT "v=DMARC1; p=quarantine; ruf=mailto:postmaster@lasuite.federez.net" _mta-sts IN TXT "v=STSv1; id=1" _smtp._tls IN TXT "v=TLSRPTv1;rua=mailto:postmaster@lasuite.federez.net" default._domainkey IN TXT "${lib.concatStringsSep "\" \"" domainkeySplitted}" cause-toujours IN TXT "v=spf1 a:lasuite.federez.net ~all" ''; in { age.secrets = { "tsig" = { file = ./../../secrets/dns/tsig.age; owner = "knot"; group = "knot"; }; }; # Ensure the directory exists and is writable systemd.tmpfiles.rules = [ "d /var/lib/knot/zones 0755 knot knot -" "f /var/lib/knot/zones/zone-lasuite-federez-net 0644 knot knot -" ]; # Knot messes with resolvd services.resolved.enable = false; networking.resolvconf.extraConfig = '' name_servers="1.1.1.1 1.0.0.1 2606:4700:4700::1111" ''; # Attach knot to writeZoneFile to force knot to restart after rebuild (otherwise no changes will be detected) systemd.services.knot = { partOf = [ "writeZoneFile.service" ]; restartTriggers = [ zone-lasuite-federez-net ]; reloadIfChanged = lib.mkForce false; }; services.knot = { enable = true; package = pkgs.knot-dns; # To regenerate the secret use: # nix-shell -p knot-dns # keymgr tsig generate -t letsencrypt hmac-sha512 keyFiles = [ config.age.secrets.tsig.path ]; settings = { server = { listen = [ "0.0.0.0@53" "::@53" ]; }; remote = remotes; acl = remotesACL ++ otherACL; zone = if myNode == mapping.dns.master then [ { domain = "lasuite.federez.net"; file = zoneFilePath; notify = remotesNames; acl = remotesACLNames ++ [ "acl_le_challenge" ]; } ] else [ { domain = "lasuite.federez.net"; master = builtins.head remotesNames; acl = remotesACLNames; } ]; log = [ { target = "syslog"; any = "debug"; } ]; }; }; # Write the generated zone file to the writable path systemd.services.writeZoneFile = { before = [ "knot.service" ]; description = "Write initial zone file for lasuite.federez.net"; serviceConfig = { ExecStart = "${pkgs.coreutils}/bin/cp '${zone-lasuite-federez-net}' ${zoneFilePath}"; }; wantedBy = [ "multi-user.target" ]; }; }