diff --git a/lib/fudo/kdc.nix b/lib/fudo/kdc.nix index eb34925..b93bb36 100644 --- a/lib/fudo/kdc.nix +++ b/lib/fudo/kdc.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... } @ toplevel: +{ config, lib, pkgs, ... }@toplevel: with lib; let @@ -9,12 +9,10 @@ let localhost-ips = let addr-only = addrinfo: addrinfo.address; interface = config.networking.interfaces.lo; - in - (map addr-only interface.ipv4.addresses) ++ - (map addr-only interface.ipv6.addresses); + in (map addr-only interface.ipv4.addresses) + ++ (map addr-only interface.ipv6.addresses); - host-ips = - (pkgs.lib.fudo.network.host-ips hostname) ++ localhost-ips; + host-ips = (pkgs.lib.fudo.network.host-ips hostname) ++ localhost-ips; state-directory = toplevel.config.fudo.auth.kdc.state-directory; @@ -24,8 +22,7 @@ let master-server = cfg.master-config != null; slave-server = cfg.slave-config != null; - get-fqdn = hostname: - "${hostname}.${config.fudo.hosts.${hostname}.domain}"; + get-fqdn = hostname: "${hostname}.${config.fudo.hosts.${hostname}.domain}"; kdc-conf = generate-kdc-conf { realm = cfg.realm; @@ -34,102 +31,103 @@ let acl-data = if master-server then cfg.master-config.acl else null; }; - initialize-db = - { realm, user, group, kdc-conf, key-file, db-name, max-lifetime, max-renewal, - primary-keytab, kadmin-keytab, kpasswd-keytab, ipropd-keytab, local-hostname }: let + initialize-db = { realm, user, group, kdc-conf, key-file, db-name + , max-lifetime, max-renewal, primary-keytab, kadmin-keytab, kpasswd-keytab + , ipropd-keytab, local-hostname }: + let - kadmin-cmd = "kadmin -l -c ${kdc-conf} --"; + kadmin-cmd = "kadmin -l -c ${kdc-conf} --"; - get-domain-hosts = domain: let + get-domain-hosts = domain: + let host-in-subdomain = host: hostOpts: (builtins.match "(.+[.])?${domain}$" hostOpts.domain) != null; in attrNames (filterAttrs host-in-subdomain config.fudo.hosts); - get-host-principals = realm: hostname: let - host = config.fudo.hosts.${hostname}; + get-host-principals = realm: hostname: + let host = config.fudo.hosts.${hostname}; in map (service: "${service}/${hostname}.${host.domain}@${realm}") - host.kerberos-services; + host.kerberos-services; - add-principal-str = principal: - "${kadmin-cmd} add --random-key --use-defaults ${principal}"; + add-principal-str = principal: + "${kadmin-cmd} add --random-key --use-defaults ${principal}"; - test-existence = principal: - "[[ $( ${kadmin-cmd} get ${principal} ) ]]"; + test-existence = principal: "[[ $( ${kadmin-cmd} get ${principal} ) ]]"; - exists-or-add = principal: '' - if ${test-existence principal}; then - echo "skipping ${principal}, already exists" - else - ${add-principal-str principal} - fi - ''; + exists-or-add = principal: '' + if ${test-existence principal}; then + echo "skipping ${principal}, already exists" + else + ${add-principal-str principal} + fi + ''; - ensure-host-principals = realm: - concatStringsSep "\n" - (map exists-or-add - (concatMap (get-host-principals realm) - (get-domain-hosts (toLower realm)))); + ensure-host-principals = realm: + concatStringsSep "\n" (map exists-or-add + (concatMap (get-host-principals realm) + (get-domain-hosts (toLower realm)))); - slave-hostnames = map get-fqdn cfg.master-config.slave-hosts; + slave-hostnames = map get-fqdn cfg.master-config.slave-hosts; - ensure-iprop-principals = concatStringsSep "\n" - (map (host: exists-or-add "iprop/${host}@${realm}") - [ local-hostname ] ++ slave-hostnames); + ensure-iprop-principals = concatStringsSep "\n" + (map (host: exists-or-add "iprop/${host}@${realm}") [ local-hostname ] + ++ slave-hostnames); - copy-slave-principals-file = let - slave-principals = map - (host: "iprop/${hostname}@${cfg.realm}") - slave-hostnames; - slave-principals-file = pkgs.writeText "heimdal-slave-principals" - (concatStringsSep "\n" slave-principals); - in optionalString (slave-principals-file != null) '' - cp ${slave-principals-file} ${state-directory}/slaves - # Since it's copied from /nix/store, this is by default read-only, - # which causes updates to fail. - chmod u+w ${state-directory}/slaves - ''; + copy-slave-principals-file = let + slave-principals = + map (host: "iprop/${hostname}@${cfg.realm}") slave-hostnames; + slave-principals-file = pkgs.writeText "heimdal-slave-principals" + (concatStringsSep "\n" slave-principals); + in optionalString (slave-principals-file != null) '' + cp ${slave-principals-file} ${state-directory}/slaves + # Since it's copied from /nix/store, this is by default read-only, + # which causes updates to fail. + chmod u+w ${state-directory}/slaves + ''; - in pkgs.writeShellScript "initialize-kdc-db.sh" '' - TMP=$(mktemp -d -t kdc-XXXXXXXX) - if [ ! -e ${database-file} ]; then - ## CHANGING HOW THIS WORKS - ## Now we expect the key to be provided - # kstash --key-file=${key-file} --random-key - ${kadmin-cmd} init --realm-max-ticket-life="${max-lifetime}" --realm-max-renewable-life="${max-renewal}" ${realm} - fi + in pkgs.writeShellScript "initialize-kdc-db.sh" '' + TMP=$(mktemp -d -t kdc-XXXXXXXX) + if [ ! -e ${database-file} ]; then + ## CHANGING HOW THIS WORKS + ## Now we expect the key to be provided + # kstash --key-file=${key-file} --random-key + ${kadmin-cmd} init --realm-max-ticket-life="${max-lifetime}" --realm-max-renewable-life="${max-renewal}" ${realm} + fi - ${ensure-host-principals realm} + ${ensure-host-principals realm} - ${ensure-iprop-principals} + ${ensure-iprop-principals} - echo "*** BEGIN EXTRACTING KEYTABS" - echo "*** You can probably ignore the 'principal does not exist' errors that follow," - echo "*** they're just testing for principal existence before creating those that" - echo "*** don't already exist" + echo "*** BEGIN EXTRACTING KEYTABS" + echo "*** You can probably ignore the 'principal does not exist' errors that follow," + echo "*** they're just testing for principal existence before creating those that" + echo "*** don't already exist" - ${kadmin-cmd} ext_keytab --keytab=$TMP/primary.keytab */${local-hostname}@${realm} - mv $TMP/primary.keytab ${primary-keytab} - ${kadmin-cmd} ext_keytab --keytab=$TMP/kadmin.keytab kadmin/admin@${realm} - mv $TMP/kadmin.keytab ${kadmin-keytab} - ${kadmin-cmd} ext_keytab --keytab=$TMP/kpasswd.keytab kadmin/changepw@${realm} - mv $TMP/kpasswd.keytab ${kpasswd-keytab} - ${kadmin-cmd} ext_keytab --keytab=$TMP/ipropd.keytab iprop/${local-hostname}@${realm} - mv $TMP/ipropd.keytab ${ipropd-keytab} + ${kadmin-cmd} ext_keytab --keytab=$TMP/primary.keytab */${local-hostname}@${realm} + mv $TMP/primary.keytab ${primary-keytab} + ${kadmin-cmd} ext_keytab --keytab=$TMP/kadmin.keytab kadmin/admin@${realm} + mv $TMP/kadmin.keytab ${kadmin-keytab} + ${kadmin-cmd} ext_keytab --keytab=$TMP/kpasswd.keytab kadmin/changepw@${realm} + mv $TMP/kpasswd.keytab ${kpasswd-keytab} + ${kadmin-cmd} ext_keytab --keytab=$TMP/ipropd.keytab iprop/${local-hostname}@${realm} + mv $TMP/ipropd.keytab ${ipropd-keytab} - echo "*** END EXTRACTING KEYTABS" + echo "*** END EXTRACTING KEYTABS" - ${copy-slave-principals-file} - ''; + ${copy-slave-principals-file} + ''; - generate-kdc-conf = { realm, db-file, key-file, acl-data }: + generate-kdc-conf = { realm, db-file, key-file, acl-data }: pkgs.writeText "kdc.conf" '' [kdc] database = { dbname = sqlite:${db-file} realm = ${realm} mkey_file = ${key-file} - ${optionalString (acl-data != null) - "acl_file = ${generate-acl-file acl-data}"} + ${ + optionalString (acl-data != null) + "acl_file = ${generate-acl-file acl-data}" + } log_file = ${iprop-log} } @@ -171,18 +169,17 @@ let }; }; - generate-acl-file = acl-entries: let - perms-to-permstring = perms: concatStringsSep "," perms; - in - pkgs.writeText "kdc.acl" (concatStringsSep "\n" (mapAttrsToList + generate-acl-file = acl-entries: + let perms-to-permstring = perms: concatStringsSep "," perms; + in pkgs.writeText "kdc.acl" (concatStringsSep "\n" (mapAttrsToList (principal: opts: "${principal} ${perms-to-permstring opts.perms}${ - optionalString (opts.target != null) " ${opts.target}" }") - acl-entries)); + optionalString (opts.target != null) " ${opts.target}" + }") acl-entries)); kadmin-local = kdc-conf: pkgs.writeShellScriptBin "kadmin.local" '' - ${pkgs.heimdalFull}/bin/kadmin -l -c ${kdc-conf} $@ + ${pkgs.heimdal}/bin/kadmin -l -c ${kdc-conf} $@ ''; masterOpts = { ... }: { @@ -329,8 +326,7 @@ in { } { assertion = !(master-server && slave-server); - message = - "Only one of master-config and slave-config may be provided."; + message = "Only one of master-config and slave-config may be provided."; } ]; @@ -363,7 +359,7 @@ in { }; environment = { - systemPackages = [ pkgs.heimdalFull (kadmin-local kdc-conf) ]; + systemPackages = [ pkgs.heimdal (kadmin-local kdc-conf) ]; ## This shouldn't be necessary...every host gets a krb5.keytab # etc = { @@ -376,9 +372,8 @@ in { # }; }; - systemd.tmpfiles.rules = [ - "d ${state-directory} 0740 ${cfg.user} ${cfg.group} - -" - ]; + systemd.tmpfiles.rules = + [ "d ${state-directory} 0740 ${cfg.user} ${cfg.group} - -" ]; fudo.system = { services = if master-server then { @@ -391,7 +386,8 @@ in { after = [ "network.target" ]; description = "Heimdal Kerberos Key Distribution Center (ticket server)."; - execStart = "${pkgs.heimdalFull}/libexec/heimdal/kdc -c ${kdc-conf} --ports=88 ${listen-addrs}"; + execStart = + "${pkgs.heimdal}/libexec/heimdal/kdc -c ${kdc-conf} --ports=88 ${listen-addrs}"; user = cfg.user; group = cfg.group; workingDirectory = state-directory; @@ -426,26 +422,29 @@ in { execStart = "${init-cmd}"; user = cfg.user; group = cfg.group; - path = with pkgs; [ heimdalFull ]; + path = with pkgs; [ heimdal ]; protectSystem = "full"; addressFamilies = [ "AF_INET" "AF_INET6" ]; workingDirectory = state-directory; environment = { KRB5_CONFIG = "/etc/krb5.conf"; }; }; - heimdal-ipropd-master = mkIf (length cfg.master-config.slave-hosts > 0) { - requires = [ "heimdal-kdc.service" ]; - wantedBy = [ "multi-user.target" ]; - description = "Propagate changes to the master KDC DB to all slaves."; - path = with pkgs; [ heimdalFull ]; - execStart = "${pkgs.heimdalFull}/libexec/heimdal/ipropd-master -c ${kdc-conf} -k ${cfg.master.ipropd-keytab}"; - user = cfg.user; - group = cfg.group; - workingDirectory = state-directory; - privateNetwork = false; - addressFamilies = [ "AF_INET" "AF_INET6" ]; - environment = { KRB5_CONFIG = "/etc/krb5.conf"; }; - }; + heimdal-ipropd-master = + mkIf (length cfg.master-config.slave-hosts > 0) { + requires = [ "heimdal-kdc.service" ]; + wantedBy = [ "multi-user.target" ]; + description = + "Propagate changes to the master KDC DB to all slaves."; + path = with pkgs; [ heimdal ]; + execStart = + "${pkgs.heimdal}/libexec/heimdal/ipropd-master -c ${kdc-conf} -k ${cfg.master.ipropd-keytab}"; + user = cfg.user; + group = cfg.group; + workingDirectory = state-directory; + privateNetwork = false; + addressFamilies = [ "AF_INET" "AF_INET6" ]; + environment = { KRB5_CONFIG = "/etc/krb5.conf"; }; + }; } else { @@ -453,7 +452,7 @@ in { listen-addrs = concatStringsSep " " (map (addr: "--addresses=${addr}") cfg.bind-addresses); command = - "${pkgs.heimdalFull}/libexec/heimdal/kdc -c ${kdc-conf} --ports=88 ${listen-addrs}"; + "${pkgs.heimdal}/libexec/heimdal/kdc -c ${kdc-conf} --ports=88 ${listen-addrs}"; in { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; @@ -471,10 +470,11 @@ in { heimdal-ipropd-slave = { wantedBy = [ "multi-user.target" ]; - description = "Receive changes propagated from the KDC master server."; - path = with pkgs; [ heimdalFull ]; + description = + "Receive changes propagated from the KDC master server."; + path = with pkgs; [ heimdal ]; execStart = concatStringsSep " " [ - "${pkgs.heimdalFull}/libexec/heimdal/ipropd-slave" + "${pkgs.heimdal}/libexec/heimdal/ipropd-slave" "--config-file=${kdc-conf}" "--keytab=${cfg.slave-config.ipropd-keytab}" "--realm=${cfg.realm}" @@ -501,7 +501,7 @@ in { { name = "kerberos-adm"; user = cfg.user; - server = "${pkgs.heimdalFull}/libexec/heimdal/kadmind"; + server = "${pkgs.heimdal}/libexec/heimdal/kadmind"; protocol = "tcp"; serverArgs = "--config-file=${kdc-conf} --keytab=${cfg.master-config.kadmin-keytab}"; @@ -509,7 +509,7 @@ in { { name = "kpasswd"; user = cfg.user; - server = "${pkgs.heimdalFull}/libexec/heimdal/kpasswdd"; + server = "${pkgs.heimdal}/libexec/heimdal/kpasswdd"; protocol = "udp"; serverArgs = "--config-file=${kdc-conf} --keytab=${cfg.master-config.kpasswdd-keytab}"; @@ -519,12 +519,10 @@ in { networking = { firewall = { - allowedTCPPorts = [ 88 ] ++ - (optionals master-server [ 749 ]) ++ - (optionals slave-server [ 2121 ]); - allowedUDPPorts = [ 88 ] ++ - (optionals master-server [ 464 ]) ++ - (optionals slave-server [ 2121 ]); + allowedTCPPorts = [ 88 ] ++ (optionals master-server [ 749 ]) + ++ (optionals slave-server [ 2121 ]); + allowedUDPPorts = [ 88 ] ++ (optionals master-server [ 464 ]) + ++ (optionals slave-server [ 2121 ]); }; }; }; diff --git a/lib/fudo/minecraft-server.nix b/lib/fudo/minecraft-server.nix index 9ee5474..ed7a56e 100644 --- a/lib/fudo/minecraft-server.nix +++ b/lib/fudo/minecraft-server.nix @@ -1,8 +1,7 @@ { config, lib, pkgs, ... }: with lib; -let - cfg = config.fudo.minecraft-server; +let cfg = config.fudo.minecraft-server; in { options.fudo.minecraft-server = { @@ -11,7 +10,7 @@ in { package = mkOption { type = types.package; description = "Minecraft package to use."; - default = pkgs.minecraft-server_1_15_1; + default = pkgs.minecraft-current; }; data-dir = mkOption { @@ -27,10 +26,11 @@ in { motd = mkOption { type = types.str; description = "Welcome message for newcomers."; + default = "Welcome to Minecraft! Have fun building..."; }; game-mode = mkOption { - type = types.enum ["survival" "creative" "adventure" "spectator"]; + type = types.enum [ "survival" "creative" "adventure" "spectator" ]; description = "Game mode of the server."; default = "survival"; }; @@ -40,12 +40,15 @@ in { description = "Difficulty level, where 0 is peaceful and 3 is hard."; default = 2; }; + + allow-cheats = mkOption { + type = types.bool; + default = false; + }; }; config = mkIf cfg.enable { - environment.systemPackages = [ - cfg.package - ]; + environment.systemPackages = [ cfg.package ]; services.minecraft-server = { enable = true; @@ -58,6 +61,7 @@ in { motd = cfg.motd; difficulty = cfg.difficulty; gamemode = cfg.game-mode; + allow-cheats = true; }; }; }; diff --git a/lib/fudo/nsd.nix b/lib/fudo/nsd.nix index bb200e4..cba7af0 100644 --- a/lib/fudo/nsd.nix +++ b/lib/fudo/nsd.nix @@ -1,4 +1,4 @@ -### NOTE: +# ## NOTE: ## This is a copy of the upstream version, which allows for overriding the state directory { config, pkgs, lib, ... }: @@ -18,25 +18,26 @@ let ipv6 = cfg.ipv6; ratelimit = cfg.ratelimit.enable; rootServer = cfg.rootServer; - zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0; + zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) + > 0; }; mkZoneFileName = name: if name == "." then "root" else name; # replaces include: directives for keys with fake keys for nsd-checkconf - injectFakeKeys = keys: concatStrings - (mapAttrsToList - (keyName: keyOptions: '' - fakeKey="$(${pkgs.bind}/bin/tsig-keygen -a ${escapeShellArgs [ keyOptions.algorithm keyName ]} | grep -oP "\s*secret \"\K.*(?=\";)")" - sed "s@^\s*include:\s*\"${stateDir}/private/${keyName}\"\$@secret: $fakeKey@" -i $out/nsd.conf - '') - keys); + injectFakeKeys = keys: + concatStrings (mapAttrsToList (keyName: keyOptions: '' + fakeKey="$(${pkgs.bind}/bin/tsig-keygen -a ${ + escapeShellArgs [ keyOptions.algorithm keyName ] + } | grep -oP "\s*secret \"\K.*(?=\";)")" + sed "s@^\s*include:\s*\"${stateDir}/private/${keyName}\"\$@secret: $fakeKey@" -i $out/nsd.conf + '') keys); nsdEnv = pkgs.buildEnv { name = "nsd-env"; paths = [ configFile ] - ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs; + ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs; postBuild = '' echo "checking zone files" @@ -64,12 +65,12 @@ let ''; }; - writeZoneData = name: text: pkgs.writeTextFile { - name = "nsd-zone-${mkZoneFileName name}"; - inherit text; - destination = "/zones/${mkZoneFileName name}"; - }; - + writeZoneData = name: text: + pkgs.writeTextFile { + name = "nsd-zone-${mkZoneFileName name}"; + inherit text; + destination = "/zones/${mkZoneFileName name}"; + }; # options are ordered alphanumerically by the nixos option name configFile = pkgs.writeTextDir "nsd.conf" '' @@ -86,19 +87,19 @@ let zonelistfile: "${stateDir}/var/zone.list" # interfaces ${forEach " ip-address: " cfg.interfaces} - ip-freebind: ${yesOrNo cfg.ipFreebind} - hide-version: ${yesOrNo cfg.hideVersion} + ip-freebind: ${yesOrNo cfg.ipFreebind} + hide-version: ${yesOrNo cfg.hideVersion} identity: "${cfg.identity}" - ip-transparent: ${yesOrNo cfg.ipTransparent} - do-ip4: ${yesOrNo cfg.ipv4} + ip-transparent: ${yesOrNo cfg.ipTransparent} + do-ip4: ${yesOrNo cfg.ipv4} ipv4-edns-size: ${toString cfg.ipv4EDNSSize} - do-ip6: ${yesOrNo cfg.ipv6} + do-ip6: ${yesOrNo cfg.ipv6} ipv6-edns-size: ${toString cfg.ipv6EDNSSize} - log-time-ascii: ${yesOrNo cfg.logTimeAscii} + log-time-ascii: ${yesOrNo cfg.logTimeAscii} ${maybeString "nsid: " cfg.nsid} port: ${toString cfg.port} - reuseport: ${yesOrNo cfg.reuseport} - round-robin: ${yesOrNo cfg.roundRobin} + reuseport: ${yesOrNo cfg.reuseport} + round-robin: ${yesOrNo cfg.roundRobin} server-count: ${toString cfg.serverCount} ${maybeToString "statistics: " cfg.statistics} tcp-count: ${toString cfg.tcpCount} @@ -107,16 +108,16 @@ let verbosity: ${toString cfg.verbosity} ${maybeString "version: " cfg.version} xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout} - zonefiles-check: ${yesOrNo cfg.zonefilesCheck} + zonefiles-check: ${yesOrNo cfg.zonefilesCheck} ${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength} ${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength} rrl-ratelimit: ${toString cfg.ratelimit.ratelimit} - ${maybeString "rrl-slip: " cfg.ratelimit.slip} + ${maybeString "rrl-slip: " cfg.ratelimit.slip} rrl-size: ${toString cfg.ratelimit.size} rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit} ${keyConfigFile} remote-control: - control-enable: ${yesOrNo cfg.remoteControl.enable} + control-enable: ${yesOrNo cfg.remoteControl.enable} control-key-file: "${cfg.remoteControl.controlKeyFile}" control-cert-file: "${cfg.remoteControl.controlCertFile}" ${forEach " control-interface: " cfg.remoteControl.interfaces} @@ -129,10 +130,10 @@ let yesOrNo = b: if b then "yes" else "no"; maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"''; - maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}''; + maybeToString = prefix: x: + if x == null then "" else "${prefix} ${toString x}"; forEach = pre: l: concatMapStrings (x: pre + x + "\n") l; - keyConfigFile = concatStrings (mapAttrsToList (keyName: keyOptions: '' key: name: "${keyName}" @@ -148,53 +149,44 @@ let chmod 0400 "$dest" '') cfg.keys); - # options are ordered alphanumerically by the nixos option name zoneConfigFile = name: zone: '' zone: name: "${name}" zonefile: "${stateDir}/zones/${mkZoneFileName name}" ${maybeString "outgoing-interface: " zone.outgoingInterface} - ${forEach " rrl-whitelist: " zone.rrlWhitelist} - ${maybeString "zonestats: " zone.zoneStats} + ${forEach " rrl-whitelist: " zone.rrlWhitelist} + ${maybeString "zonestats: " zone.zoneStats} ${maybeToString "max-refresh-time: " zone.maxRefreshSecs} ${maybeToString "min-refresh-time: " zone.minRefreshSecs} ${maybeToString "max-retry-time: " zone.maxRetrySecs} ${maybeToString "min-retry-time: " zone.minRetrySecs} - allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} - ${forEach " allow-notify: " zone.allowNotify} - ${forEach " request-xfr: " zone.requestXFR} - ${forEach " notify: " zone.notify} + allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} + ${forEach " allow-notify: " zone.allowNotify} + ${forEach " request-xfr: " zone.requestXFR} + ${forEach " notify: " zone.notify} notify-retry: ${toString zone.notifyRetry} - ${forEach " provide-xfr: " zone.provideXFR} + ${forEach " provide-xfr: " zone.provideXFR} ''; - zoneConfigs = zoneConfigs' {} "" { children = cfg.zones; }; + zoneConfigs = zoneConfigs' { } "" { children = cfg.zones; }; zoneConfigs' = parent: name: zone: if !(zone ? children) || zone.children == null || zone.children == { } - # leaf -> actual zone - then listToAttrs [ (nameValuePair name (parent // zone)) ] + # leaf -> actual zone + then + listToAttrs [ + (nameValuePair name (parent // zone)) + ] # fork -> pattern - else zipAttrsWith (name: head) ( - mapAttrsToList (name: child: zoneConfigs' (parent // zone // { children = {}; }) name child) - zone.children - ); - - # fighting infinite recursion - zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true; - zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false; - zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false; - zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false; - zoneOptions4 = zoneOptionsRaw // childConfig zoneOptions5 false; - zoneOptions5 = zoneOptionsRaw // childConfig zoneOptions6 false; - zoneOptions6 = zoneOptionsRaw // childConfig null false; - - childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; }; + else + zipAttrsWith (name: head) (mapAttrsToList (name: child: + zoneConfigs' (parent // zone // { children = { }; }) name child) + zone.children); # options are ordered alphanumerically - zoneOptionsRaw = types.submodule { + zoneOptions = types.submodule { options = { allowAXFRFallback = mkOption { @@ -209,9 +201,11 @@ let allowNotify = mkOption { type = types.listOf types.str; default = [ ]; - example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name" - "10.0.3.4&255.255.0.0 BLOCKED" - ]; + example = [ + "192.0.2.0/24 NOKEY" + "10.0.0.1-10.0.0.5 my_tsig_key_name" + "10.0.3.4&255.255.0.0 BLOCKED" + ]; description = '' Listed primary servers are allowed to notify this secondary server. [AXFR|UDP] <ip-address> <key-name | NOKEY> ''; }; rrlWhitelist = mkOption { - type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]); - default = []; + type = with types; + listOf (enum [ + "nxdomain" + "error" + "referral" + "any" + "rrsig" + "wildcard" + "nodata" + "dnskey" + "positive" + "all" + ]); + default = [ ]; description = '' Whitelists the given rrl-types. ''; @@ -430,9 +442,10 @@ let }; }; - dnssecZones = (filterAttrs (n: v: if v ? dnssec then v.dnssec else false) zoneConfigs); + dnssecZones = + (filterAttrs (n: v: if v ? dnssec then v.dnssec else false) zoneConfigs); - dnssec = dnssecZones != {}; + dnssec = dnssecZones != { }; dnssecTools = pkgs.bind.override { enablePython = true; }; @@ -443,32 +456,40 @@ let ${concatStrings (mapAttrsToList signZone dnssecZones)} ''; signZone = name: zone: '' - ${dnssecTools}/bin/dnssec-keymgr -g ${dnssecTools}/bin/dnssec-keygen -s ${dnssecTools}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${policyFile name zone.dnssecPolicy} ${name} + ${dnssecTools}/bin/dnssec-keymgr -g ${dnssecTools}/bin/dnssec-keygen -s ${dnssecTools}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${ + policyFile name zone.dnssecPolicy + } ${name} ${dnssecTools}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name} ${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name} ''; - policyFile = name: policy: pkgs.writeText "${name}.policy" '' - zone ${name} { - algorithm ${policy.algorithm}; - key-size zsk ${toString policy.zsk.keySize}; - key-size ksk ${toString policy.ksk.keySize}; - keyttl ${policy.keyttl}; - pre-publish zsk ${policy.zsk.prePublish}; - pre-publish ksk ${policy.ksk.prePublish}; - post-publish zsk ${policy.zsk.postPublish}; - post-publish ksk ${policy.ksk.postPublish}; - roll-period zsk ${policy.zsk.rollPeriod}; - roll-period ksk ${policy.ksk.rollPeriod}; - coverage ${policy.coverage}; - }; - ''; -in -{ + policyFile = name: policy: + pkgs.writeText "${name}.policy" '' + zone ${name} { + algorithm ${policy.algorithm}; + key-size zsk ${toString policy.zsk.keySize}; + key-size ksk ${toString policy.ksk.keySize}; + keyttl ${policy.keyttl}; + pre-publish zsk ${policy.zsk.prePublish}; + pre-publish ksk ${policy.ksk.prePublish}; + post-publish zsk ${policy.zsk.postPublish}; + post-publish ksk ${policy.ksk.postPublish}; + roll-period zsk ${policy.zsk.rollPeriod}; + roll-period ksk ${policy.ksk.rollPeriod}; + coverage ${policy.coverage}; + }; + ''; +in { # options are ordered alphanumerically options.fudo.nsd = { enable = mkEnableOption "NSD authoritative DNS server"; + stateDir = mkOption { + type = types.str; + description = "Directory at which to store NSD state data."; + default = "/var/lib/nsd"; + }; + bind8Stats = mkEnableOption "BIND8 like statistics"; dnssecInterval = mkOption { @@ -587,6 +608,7 @@ in reuseport = mkOption { type = types.bool; default = pkgs.stdenv.isLinux; + defaultText = literalExpression "pkgs.stdenv.isLinux"; description = '' Whether to enable SO_REUSEPORT on all used sockets. This lets multiple processes bind to the same port. This speeds up operation especially @@ -614,13 +636,6 @@ in ''; }; - - stateDir = mkOption { - type = types.str; - description = "Directory at which to store NSD state data."; - default = "/var/lib/nsd"; - }; - statistics = mkOption { type = types.nullOr types.int; default = null; @@ -689,7 +704,6 @@ in ''; }; - keys = mkOption { type = types.attrsOf (types.submodule { options = { @@ -714,8 +728,8 @@ in }; }); - default = {}; - example = literalExample '' + default = { }; + example = literalExpression '' { "tsig.example.org" = { algorithm = "hmac-md5"; keyFile = "/path/to/my/key"; @@ -727,7 +741,6 @@ in ''; }; - ratelimit = { enable = mkEnableOption "ratelimit capabilities"; @@ -788,7 +801,6 @@ in }; - remoteControl = { enable = mkEnableOption "remote control via nsd-control"; @@ -849,8 +861,8 @@ in zones = mkOption { type = types.attrsOf zoneOptions; - default = {}; - example = literalExample '' + default = { }; + example = literalExpression '' { "serverGroup1" = { provideXFR = [ "10.1.2.3 NOKEY" ]; children = { @@ -861,7 +873,7 @@ in @ IN SOA a.ns.example.com. admin.example.com. ( ... '''; - }; + }; "example.org." = { data = ''' $ORIGIN example.org. @@ -869,16 +881,16 @@ in @ IN SOA a.ns.example.com. admin.example.com. ( ... '''; - }; - }; }; + }; + }; "example.net." = { provideXFR = [ "10.3.2.1 NOKEY" ]; data = ''' ... '''; }; - } + } ''; description = '' Define your zones here. Zones can cascade other zones and therefore @@ -896,7 +908,7 @@ in assertions = singleton { assertion = zoneConfigs ? "." -> cfg.rootServer; message = "You have a root zone configured. If this is really what you " - + "want, please enable 'services.nsd.rootServer'."; + + "want, please enable 'services.nsd.rootServer'."; }; environment = { @@ -909,7 +921,7 @@ in users.users.${username} = { description = "NSD service user"; home = stateDir; - createHome = true; + createHome = true; uid = config.ids.uids.nsd; group = username; }; @@ -921,7 +933,7 @@ in wantedBy = [ "multi-user.target" ]; startLimitBurst = 4; - startLimitIntervalSec = 5 * 60; # 5 mins + startLimitIntervalSec = 5 * 60; # 5 mins serviceConfig = { ExecStart = "${nsdPkg}/sbin/nsd -d -c ${nsdEnv}/nsd.conf"; StandardError = "null"; @@ -975,4 +987,6 @@ in }; }; + + meta.maintainers = with lib.maintainers; [ hrdinka ]; } diff --git a/lib/fudo/powerdns.nix b/lib/fudo/powerdns.nix index 8f5d0e3..2898e66 100644 --- a/lib/fudo/powerdns.nix +++ b/lib/fudo/powerdns.nix @@ -32,7 +32,6 @@ let pdns-config-dir = pkgs.writeTextDir "pdns.conf" '' local-address=${concatStringsSep ", " cfg.listen-v4-addresses} - local-ipv6=${concatStringsSep ", " cfg.listen-v6-addresses} local-port=${toString cfg.port} launch= include-dir=${runtime-dir} @@ -305,13 +304,14 @@ in { psql -f ${pkgs.powerdns}/share/doc/pdns/schema.pgsql.sql fi ''; - # Wait until posgresql is available before starting - ExecStartPre = - pkgs.writeShellScript "ensure-postgresql-running.sh" '' - while [ ! "$( psql -tAc "SELECT 1" )" ]; do - ${pkgs.coreutils}/bin/sleep 3 - done - ''; + ## Doesn't seem to use env vars, and so fails if remote + ## Wait until posgresql is available before starting + # ExecStartPre = + # pkgs.writeShellScript "ensure-postgresql-running.sh" '' + # while [ ! "$( psql -tAc "SELECT 1" )" ]; do + # ${pkgs.coreutils}/bin/sleep 3 + # done + # ''; }; };