From 84f1f97605731939410a315593deb2c11c11c9e1 Mon Sep 17 00:00:00 2001 From: Luflosi Date: Wed, 5 Jun 2024 12:49:28 +0200 Subject: [PATCH] Add NixOS option useNsupdateProgram to simplify setup with nsupdate --- nix/e2e-test.nix | 20 +---------- nix/module.nix | 89 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/nix/e2e-test.nix b/nix/e2e-test.nix index 3492739..93557a9 100644 --- a/nix/e2e-test.nix +++ b/nix/e2e-test.nix @@ -10,8 +10,6 @@ self: self.outputs.nixosModules.dyndnsd ]; - users.groups.ddns = {}; - systemd.services.dyndnsd.serviceConfig.SupplementaryGroups = [ "ddns" ]; systemd.services.bind.preStart = let zoneFile = pkgs.writeText "root.zone" '' $ORIGIN example.org. @@ -32,12 +30,6 @@ self: test IN AAAA 8:7:6:5:4:3:2:1 ''; in '' - mkdir -m 0755 -p /run/named - if ! [ -f "/run/named/ddns.key" ]; then - ${config.services.bind.package.out}/sbin/rndc-confgen -c /run/named/ddns.key -u named -a -k ddns 2>/dev/null - chgrp ddns /run/named/ddns.key - chmod 440 /run/named/ddns.key - fi mkdir -p '/var/lib/bind/zones/example.org/' chown -R named '/var/lib/bind/zones/example.org/' cp '${zoneFile}' '/var/lib/bind/zones/example.org/example.org.zone' @@ -48,9 +40,6 @@ self: enable = true; forward = "only"; forwarders = []; - extraConfig = '' - include "/run/named/ddns.key"; - ''; extraOptions = '' empty-zones-enable no; ''; @@ -77,15 +66,8 @@ self: }; services.dyndnsd = { enable = true; + useNsupdateProgram = true; settings = { - update_program = { - bin = "${pkgs.dig.dnsutils}/bin/nsupdate"; - args = [ "-k" "/run/named/ddns.key" ]; - stdin_per_zone_update = "send\n"; - final_stdin = "quit\n"; - ipv4.stdin = "update delete {domain}. IN A\nupdate add {domain}. {ttl} IN A {ipv4}\n"; - ipv6.stdin = "update delete {domain}. IN AAAA\nupdate add {domain}. {ttl} IN AAAA {ipv6}\n"; - }; users = { alice = { hash = "$HASH"; diff --git a/nix/module.nix b/nix/module.nix index 1a9e8f7..a5eb248 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -82,6 +82,16 @@ in options = { services.dyndnsd = { enable = lib.mkEnableOption "the DynDNS server"; + + useNsupdateProgram = lib.mkEnableOption '' + the recommended default configuration for using nsupdate with BIND. + This sets all of the `services.dyndnsd.settings.update_program` options. + It also creates a key when the system starts and tells BIND where to find it. + The key is readable by the `ddns` group, which is also created. `dyndnsd` is allowed access to the key. + You still need to manually set an `update-policy` with which BIND allows updates to specific domain names. + Look at `nix/e2e-test.nix` for an example of how to do that. + ''; + localhost = lib.mkOption { type = lib.types.bool; default = true; @@ -214,30 +224,59 @@ in }; - config = lib.mkIf cfg.enable { - systemd.packages = [ pkgs.dyndnsd ]; - - systemd.services.dyndnsd = { - description = "Service that updates a dynamic DNS record"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - startLimitBurst = 1; - - serviceConfig = let - settingsNoNulls = lib.filterAttrsRecursive (_: v: v != null) cfg.settings; - settingsFile = settingsFormat.generate "dyndnsd.toml" settingsNoNulls; - runtimeConfigPath = if cfg.environmentFiles != [] - then "/run/${RuntimeDirectory}/dyndnsd.toml" - else settingsFile; - in { - inherit RuntimeDirectory; - EnvironmentFile = cfg.environmentFiles; - ExecStartPre = lib.mkIf (cfg.environmentFiles != []) [ "'${pkgs.envsubst}/bin/envsubst' -no-unset -i '${settingsFile}' -o '${runtimeConfigPath}'" ]; - ExecStart = [ "" "${pkgs.dyndnsd}/bin/dyndnsd --config '${runtimeConfigPath}'" ]; - } // lib.optionalAttrs cfg.localhost { - IPAddressAllow = [ "localhost" ]; - IPAddressDeny = "any"; + config = lib.mkMerge [ + (lib.mkIf cfg.enable { + systemd.packages = [ pkgs.dyndnsd ]; + + systemd.services.dyndnsd = { + description = "Service that updates a dynamic DNS record"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + startLimitBurst = 1; + + serviceConfig = let + settingsNoNulls = lib.filterAttrsRecursive (_: v: v != null) cfg.settings; + settingsFile = settingsFormat.generate "dyndnsd.toml" settingsNoNulls; + runtimeConfigPath = if cfg.environmentFiles != [] + then "/run/${RuntimeDirectory}/dyndnsd.toml" + else settingsFile; + in { + inherit RuntimeDirectory; + EnvironmentFile = cfg.environmentFiles; + ExecStartPre = lib.mkIf (cfg.environmentFiles != []) [ "'${pkgs.envsubst}/bin/envsubst' -no-unset -i '${settingsFile}' -o '${runtimeConfigPath}'" ]; + ExecStart = [ "" "${pkgs.dyndnsd}/bin/dyndnsd --config '${runtimeConfigPath}'" ]; + } // lib.optionalAttrs cfg.localhost { + IPAddressAllow = [ "localhost" ]; + IPAddressDeny = "any"; + }; }; - }; - }; + }) + (lib.mkIf (cfg.enable && cfg.useNsupdateProgram) { + users.groups.ddns = {}; + systemd.services.dyndnsd.serviceConfig.SupplementaryGroups = [ "ddns" ]; + + systemd.services.bind.preStart = '' + mkdir -m 0755 -p /run/named + if ! [ -f "/run/named/ddns.key" ]; then + (umask 227 && ${config.services.bind.package.out}/sbin/rndc-confgen -c /run/named/ddns.key -u named -a -k ddns 2>/dev/null) + chgrp ddns /run/named/ddns.key + chmod 440 /run/named/ddns.key + fi + ''; + + services.bind.extraConfig = '' + include "/run/named/ddns.key"; + ''; + + services.dyndnsd.settings.update_program = { + bin = lib.mkDefault "${pkgs.dig.dnsutils}/bin/nsupdate"; + args = lib.mkDefault [ "-k" "/run/named/ddns.key" ]; + initial_stdin = lib.mkDefault (if cfg.localhost then "server ::1\n" else null); + stdin_per_zone_update = lib.mkDefault "send\n"; + final_stdin = lib.mkDefault "quit\n"; + ipv4.stdin = lib.mkDefault "update delete {domain}. IN A\nupdate add {domain}. {ttl} IN A {ipv4}\n"; + ipv6.stdin = lib.mkDefault "update delete {domain}. IN AAAA\nupdate add {domain}. {ttl} IN AAAA {ipv6}\n"; + }; + }) + ]; }