{ machines, nixosConfigurations, machineConfig, config, lib, pkgs, ... }: let inherit (lib) types; cfg = config.zpha.services.knot; dnsServerAddresses = isPrimary: (lib.pipe nixosConfigurations [ (lib.filterAttrs ( _hostName: machineConfig: let cfgModule = machineConfig.config.zpha.services.knot; in cfgModule.enable && cfgModule.primary == isPrimary )) (lib.mapAttrsToList ( hostName: _machineConfig: [ (lib.mkIf (machines."${hostName}".networking.ip6Address != "") machines."${hostName}".networking.ip6Address ) (lib.mkIf (machines."${hostName}".networking.ip4Address != "") machines."${hostName}".networking.ip4Address ) ] )) lib.flatten ]); dnsServerSecondaries = lib.pipe nixosConfigurations [ (lib.filterAttrs ( _hostName: machineConfig: let cfgModule = machineConfig.config.zpha.services.knot; in cfgModule.enable && !cfgModule.primary )) (lib.mapAttrs ( hostName: _machineConfig: { address = [ (lib.mkIf (machines."${hostName}".networking.ip6Address != "") machines."${hostName}".networking.ip6Address ) (lib.mkIf (machines."${hostName}".networking.ip4Address != "") machines."${hostName}".networking.ip4Address ) ]; } )) ]; in { options.zpha.services.knot = { enable = lib.mkEnableOption "knot dns server"; primary = lib.mkOption { type = types.bool; default = false; }; dataDir = lib.mkOption { type = types.str; default = "/var/lib/knot"; }; keyFiles = lib.mkOption { type = types.listOf types.path; default = [ ]; }; zones = lib.mkOption { inherit ((pkgs.formats.yaml { })) type; default = { }; }; extraACL = lib.mkOption { inherit ((pkgs.formats.yaml { })) type; default = { }; }; }; config = lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ 53 ]; networking.firewall.allowedUDPPorts = [ 53 ]; users = { users.knot.uid = 553; groups.knot.gid = 553; }; common.configure.persist.system.dirs = [ { directory = cfg.dataDir; mode = "770"; user = "knot"; group = "knot"; } ]; systemd.tmpfiles.settings = { knotDataDir."${cfg.dataDir}".d = { group = "knot"; user = "knot"; mode = "770"; age = "-"; }; }; services.knot = let primaryAddresses = dnsServerAddresses true; secondaryAddresses = dnsServerAddresses false; secondaries = dnsServerSecondaries; in { enable = true; keyFiles = lib.mkIf (cfg.keyFiles != [ ]) cfg.keyFiles; settings = { log.syslog.any = "debug"; server.listen = [ (lib.mkIf (machineConfig.networking.ip6Address != "") "${machineConfig.networking.ip6Address}@53") (lib.mkIf (machineConfig.networking.ip4Address != "") "${machineConfig.networking.ip4Address}@53") "::1@53" "127.0.0.2@53" ]; mod-rrl.default.rate-limit = 200; mod-rrl.default.slip = 2; remote = { primary.address = primaryAddresses; } // secondaries; acl = { allowTransfer = lib.mkIf (secondaryAddresses != [ ]) { address = secondaryAddresses; action = "transfer"; }; allowNotify.address = primaryAddresses; allowNotify.action = "notify"; } // cfg.extraACL; template = let notify = { acl = lib.mkIf (config.services.knot.settings.acl ? allowTransfer) "allowTransfer"; notify = lib.mkIf (config.services.knot.settings.acl ? allowTransfer) ( builtins.attrNames secondaries ); }; catalog = { catalog-role = "member"; catalog-zone = "catalog."; }; in { default = { semantic-checks = true; global-module = "mod-rrl/default"; }; notifyZone = notify; nixZone = notify // catalog; secondaryZone = { master = "primary"; acl = "allowNotify"; journal-content = "all"; zonefile-sync = -1; zonefile-load = "none"; }; }; zone = ( if !cfg.primary then { "catalog.".catalog-role = "interpret"; "catalog.".catalog-template = "secondaryZone"; "catalog.".template = "secondaryZone"; } else { "catalog.".catalog-role = "generate"; "catalog.".template = "notifyZone"; } ) // (lib.mapAttrs ( _name: zone: zone // { template = "nixZone"; acl = lib.mkIf (zone ? acl) ( if (config.services.knot.settings.acl ? allowTransfer) then lib.flatten [ [ "allowTransfer" ] zone.acl ] else zone.acl ); } ) cfg.zones); }; }; }; }