2021-04-10 14:52:13 +02:00
|
|
|
# ISC DHCP/IPv4 server configuration
|
2022-03-22 18:13:17 +01:00
|
|
|
{ hostName, config, lib, ... }:
|
2021-03-31 02:11:19 +02:00
|
|
|
|
|
|
|
let
|
|
|
|
dhcpNets =
|
|
|
|
lib.filterAttrs (_: { dhcp, ... }:
|
|
|
|
dhcp != null &&
|
|
|
|
dhcp.server == hostName
|
|
|
|
) config.site.net;
|
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
concatMapDhcpNets = f:
|
|
|
|
lib.pipe dhcpNets [
|
|
|
|
(builtins.mapAttrs f)
|
|
|
|
builtins.attrValues
|
|
|
|
(map (r: if builtins.isList r then r else [ r ]))
|
|
|
|
builtins.concatLists
|
|
|
|
];
|
|
|
|
|
2021-03-31 02:11:19 +02:00
|
|
|
enabled = builtins.length (builtins.attrNames dhcpNets) > 0;
|
|
|
|
in
|
|
|
|
{
|
2023-10-24 00:57:25 +02:00
|
|
|
services.kea.dhcp4 = lib.optionalAttrs enabled {
|
2021-03-31 02:11:19 +02:00
|
|
|
enable = true;
|
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
settings = {
|
|
|
|
interfaces-config.interfaces = builtins.attrNames dhcpNets;
|
|
|
|
dhcp-ddns.enable-updates = true;
|
2023-10-27 23:45:36 +02:00
|
|
|
ddns-send-updates = true;
|
2023-10-28 00:45:24 +02:00
|
|
|
# TODO: use with kea >= 2.5.0
|
|
|
|
# ddns-conflict-resolution-mode = "check-exists-with-dhcid";
|
|
|
|
ddns-use-conflict-resolution = false;
|
2021-05-06 03:21:58 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
subnet4 = concatMapDhcpNets (net: { vlan, subnet4, dhcp, domainName, ... }: {
|
|
|
|
id = vlan;
|
|
|
|
subnet = subnet4;
|
|
|
|
pools = [ {
|
|
|
|
pool = "${dhcp.start} - ${dhcp.end}";
|
|
|
|
} ];
|
2023-10-27 23:45:36 +02:00
|
|
|
renew-timer = dhcp.time / 2;
|
2023-10-24 00:57:25 +02:00
|
|
|
rebind-timer = dhcp.time;
|
|
|
|
valid-lifetime = dhcp.max-time;
|
|
|
|
option-data = [ {
|
|
|
|
space = "dhcp4";
|
|
|
|
name = "routers";
|
|
|
|
code = 3;
|
|
|
|
data = config.site.net.${net}.hosts4.${dhcp.router};
|
|
|
|
} {
|
|
|
|
space = "dhcp4";
|
|
|
|
name = "domain-name";
|
|
|
|
code = 15;
|
|
|
|
data = domainName;
|
|
|
|
} {
|
|
|
|
space = "dhcp4";
|
|
|
|
name = "domain-name-servers";
|
|
|
|
code = 6;
|
|
|
|
data = "172.20.73.8, 9.9.9.9";
|
|
|
|
} ];
|
2023-10-24 01:17:41 +02:00
|
|
|
ddns-qualifying-suffix = domainName;
|
2023-10-24 00:57:25 +02:00
|
|
|
});
|
2021-03-31 02:20:08 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
match-client-id = false;
|
|
|
|
host-reservation-identifiers = [ "hw-address" ];
|
|
|
|
reservations = concatMapDhcpNets (net: { hosts4, dhcp, ... }:
|
2023-10-28 03:20:51 +02:00
|
|
|
lib.pipe dhcp.fixed-hosts [
|
|
|
|
(builtins.mapAttrs (fixedAddr: hwaddr:
|
|
|
|
if hosts4 ? ${fixedAddr}
|
|
|
|
then # fixedAddr is a known hostname
|
|
|
|
let
|
|
|
|
name = fixedAddr;
|
|
|
|
addr = hosts4.${fixedAddr};
|
|
|
|
in {
|
|
|
|
hostname = "${name}.${net}.zentralwerk.org";
|
|
|
|
hw-address = hwaddr;
|
|
|
|
ip-address = addr;
|
|
|
|
}
|
|
|
|
else
|
2023-10-27 23:45:36 +02:00
|
|
|
let
|
|
|
|
names = builtins.attrNames (
|
|
|
|
lib.filterAttrs (_: hostAddr:
|
2023-10-28 03:20:51 +02:00
|
|
|
hostAddr == fixedAddr
|
2023-10-27 23:45:36 +02:00
|
|
|
) hosts4);
|
|
|
|
name = builtins.head names;
|
|
|
|
in
|
2023-10-28 03:20:51 +02:00
|
|
|
if builtins.length names > 0
|
|
|
|
then { # fixedAddr is IPv4 of a known hostname
|
|
|
|
hostname = "${name}.${net}.zentralwerk.org";
|
|
|
|
hw-address = hwaddr;
|
|
|
|
ip-address = hosts4.${name};
|
|
|
|
} # fixedAddr is IPv4?
|
|
|
|
else {
|
|
|
|
hw-address = hwaddr;
|
|
|
|
ip-address = fixedAddr;
|
|
|
|
}
|
|
|
|
))
|
|
|
|
builtins.attrValues
|
|
|
|
(builtins.filter (r: r != null))
|
|
|
|
]
|
|
|
|
);
|
2022-08-22 23:31:18 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
# Netbooting
|
|
|
|
option-def = [ {
|
|
|
|
name = "PXEDiscoveryControl";
|
|
|
|
code = 6;
|
|
|
|
space = "vendor-encapsulated-options-space";
|
|
|
|
type = "uint8";
|
|
|
|
array = false;
|
|
|
|
} {
|
|
|
|
name = "PXEMenuPrompt";
|
|
|
|
code = 10;
|
|
|
|
space = "vendor-encapsulated-options-space";
|
|
|
|
type = "record";
|
|
|
|
array = false;
|
|
|
|
record-types = "uint8,string";
|
|
|
|
} {
|
|
|
|
name = "PXEBootMenu";
|
|
|
|
code = 9;
|
|
|
|
space = "vendor-encapsulated-options-space";
|
|
|
|
type = "record";
|
|
|
|
array = false;
|
|
|
|
record-types = "uint16,uint8,string";
|
|
|
|
} ];
|
|
|
|
client-classes =
|
|
|
|
let
|
|
|
|
rpi4Class = {
|
|
|
|
name = "rpi4-pxe";
|
|
|
|
test = "option[vendor-class-identifier].text == 'PXEClient:Arch:00000:UNDI:002001'";
|
|
|
|
option-data = [ {
|
|
|
|
name = "boot-file-name";
|
|
|
|
data = "bootcode.bin";
|
|
|
|
} {
|
|
|
|
name = "vendor-class-identifier";
|
|
|
|
data = "PXEClient";
|
|
|
|
} {
|
|
|
|
name = "vendor-encapsulated-options";
|
|
|
|
} {
|
|
|
|
name = "PXEBootMenu";
|
|
|
|
csv-format = true;
|
|
|
|
data = "0,17,Raspberry Pi Boot";
|
|
|
|
space = "vendor-encapsulated-options-space";
|
|
|
|
} {
|
|
|
|
name = "PXEDiscoveryControl";
|
|
|
|
data = "3";
|
|
|
|
space = "vendor-encapsulated-options-space";
|
|
|
|
} {
|
|
|
|
name = "PXEMenuPrompt";
|
|
|
|
csv-format = true;
|
|
|
|
data = "0,PXE";
|
|
|
|
space = "vendor-encapsulated-options-space";
|
|
|
|
} ];
|
|
|
|
};
|
2021-06-18 19:56:49 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
pxeClassData = {
|
|
|
|
PXE-Legacy = {
|
|
|
|
arch = "00000";
|
|
|
|
boot-file-name = "netboot.xyz.kpxe";
|
|
|
|
};
|
|
|
|
PXE-UEFI-32-1.arch = "00002";
|
|
|
|
PXE-UEFI-32-2.arch = "00006";
|
|
|
|
PXE-UEFI-64-1.arch = "00007";
|
|
|
|
PXE-UEFI-64-2.arch = "00008";
|
|
|
|
PXE-UEFI-64-3.arch = "00009";
|
|
|
|
};
|
2022-09-14 20:23:55 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
makePxe = name: { boot-file-name ? "netboot.xyz.efi", arch }: {
|
|
|
|
inherit name boot-file-name;
|
|
|
|
test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:${arch}'";
|
|
|
|
next-server = config.site.net.serv.hosts4.nfsroot;
|
|
|
|
};
|
|
|
|
in
|
|
|
|
[ rpi4Class ]
|
|
|
|
++
|
|
|
|
builtins.attrValues (
|
|
|
|
builtins.mapAttrs makePxe pxeClassData
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
2023-10-27 23:45:36 +02:00
|
|
|
services.kea.dhcp6 = lib.optionalAttrs enabled {
|
|
|
|
enable = true;
|
|
|
|
|
|
|
|
settings = {
|
|
|
|
interfaces-config.interfaces = builtins.attrNames dhcpNets;
|
|
|
|
dhcp-ddns.enable-updates = true;
|
|
|
|
ddns-override-no-update = true;
|
|
|
|
ddns-override-client-update = true;
|
|
|
|
ddns-replace-client-name = "when-not-present";
|
2023-10-28 00:45:24 +02:00
|
|
|
# TODO: use with kea >= 2.5.0
|
|
|
|
# ddns-conflict-resolution-mode = "check-exists-with-dhcid";
|
|
|
|
ddns-use-conflict-resolution = false;
|
2023-10-27 23:45:36 +02:00
|
|
|
|
|
|
|
subnet6 = concatMapDhcpNets (net: { vlan, subnets6, dhcp, domainName, ... }:
|
|
|
|
let
|
|
|
|
subnet = subnets6.up4 or subnets6.flpk or null;
|
|
|
|
prefix = builtins.head (builtins.split "::/" subnet);
|
|
|
|
in
|
|
|
|
if subnet != null
|
|
|
|
then {
|
|
|
|
id = vlan;
|
|
|
|
interface = net;
|
|
|
|
inherit subnet;
|
|
|
|
pools = [ {
|
|
|
|
pool = "${prefix}:c3d2:c3d2:c3d2:1000 - ${prefix}:c3d2:c3d2:c3d2:ffff";
|
|
|
|
#pool = subnet;
|
|
|
|
} ];
|
|
|
|
renew-timer = dhcp.time / 2;
|
|
|
|
rebind-timer = dhcp.time;
|
|
|
|
valid-lifetime = dhcp.max-time;
|
|
|
|
#option-data = [ {
|
|
|
|
# space = "dhcp6";
|
|
|
|
# name = "domain-search";
|
|
|
|
# code = 24;
|
|
|
|
# data = domainName;
|
|
|
|
#} {
|
|
|
|
# space = "dhcp6";
|
|
|
|
# name = "dns-servers";
|
|
|
|
# code = 23;
|
|
|
|
# data = "172.20.73.8, 9.9.9.9";
|
|
|
|
#} ];
|
|
|
|
ddns-generated-prefix = "d";
|
|
|
|
ddns-qualifying-suffix = domainName;
|
|
|
|
}
|
|
|
|
else []
|
|
|
|
);
|
|
|
|
|
|
|
|
host-reservation-identifiers = [ "hw-address" ];
|
|
|
|
#reservations = concatMapDhcpNets (net: { hosts6, dhcp, ... }:
|
|
|
|
# builtins.filter (r: r != null) (
|
|
|
|
# builtins.attrValues (
|
|
|
|
# builtins.mapAttrs (name: hwaddr:
|
|
|
|
# let
|
|
|
|
# ip-addresses = lib.pipe hosts6 [
|
|
|
|
# (builtins.mapAttrs (_: hosts6: hosts6.${name} or null))
|
|
|
|
# builtins.attrValues
|
|
|
|
# (builtins.filter (a: a != null))
|
|
|
|
# ];
|
|
|
|
# in
|
|
|
|
# if builtins.trace (lib.generators.toPretty {} ip-addresses) (builtins.length ip-addresses) > 0
|
|
|
|
# then {
|
|
|
|
# hostname = "${name}.${net}.zentralwerk.org";
|
|
|
|
# hw-address = hwaddr;
|
|
|
|
# inherit ip-addresses;
|
|
|
|
# }
|
|
|
|
# else null
|
|
|
|
# ) dhcp.fixed-hosts
|
|
|
|
# )));
|
|
|
|
};
|
|
|
|
};
|
2023-10-24 00:57:25 +02:00
|
|
|
services.kea.dhcp-ddns = lib.optionalAttrs enabled {
|
|
|
|
enable = true;
|
2021-03-31 02:20:08 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
settings = {
|
|
|
|
tsig-keys = [ {
|
|
|
|
name = "dyndns";
|
|
|
|
algorithm = "hmac-sha256";
|
|
|
|
secret = config.site.dyndnsKey;
|
|
|
|
} ];
|
2021-05-06 03:21:58 +02:00
|
|
|
|
2023-10-24 00:57:25 +02:00
|
|
|
forward-ddns.ddns-domains = concatMapDhcpNets (net: { domainName, ... }: {
|
|
|
|
name = "${domainName}.";
|
|
|
|
key-name = "dyndns";
|
|
|
|
dns-servers = [ {
|
|
|
|
ip-address = config.site.net.serv.hosts4.dns;
|
|
|
|
} {
|
|
|
|
ip-address = config.site.net.serv.hosts6.dn42.dns;
|
|
|
|
} ];
|
|
|
|
});
|
|
|
|
reverse-ddns.ddns-domains = map ({ name, ...}: {
|
|
|
|
name = "${name}.";
|
|
|
|
key-name = "dyndns";
|
|
|
|
dns-servers = [ {
|
|
|
|
ip-address = config.site.net.serv.hosts4.dns;
|
|
|
|
} {
|
|
|
|
ip-address = config.site.net.serv.hosts6.dn42.dns;
|
|
|
|
} ];
|
|
|
|
}) (
|
|
|
|
builtins.filter ({ name, dynamic, ... }:
|
|
|
|
dynamic &&
|
2023-10-27 23:45:36 +02:00
|
|
|
(lib.hasSuffix ".in-addr.arpa" name ||
|
|
|
|
lib.hasSuffix ".ip6.arpa" name)
|
2023-10-24 00:57:25 +02:00
|
|
|
) config.site.dns.localZones
|
|
|
|
);
|
|
|
|
};
|
2021-03-31 02:11:19 +02:00
|
|
|
};
|
2023-10-27 23:45:36 +02:00
|
|
|
|
|
|
|
# Hotfix weird ddns service behaviour
|
|
|
|
systemd.services.kea-dhcp-ddns-server.after = [
|
|
|
|
"systemd.services.kea-dhcp4-server.service"
|
|
|
|
"systemd.services.kea-dhcp6-server.service"
|
|
|
|
];
|
2021-03-31 02:11:19 +02:00
|
|
|
}
|