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