Skip to content

Commit

Permalink
Add NixOS option useNsupdateProgram to simplify setup with nsupdate
Browse files Browse the repository at this point in the history
  • Loading branch information
Luflosi committed Jun 5, 2024
1 parent 4010de1 commit 84f1f97
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 44 deletions.
20 changes: 1 addition & 19 deletions nix/e2e-test.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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'
Expand All @@ -48,9 +40,6 @@ self:
enable = true;
forward = "only";
forwarders = [];
extraConfig = ''
include "/run/named/ddns.key";
'';
extraOptions = ''
empty-zones-enable no;
'';
Expand All @@ -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";
Expand Down
89 changes: 64 additions & 25 deletions nix/module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
};
})
];
}

0 comments on commit 84f1f97

Please sign in to comment.