1{ config, pkgs, lib, ... }:
2
3let
4 cfg = config.services.ddclient;
5 boolToStr = bool: if bool then "yes" else "no";
6 dataDir = "/var/lib/ddclient";
7
8 configText = ''
9 # This file can be used as a template for configFile or is automatically generated by Nix options.
10 cache=${dataDir}/ddclient.cache
11 foreground=YES
12 use=${cfg.use}
13 login=${cfg.username}
14 password=${cfg.password}
15 protocol=${cfg.protocol}
16 ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
17 ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
18 ${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"}
19 ssl=${boolToStr cfg.ssl}
20 wildcard=YES
21 quiet=${boolToStr cfg.quiet}
22 verbose=${boolToStr cfg.verbose}
23 ${cfg.extraConfig}
24 ${lib.concatStringsSep "," cfg.domains}
25 '';
26
27in
28
29with lib;
30
31{
32
33 imports = [
34 (mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ]
35 (config:
36 let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
37 in if value != "" then [ value ] else []))
38 (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
39 ];
40
41 ###### interface
42
43 options = {
44
45 services.ddclient = with lib.types; {
46
47 enable = mkOption {
48 default = false;
49 type = bool;
50 description = ''
51 Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
52 '';
53 };
54
55 domains = mkOption {
56 default = [ "" ];
57 type = listOf str;
58 description = ''
59 Domain name(s) to synchronize.
60 '';
61 };
62
63 username = mkOption {
64 default = "";
65 type = str;
66 description = ''
67 User name.
68 '';
69 };
70
71 password = mkOption {
72 default = "";
73 type = str;
74 description = ''
75 Password. WARNING: The password becomes world readable in the Nix store.
76 '';
77 };
78
79 interval = mkOption {
80 default = "10min";
81 type = str;
82 description = ''
83 The interval at which to run the check and update.
84 See <command>man 7 systemd.time</command> for the format.
85 '';
86 };
87
88 configFile = mkOption {
89 default = "/etc/ddclient.conf";
90 type = path;
91 description = ''
92 Path to configuration file.
93 When set to the default '/etc/ddclient.conf' it will be populated with the various other options in this module. When it is changed (for example: '/root/nixos/secrets/ddclient.conf') the file read directly to configure ddclient. This is a source of impurity.
94 The purpose of this is to avoid placing secrets into the store.
95 '';
96 example = "/root/nixos/secrets/ddclient.conf";
97 };
98
99 protocol = mkOption {
100 default = "dyndns2";
101 type = str;
102 description = ''
103 Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
104 '';
105 };
106
107 server = mkOption {
108 default = "";
109 type = str;
110 description = ''
111 Server address.
112 '';
113 };
114
115 ssl = mkOption {
116 default = true;
117 type = bool;
118 description = ''
119 Whether to use to use SSL/TLS to connect to dynamic DNS provider.
120 '';
121 };
122
123
124 quiet = mkOption {
125 default = false;
126 type = bool;
127 description = ''
128 Print no messages for unnecessary updates.
129 '';
130 };
131
132 script = mkOption {
133 default = "";
134 type = str;
135 description = ''
136 script as required by some providers.
137 '';
138 };
139
140 use = mkOption {
141 default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '";
142 type = str;
143 description = ''
144 Method to determine the IP address to send to the dynamic DNS provider.
145 '';
146 };
147
148 verbose = mkOption {
149 default = true;
150 type = bool;
151 description = ''
152 Print verbose information.
153 '';
154 };
155
156 zone = mkOption {
157 default = "";
158 type = str;
159 description = ''
160 zone as required by some providers.
161 '';
162 };
163
164 extraConfig = mkOption {
165 default = "";
166 type = lines;
167 description = ''
168 Extra configuration. Contents will be added verbatim to the configuration file.
169 '';
170 };
171 };
172 };
173
174
175 ###### implementation
176
177 config = mkIf config.services.ddclient.enable {
178 environment.etc."ddclient.conf" = {
179 enable = cfg.configFile == "/etc/ddclient.conf";
180 mode = "0600";
181 text = configText;
182 };
183
184 systemd.services.ddclient = {
185 description = "Dynamic DNS Client";
186 wantedBy = [ "multi-user.target" ];
187 after = [ "network.target" ];
188 restartTriggers = [ config.environment.etc."ddclient.conf".source ];
189
190 serviceConfig = rec {
191 DynamicUser = true;
192 RuntimeDirectory = StateDirectory;
193 StateDirectory = builtins.baseNameOf dataDir;
194 Type = "oneshot";
195 ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf";
196 ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
197 };
198 };
199
200 systemd.timers.ddclient = {
201 description = "Run ddclient";
202 wantedBy = [ "timers.target" ];
203 timerConfig = {
204 OnBootSec = cfg.interval;
205 OnUnitInactiveSec = cfg.interval;
206 };
207 };
208 };
209}