nixos ddclient: support multiple domains and run via systemd timer

a) Some providers can update multiple domains - support that.

b) Make "zone" and "script" configurable. Some providers require these.

c) Instead of leaving the ddclient daemon running all the time, use a systemd
timer to kick it off.

d) Don't use a predefined user - run everything via DynamicUser

e) Add documentation

Changed files
+69 -55
nixos
modules
misc
services
networking
pkgs
tools
networking
ddclient
+2 -2
nixos/modules/misc/ids.nix
···
#dialout = 27; # unused
polkituser = 28;
#utmp = 29; # unused
-
ddclient = 30;
+
# ddclient = 30; # converted to DynamicUser = true
davfs2 = 31;
#disnix = 33; # unused
osgi = 34;
···
dialout = 27;
#polkituser = 28; # currently unused, polkitd doesn't need a group
utmp = 29;
-
ddclient = 30;
+
# ddclient = 30; # converted to DynamicUser = true
davfs2 = 31;
disnix = 33;
osgi = 34;
+2
nixos/modules/rename.nix
···
(config:
let enabled = getAttrFromPath [ "services" "printing" "gutenprint" ] config;
in if enabled then [ pkgs.gutenprint ] else [ ]))
+
(mkRenamedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ])
+
(mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
(mkRenamedOptionModule [ "services" "elasticsearch" "host" ] [ "services" "elasticsearch" "listenAddress" ])
(mkRenamedOptionModule [ "services" "graphite" "api" "host" ] [ "services" "graphite" "api" "listenAddress" ])
(mkRenamedOptionModule [ "services" "graphite" "web" "host" ] [ "services" "graphite" "web" "listenAddress" ])
+59 -53
nixos/modules/services/networking/ddclient.nix
···
let
cfg = config.services.ddclient;
boolToStr = bool: if bool then "yes" else "no";
+
dataDir = "/var/lib/ddclient";
configText = ''
# This file can be used as a template for configFile or is automatically generated by Nix options.
-
daemon=${toString cfg.interval}
-
cache=${cfg.homeDir}/ddclient.cache
-
pid=/run/ddclient/ddclient.pid
-
foreground=NO
+
cache=${dataDir}/ddclient.cache
+
foreground=YES
use=${cfg.use}
login=${cfg.username}
password=${cfg.password}
protocol=${cfg.protocol}
-
${let server = cfg.server; in
-
lib.optionalString (server != "") "server=${server}"}
+
${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
+
${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
+
${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"}
ssl=${boolToStr cfg.ssl}
wildcard=YES
quiet=${boolToStr cfg.quiet}
verbose=${boolToStr cfg.verbose}
-
${cfg.domain}
+
${lib.concatStringsSep "," cfg.domains}
${cfg.extraConfig}
'';
···
'';
};
-
homeDir = mkOption {
-
default = "/var/lib/ddclient";
-
type = str;
-
description = "Home directory for the daemon user.";
-
};
-
-
domain = mkOption {
-
default = "";
-
type = str;
+
domains = mkOption {
+
default = [ "" ];
+
type = listOf str;
description = ''
-
Domain name to synchronize.
+
Domain name(s) to synchronize.
'';
};
···
default = "";
type = str;
description = ''
-
Username.
+
User name.
'';
};
···
};
interval = mkOption {
-
default = 600;
-
type = int;
-
description = "The interval at which to run the check and update.";
+
default = "10min";
+
type = str;
+
description = ''
+
The interval at which to run the check and update.
+
See <command>man 7 systemd.time</command> for the format.
+
'';
};
configFile = mkOption {
···
default = "dyndns2";
type = str;
description = ''
-
Protocol to use with dynamic DNS provider (see http://sourceforge.net/apps/trac/ddclient/wiki/Protocols).
+
Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
'';
};
···
'';
};
-
extraConfig = mkOption {
+
+
quiet = mkOption {
+
default = false;
+
type = bool;
+
description = ''
+
Print no messages for unnecessary updates.
+
'';
+
};
+
+
script = mkOption {
default = "";
-
type = lines;
+
type = str;
description = ''
-
Extra configuration. Contents will be added verbatim to the configuration file.
+
script as required by some providers.
'';
};
···
'';
};
-
quiet = mkOption {
-
default = false;
-
type = bool;
+
zone = mkOption {
+
default = "";
+
type = str;
description = ''
-
Print no messages for unnecessary updates.
+
zone as required by some providers.
+
'';
+
};
+
+
extraConfig = mkOption {
+
default = "";
+
type = lines;
+
description = ''
+
Extra configuration. Contents will be added verbatim to the configuration file.
'';
};
};
···
###### implementation
config = mkIf config.services.ddclient.enable {
-
-
users = {
-
extraGroups.ddclient.gid = config.ids.gids.ddclient;
-
-
extraUsers.ddclient = {
-
uid = config.ids.uids.ddclient;
-
description = "ddclient daemon user";
-
group = "ddclient";
-
home = cfg.homeDir;
-
createHome = true;
-
};
-
};
-
environment.etc."ddclient.conf" = {
enable = cfg.configFile == "/etc/ddclient.conf";
-
uid = config.ids.uids.ddclient;
-
gid = config.ids.gids.ddclient;
mode = "0600";
text = configText;
};
···
after = [ "network.target" ];
restartTriggers = [ config.environment.etc."ddclient.conf".source ];
-
serviceConfig = {
-
RuntimeDirectory = "ddclient";
-
# we cannot run in forking mode as it swallows all the program output
-
Type = "simple";
-
User = "ddclient";
-
Group = "ddclient";
-
ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -foreground -file ${cfg.configFile}";
-
ProtectSystem = "full";
-
PrivateTmp = true;
+
serviceConfig = rec {
+
DynamicUser = true;
+
RuntimeDirectory = StateDirectory;
+
StateDirectory = builtins.baseNameOf dataDir;
+
Type = "oneshot";
+
ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf";
+
ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
+
};
+
};
+
+
systemd.timers.ddclient = {
+
description = "Run ddclient";
+
wantedBy = [ "timers.target" ];
+
timerConfig = {
+
OnBootSec = cfg.interval;
+
OnUnitInactiveSec = cfg.interval;
};
};
};
+6
pkgs/tools/networking/ddclient/default.nix
···
sha256 = "1j8zdn7fy7i0bjk3jf0hxnbnshc2yf054vxq64imxdpfd7n5zgfy";
};
+
# perl packages by default get devdoc which isn't present
outputs = [ "out" ];
buildInputs = with perlPackages; [ IOSocketSSL DigestSHA1 ];
···
'';
installPhase = ''
+
runHook preInstall
+
install -Dm755 ddclient $out/bin/ddclient
+
install -Dm644 -t $out/share/doc/ddclient COP* ChangeLog README.* RELEASENOTE
+
+
runHook postInstall
'';
# there are no tests distributed with ddclient