1{ config
2, lib
3, pkgs
4, ...
5}:
6
7with lib;
8
9let
10 cfg = config.services.kea;
11
12 xor = x: y: (!x && y) || (x && !y);
13 format = pkgs.formats.json {};
14
15 chooseNotNull = x: y: if x != null then x else y;
16
17 ctrlAgentConfig = chooseNotNull cfg.ctrl-agent.configFile (format.generate "kea-ctrl-agent.conf" {
18 Control-agent = cfg.ctrl-agent.settings;
19 });
20
21 dhcp4Config = chooseNotNull cfg.dhcp4.configFile (format.generate "kea-dhcp4.conf" {
22 Dhcp4 = cfg.dhcp4.settings;
23 });
24
25 dhcp6Config = chooseNotNull cfg.dhcp6.configFile (format.generate "kea-dhcp6.conf" {
26 Dhcp6 = cfg.dhcp6.settings;
27 });
28
29 dhcpDdnsConfig = chooseNotNull cfg.dhcp-ddns.configFile (format.generate "kea-dhcp-ddns.conf" {
30 DhcpDdns = cfg.dhcp-ddns.settings;
31 });
32
33 package = pkgs.kea;
34in
35{
36 options.services.kea = with types; {
37 ctrl-agent = mkOption {
38 description = lib.mdDoc ''
39 Kea Control Agent configuration
40 '';
41 default = {};
42 type = submodule {
43 options = {
44 enable = mkEnableOption (lib.mdDoc "Kea Control Agent");
45
46 extraArgs = mkOption {
47 type = listOf str;
48 default = [];
49 description = lib.mdDoc ''
50 List of additional arguments to pass to the daemon.
51 '';
52 };
53
54 configFile = mkOption {
55 type = nullOr path;
56 default = null;
57 description = lib.mdDoc ''
58 Kea Control Agent configuration as a path, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html>.
59
60 Takes preference over [settings](#opt-services.kea.ctrl-agent.settings).
61 Most users should prefer using [settings](#opt-services.kea.ctrl-agent.settings) instead.
62 '';
63 };
64
65 settings = mkOption {
66 type = format.type;
67 default = null;
68 description = lib.mdDoc ''
69 Kea Control Agent configuration as an attribute set, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html>.
70 '';
71 };
72 };
73 };
74 };
75
76 dhcp4 = mkOption {
77 description = lib.mdDoc ''
78 DHCP4 Server configuration
79 '';
80 default = {};
81 type = submodule {
82 options = {
83 enable = mkEnableOption (lib.mdDoc "Kea DHCP4 server");
84
85 extraArgs = mkOption {
86 type = listOf str;
87 default = [];
88 description = lib.mdDoc ''
89 List of additional arguments to pass to the daemon.
90 '';
91 };
92
93 configFile = mkOption {
94 type = nullOr path;
95 default = null;
96 description = lib.mdDoc ''
97 Kea DHCP4 configuration as a path, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html>.
98
99 Takes preference over [settings](#opt-services.kea.dhcp4.settings).
100 Most users should prefer using [settings](#opt-services.kea.dhcp4.settings) instead.
101 '';
102 };
103
104 settings = mkOption {
105 type = format.type;
106 default = null;
107 example = {
108 valid-lifetime = 4000;
109 renew-timer = 1000;
110 rebind-timer = 2000;
111 interfaces-config = {
112 interfaces = [
113 "eth0"
114 ];
115 };
116 lease-database = {
117 type = "memfile";
118 persist = true;
119 name = "/var/lib/kea/dhcp4.leases";
120 };
121 subnet4 = [ {
122 subnet = "192.0.2.0/24";
123 pools = [ {
124 pool = "192.0.2.100 - 192.0.2.240";
125 } ];
126 } ];
127 };
128 description = lib.mdDoc ''
129 Kea DHCP4 configuration as an attribute set, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html>.
130 '';
131 };
132 };
133 };
134 };
135
136 dhcp6 = mkOption {
137 description = lib.mdDoc ''
138 DHCP6 Server configuration
139 '';
140 default = {};
141 type = submodule {
142 options = {
143 enable = mkEnableOption (lib.mdDoc "Kea DHCP6 server");
144
145 extraArgs = mkOption {
146 type = listOf str;
147 default = [];
148 description = lib.mdDoc ''
149 List of additional arguments to pass to the daemon.
150 '';
151 };
152
153 configFile = mkOption {
154 type = nullOr path;
155 default = null;
156 description = lib.mdDoc ''
157 Kea DHCP6 configuration as a path, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html>.
158
159 Takes preference over [settings](#opt-services.kea.dhcp6.settings).
160 Most users should prefer using [settings](#opt-services.kea.dhcp6.settings) instead.
161 '';
162 };
163
164 settings = mkOption {
165 type = format.type;
166 default = null;
167 example = {
168 valid-lifetime = 4000;
169 renew-timer = 1000;
170 rebind-timer = 2000;
171 preferred-lifetime = 3000;
172 interfaces-config = {
173 interfaces = [
174 "eth0"
175 ];
176 };
177 lease-database = {
178 type = "memfile";
179 persist = true;
180 name = "/var/lib/kea/dhcp6.leases";
181 };
182 subnet6 = [ {
183 subnet = "2001:db8:1::/64";
184 pools = [ {
185 pool = "2001:db8:1::1-2001:db8:1::ffff";
186 } ];
187 } ];
188 };
189 description = lib.mdDoc ''
190 Kea DHCP6 configuration as an attribute set, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html>.
191 '';
192 };
193 };
194 };
195 };
196
197 dhcp-ddns = mkOption {
198 description = lib.mdDoc ''
199 Kea DHCP-DDNS configuration
200 '';
201 default = {};
202 type = submodule {
203 options = {
204 enable = mkEnableOption (lib.mdDoc "Kea DDNS server");
205
206 extraArgs = mkOption {
207 type = listOf str;
208 default = [];
209 description = lib.mdDoc ''
210 List of additional arguments to pass to the daemon.
211 '';
212 };
213
214 configFile = mkOption {
215 type = nullOr path;
216 default = null;
217 description = lib.mdDoc ''
218 Kea DHCP-DDNS configuration as a path, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html>.
219
220 Takes preference over [settings](#opt-services.kea.dhcp-ddns.settings).
221 Most users should prefer using [settings](#opt-services.kea.dhcp-ddns.settings) instead.
222 '';
223 };
224
225 settings = mkOption {
226 type = format.type;
227 default = null;
228 example = {
229 ip-address = "127.0.0.1";
230 port = 53001;
231 dns-server-timeout = 100;
232 ncr-protocol = "UDP";
233 ncr-format = "JSON";
234 tsig-keys = [ ];
235 forward-ddns = {
236 ddns-domains = [ ];
237 };
238 reverse-ddns = {
239 ddns-domains = [ ];
240 };
241 };
242 description = lib.mdDoc ''
243 Kea DHCP-DDNS configuration as an attribute set, see <https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html>.
244 '';
245 };
246 };
247 };
248 };
249 };
250
251 config = let
252 commonServiceConfig = {
253 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
254 DynamicUser = true;
255 User = "kea";
256 ConfigurationDirectory = "kea";
257 RuntimeDirectory = "kea";
258 StateDirectory = "kea";
259 UMask = "0077";
260 };
261 in mkIf (cfg.ctrl-agent.enable || cfg.dhcp4.enable || cfg.dhcp6.enable || cfg.dhcp-ddns.enable) (mkMerge [
262 {
263 environment.systemPackages = [ package ];
264 }
265
266 (mkIf cfg.ctrl-agent.enable {
267 assertions = [{
268 assertion = xor (cfg.ctrl-agent.settings == null) (cfg.ctrl-agent.configFile == null);
269 message = "Either services.kea.ctrl-agent.settings or services.kea.ctrl-agent.configFile must be set to a non-null value.";
270 }];
271
272 environment.etc."kea/ctrl-agent.conf".source = ctrlAgentConfig;
273
274 systemd.services.kea-ctrl-agent = {
275 description = "Kea Control Agent";
276 documentation = [
277 "man:kea-ctrl-agent(8)"
278 "https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html"
279 ];
280
281 after = [
282 "network-online.target"
283 "time-sync.target"
284 ];
285 wantedBy = [
286 "kea-dhcp4-server.service"
287 "kea-dhcp6-server.service"
288 "kea-dhcp-ddns-server.service"
289 ];
290
291 environment = {
292 KEA_PIDFILE_DIR = "/run/kea";
293 KEA_LOCKFILE_DIR = "/run/kea";
294 };
295
296 restartTriggers = [
297 ctrlAgentConfig
298 ];
299
300 serviceConfig = {
301 ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.ctrl-agent.extraArgs}";
302 KillMode = "process";
303 Restart = "on-failure";
304 } // commonServiceConfig;
305 };
306 })
307
308 (mkIf cfg.dhcp4.enable {
309 assertions = [{
310 assertion = xor (cfg.dhcp4.settings == null) (cfg.dhcp4.configFile == null);
311 message = "Either services.kea.dhcp4.settings or services.kea.dhcp4.configFile must be set to a non-null value.";
312 }];
313
314 environment.etc."kea/dhcp4-server.conf".source = dhcp4Config;
315
316 systemd.services.kea-dhcp4-server = {
317 description = "Kea DHCP4 Server";
318 documentation = [
319 "man:kea-dhcp4(8)"
320 "https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp4-srv.html"
321 ];
322
323 after = [
324 "network-online.target"
325 "time-sync.target"
326 ];
327 wantedBy = [
328 "multi-user.target"
329 ];
330
331 environment = {
332 KEA_PIDFILE_DIR = "/run/kea";
333 KEA_LOCKFILE_DIR = "/run/kea";
334 };
335
336 restartTriggers = [
337 dhcp4Config
338 ];
339
340 serviceConfig = {
341 ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
342 # Kea does not request capabilities by itself
343 AmbientCapabilities = [
344 "CAP_NET_BIND_SERVICE"
345 "CAP_NET_RAW"
346 ];
347 CapabilityBoundingSet = [
348 "CAP_NET_BIND_SERVICE"
349 "CAP_NET_RAW"
350 ];
351 } // commonServiceConfig;
352 };
353 })
354
355 (mkIf cfg.dhcp6.enable {
356 assertions = [{
357 assertion = xor (cfg.dhcp6.settings == null) (cfg.dhcp6.configFile == null);
358 message = "Either services.kea.dhcp6.settings or services.kea.dhcp6.configFile must be set to a non-null value.";
359 }];
360
361 environment.etc."kea/dhcp6-server.conf".source = dhcp6Config;
362
363 systemd.services.kea-dhcp6-server = {
364 description = "Kea DHCP6 Server";
365 documentation = [
366 "man:kea-dhcp6(8)"
367 "https://kea.readthedocs.io/en/kea-${package.version}/arm/dhcp6-srv.html"
368 ];
369
370 after = [
371 "network-online.target"
372 "time-sync.target"
373 ];
374 wantedBy = [
375 "multi-user.target"
376 ];
377
378 environment = {
379 KEA_PIDFILE_DIR = "/run/kea";
380 KEA_LOCKFILE_DIR = "/run/kea";
381 };
382
383 restartTriggers = [
384 dhcp6Config
385 ];
386
387 serviceConfig = {
388 ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}";
389 # Kea does not request capabilities by itself
390 AmbientCapabilities = [
391 "CAP_NET_BIND_SERVICE"
392 ];
393 CapabilityBoundingSet = [
394 "CAP_NET_BIND_SERVICE"
395 ];
396 } // commonServiceConfig;
397 };
398 })
399
400 (mkIf cfg.dhcp-ddns.enable {
401 assertions = [{
402 assertion = xor (cfg.dhcp-ddns.settings == null) (cfg.dhcp-ddns.configFile == null);
403 message = "Either services.kea.dhcp-ddns.settings or services.kea.dhcp-ddns.configFile must be set to a non-null value.";
404 }];
405
406 environment.etc."kea/dhcp-ddns.conf".source = dhcpDdnsConfig;
407
408 systemd.services.kea-dhcp-ddns-server = {
409 description = "Kea DHCP-DDNS Server";
410 documentation = [
411 "man:kea-dhcp-ddns(8)"
412 "https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html"
413 ];
414
415 after = [
416 "network-online.target"
417 "time-sync.target"
418 ];
419 wantedBy = [
420 "multi-user.target"
421 ];
422
423 environment = {
424 KEA_PIDFILE_DIR = "/run/kea";
425 KEA_LOCKFILE_DIR = "/run/kea";
426 };
427
428 restartTriggers = [
429 dhcpDdnsConfig
430 ];
431
432 serviceConfig = {
433 ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}";
434 AmbientCapabilities = [
435 "CAP_NET_BIND_SERVICE"
436 ];
437 CapabilityBoundingSet = [
438 "CAP_NET_BIND_SERVICE"
439 ];
440 } // commonServiceConfig;
441 };
442 })
443
444 ]);
445
446 meta.maintainers = with maintainers; [ hexa ];
447 # uses attributes of the linked package
448 meta.buildDocsInSandbox = false;
449}