{ flake, config, pkgs, lib, ... }: { config = { services.kea = { dhcp4 = { enable = true; settings = { valid-lifetime = 4000; rebind-timer = 2000; renew-timer = 1000; interfaces-config = { interfaces = [ "lan" "mgmt" ]; }; lease-database = { name = "/var/lib/kea/dhcp4.leases"; persist = true; type = "memfile"; }; client-classes = [ { name = "ipxe"; test = "option[77].hex == 'iPXE'"; next-server = "10.141.0.1"; boot-file-name = "netboot.ipxe"; only-if-required = true; } { name = "uefi-64"; test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; only-if-required = true; option-data = [ { name = "tftp-server-name"; data = "10.141.0.1"; } ]; boot-file-name = "ipxe.efi"; } { name = "uefi-32"; test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; only-if-required = true; option-data = [ { name = "tftp-server-name"; data = "10.141.0.1"; } ]; boot-file-name = "i386-ipxe.efi"; } { name = "legacy"; test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; only-if-required = true; option-data = [ { name = "tftp-server-name"; data = "10.141.0.1"; } ]; boot-file-name = "undionly.kpxe"; } ]; dhcp-ddns.enable-updates = true; ddns-send-updates = false; ddns-override-client-update = true; ddns-override-no-update = true; ddns-replace-client-name = "when-not-present"; ddns-generated-prefix = "noname"; ddns-update-on-renew = true; subnet4 = [ { subnet = "10.141.0.0/24"; option-data = [ { name = "domain-name-servers"; data = "10.141.0.1"; } { name = "broadcast-address"; data = "10.141.0.255"; } { name = "routers"; data = "10.141.0.1"; } { name = "domain-name"; data = "yggdrasil"; } { name = "domain-search"; data = "lan.yggdrasil, yggdrasil"; } ]; ddns-send-updates = true; ddns-qualifying-suffix = "lan.yggdrasil"; pools = [ { pool = "10.141.0.128 - 10.141.0.254"; } ]; require-client-classes = ["ipxe" "uefi-64" "uefi-32" "legacy"]; reservations = [ { hostname = "sif"; hw-address = "3c:e1:a1:52:24:35"; } { hostname = "sif"; hw-address = "ee:32:68:76:83:ac"; } { hostname = "sif"; hw-address = "48:2a:e3:64:62:97"; } { hostname = "eos"; hw-address = "00:d8:61:79:c5:40"; } ]; } { subnet = "10.141.1.0/24"; option-data = [ { name = "domain-name-servers"; data = "10.141.1.1"; } { name = "broadcast-address"; data = "10.141.1.255"; } { name = "domain-name"; data = "yggdrasil"; } { name = "domain-search"; data = "mgmt.yggdrasil, yggdrasil"; } ]; ddns-send-updates = true; ddns-qualifying-suffix = "mgmt.yggdrasil"; pools = [ { pool = "10.141.1.128 - 10.141.1.254"; } ]; reservations = [ { hostname = "switch01"; hw-address = "60:a4:b7:53:94:b5"; ip-address = "10.141.1.2"; } { hostname = "ap01"; hw-address = "74:ac:b9:29:ad:9a"; ip-address = "10.141.1.4"; } ]; } { subnet = "10.141.2.0/24"; option-data = [ { name = "domain-name-servers"; data = "10.141.2.1"; } { name = "broadcast-address"; data = "10.141.2.255"; } { name = "routers"; data = "10.141.2.1"; } ]; ddns-send-updates = false; pools = [ { pool = "10.141.2.128 - 10.141.2.254"; } ]; reservations = []; } ]; }; }; dhcp6 = { enable = true; settings = { interfaces-config = { interfaces = [ "lan" ]; }; lease-database = { name = "/var/lib/kea/dhcp6.leases"; persist = true; type = "memfile"; }; }; }; dhcp-ddns = { enable = true; settings = { forward-ddns = { ddns-domains = [ { name = "lan.yggdrasil."; dns-servers = [ { ip-address = "127.0.0.1"; port = 5353; key-name = "local_key"; } ]; } { name = "mgmt.yggdrasil."; dns-servers = [ { ip-address = "127.0.0.1"; port = 5353; key-name = "local_key"; } ]; } ]; }; reverse-ddns = { ddns-domains = [ { name = "0.141.10.in-addr.arpa."; dns-servers = [ { ip-address = "127.0.0.1"; port = 5353; key-name = "local_key"; } ]; } { name = "1.141.10.in-addr.arpa."; dns-servers = [ { ip-address = "127.0.0.1"; port = 5353; key-name = "local_key"; } ]; } ]; }; }; }; }; systemd.services.kea-dhcp-ddns-server = { preStart = let configLines = [ "" ] ++ lib.mapAttrsToList (k: v: "\"${k}\": ${builtins.toJSON v}" ) config.services.kea.dhcp-ddns.settings; config-template = pkgs.writeText "dhcp-ddns.conf" '' {"DhcpDdns": { ${lib.concatStringsSep ",\n " configLines} }} ''; in '' ${pkgs.envsubst}/bin/envsubst -i "${config-template}" -o "''${RUNTIME_DIRECTORY}/dhcp-ddns.conf" ''; serviceConfig = { ExecStart = lib.mkForce '' ${pkgs.kea}/bin/kea-dhcp-ddns -c "''${RUNTIME_DIRECTORY}/dhcp-ddns.conf" ${lib.escapeShellArgs config.services.kea.dhcp-ddns.extraArgs} ''; LoadCredential = [ "knot-tsig.json.frag:${config.sops.secrets."kea-knot-tsig.json.frag".path}" ]; }; }; sops.secrets."kea-knot-tsig.json.frag" = { format = "binary"; sopsFile = ./knot-tsig.json.frag; }; systemd.services."installer-atftpd" = { description = "TFTP Server for PXE Booting NixOS Installer"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = let installerBuild = flake.nixosConfigurations.installer-x86_64-linux-netboot.config.system.build; ipxe = pkgs.ipxe.override { additionalTargets = { "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; }; }; tftpRoot = pkgs.runCommandLocal "installer-netboot" {} '' mkdir -p $out install -m 0444 -t $out \ ${installerBuild.netbootRamdisk}/initrd \ ${installerBuild.kernel}/bzImage \ ${installerBuild.netbootIpxeScript}/netboot.ipxe \ ${ipxe}/ipxe.efi ${ipxe}/i386-ipxe.efi ${ipxe}/undionly.kpxe ''; in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=10.141.0.1 ${tftpRoot}"; }; }; }