nix/nixos-module/container/dhcp-server: migrate from isc-dhcpd to kea-dhcp4
This commit is contained in:
parent
db71886898
commit
9b39803076
|
@ -90,7 +90,7 @@ in
|
|||
Host "inbert.c3d2.de"
|
||||
Host "heise.de"
|
||||
'';
|
||||
}) (lib.optionalAttrs config.services.dhcpd4.enable {
|
||||
}) (lib.optionalAttrs config.services.kea.dhcp4.enable {
|
||||
plugins.exec =
|
||||
let
|
||||
maxTimeout = builtins.foldl' (maxTimeout: net:
|
||||
|
@ -117,11 +117,11 @@ in
|
|||
}) ];
|
||||
|
||||
|
||||
systemd.services.collectd = lib.mkIf config.services.dhcpd4.enable {
|
||||
after = [ "dhcpd4.service" ];
|
||||
systemd.services.collectd = lib.mkIf config.services.kea.dhcp4.enable {
|
||||
after = [ "kea-dhcp4-server.service" ];
|
||||
};
|
||||
|
||||
security.wrappers = lib.mkIf config.services.dhcpd4.enable {
|
||||
security.wrappers = lib.mkIf config.services.kea.dhcp4.enable {
|
||||
collectd-dhcpcount =
|
||||
let
|
||||
dhcpcount = pkgs.runCommand "dhcpcount" {
|
||||
|
|
|
@ -1,36 +1,26 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'date'
|
||||
require 'csv'
|
||||
|
||||
INTERVAL = 300
|
||||
TIMEOUT = ARGV[0].to_i
|
||||
hostname = IO::readlines("/proc/sys/kernel/hostname").join.strip
|
||||
INTERVAL = 60
|
||||
TIMEOUT = ARGV[0].to_i # TODO: now unused
|
||||
hostname = CSV::readlines("/proc/sys/kernel/hostname").join.strip
|
||||
STDOUT.sync = true
|
||||
|
||||
loop do
|
||||
seen = {}
|
||||
count = 0
|
||||
|
||||
addr = nil
|
||||
starts = nil
|
||||
header = nil
|
||||
|
||||
IO::readlines("/var/lib/dhcpd4/dhcpd.leases").each do |line|
|
||||
if line =~ /^lease (.+) \{/
|
||||
addr = $1
|
||||
CSV::readlines("/var/lib/kea/kea-leases4.csv", headers: true).each do |rec|
|
||||
h = rec.to_h
|
||||
addr = h["hwaddr"]
|
||||
next unless addr
|
||||
|
||||
starts = nil
|
||||
elsif line =~ /starts \d+ (.+?);/
|
||||
starts = DateTime.parse($1).to_time
|
||||
elsif line =~ /^\}/
|
||||
now = Time.now
|
||||
if starts and
|
||||
now >= starts and now < starts + TIMEOUT
|
||||
|
||||
unless seen[addr]
|
||||
count += 1
|
||||
seen[addr] = true
|
||||
end
|
||||
end
|
||||
unless seen[addr]
|
||||
count += 1
|
||||
seen[addr] = true
|
||||
end
|
||||
end
|
||||
puts "PUTVAL \"#{hostname}/exec-dhcpd/current_sessions-leases\" interval=#{INTERVAL} N:#{count}"
|
||||
|
|
|
@ -8,98 +8,171 @@ let
|
|||
dhcp.server == hostName
|
||||
) config.site.net;
|
||||
|
||||
concatMapDhcpNets = f:
|
||||
lib.pipe dhcpNets [
|
||||
(builtins.mapAttrs f)
|
||||
builtins.attrValues
|
||||
(map (r: if builtins.isList r then r else [ r ]))
|
||||
builtins.concatLists
|
||||
];
|
||||
|
||||
enabled = builtins.length (builtins.attrNames dhcpNets) > 0;
|
||||
in
|
||||
{
|
||||
services.dhcpd4 = lib.optionalAttrs enabled {
|
||||
services.kea.dhcp4 = lib.optionalAttrs enabled {
|
||||
enable = true;
|
||||
|
||||
interfaces = builtins.attrNames dhcpNets;
|
||||
settings = {
|
||||
interfaces-config.interfaces = builtins.attrNames dhcpNets;
|
||||
dhcp-ddns.enable-updates = true;
|
||||
|
||||
extraConfig = ''
|
||||
${builtins.concatStringsSep "\n" (
|
||||
subnet4 = concatMapDhcpNets (net: { vlan, subnet4, dhcp, domainName, ... }: {
|
||||
id = vlan;
|
||||
subnet = subnet4;
|
||||
pools = [ {
|
||||
pool = "${dhcp.start} - ${dhcp.end}";
|
||||
} ];
|
||||
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";
|
||||
} ];
|
||||
});
|
||||
|
||||
match-client-id = false;
|
||||
host-reservation-identifiers = [ "hw-address" ];
|
||||
reservations = concatMapDhcpNets (net: { hosts4, dhcp, ... }:
|
||||
builtins.attrValues (
|
||||
builtins.mapAttrs (net: { dhcp, subnet4Net, subnet4Len, domainName, ...}:
|
||||
''
|
||||
ddns-update-style standard;
|
||||
key dyndns {
|
||||
algorithm hmac-sha256;
|
||||
secret ${config.site.dyndnsKey};
|
||||
};
|
||||
zone ${domainName}. {
|
||||
primary ${config.site.net.serv.hosts4.dns};
|
||||
primary6 ${config.site.net.serv.hosts6.dn42.dns};
|
||||
key dyndns;
|
||||
}
|
||||
${lib.concatMapStrings ({ name, dynamic, ... }:
|
||||
lib.optionalString (
|
||||
dynamic &&
|
||||
lib.hasSuffix ".in-addr.arpa" name
|
||||
) ''
|
||||
zone ${name}. {
|
||||
primary ${config.site.net.serv.hosts4.dns};
|
||||
primary6 ${config.site.net.serv.hosts6.dn42.dns};
|
||||
key dyndns;
|
||||
}
|
||||
''
|
||||
) config.site.dns.localZones}
|
||||
builtins.mapAttrs (name: hwaddr: {
|
||||
hostname = "${name}.${net}.zentralwerk.org";
|
||||
hw-address = hwaddr;
|
||||
ip-address = hosts4.${name};
|
||||
}) dhcp.fixed-hosts
|
||||
));
|
||||
|
||||
option guid code 97 = text;
|
||||
group {
|
||||
default-lease-time ${toString dhcp.time};
|
||||
max-lease-time ${toString dhcp.max-time};
|
||||
option routers ${config.site.net.${net}.hosts4.${dhcp.router}};
|
||||
option domain-name "${domainName}";
|
||||
option domain-name-servers 172.20.73.8, 9.9.9.9;
|
||||
ddns-domainname "${domainName}";
|
||||
# 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";
|
||||
} ];
|
||||
};
|
||||
|
||||
class "pxeclients" {
|
||||
match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
|
||||
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";
|
||||
};
|
||||
|
||||
next-server ${config.site.net.serv.hosts4.nfsroot};
|
||||
option tftp-server-address ${config.site.net.serv.hosts4.nfsroot};
|
||||
if suffix(reverse(1, option guid), 5) = 34:69:50:52:00 {
|
||||
# RPi4
|
||||
option vendor-class-identifier "PXEClient";
|
||||
option vendor-encapsulated-options "Raspberry Pi Boot";
|
||||
option tftp-server-name "${config.site.net.serv.hosts4.nfsroot}";
|
||||
} elsif option pxe-system-type = 00:00 {
|
||||
filename "netboot.xyz.kpxe"; # BIOS
|
||||
} elsif option pxe-system-type = 00:07 {
|
||||
filename "netboot.xyz.efi"; # EFI
|
||||
option bootfile-name "netboot.xyz.efi";
|
||||
} elsif option pxe-system-type = 00:06 {
|
||||
filename "netboot.xyz.efi"; # ia32_EFI
|
||||
}
|
||||
}
|
||||
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
|
||||
);
|
||||
};
|
||||
};
|
||||
services.kea.dhcp-ddns = lib.optionalAttrs enabled {
|
||||
enable = true;
|
||||
|
||||
subnet ${subnet4Net} netmask ${lib.netmasks.${toString subnet4Len}} {
|
||||
range ${dhcp.start} ${dhcp.end};
|
||||
settings = {
|
||||
tsig-keys = [ {
|
||||
name = "dyndns";
|
||||
algorithm = "hmac-sha256";
|
||||
secret = config.site.dyndnsKey;
|
||||
} ];
|
||||
|
||||
# always assign the same IP to the same MAC address.
|
||||
# fixes changing IP for PXE clients.
|
||||
ignore-client-uids true;
|
||||
}
|
||||
|
||||
update-static-leases on;
|
||||
|
||||
${builtins.concatStringsSep "\n" (
|
||||
builtins.attrValues (
|
||||
builtins.mapAttrs (addr: hwaddr:
|
||||
''
|
||||
host ${addr} {
|
||||
hardware ethernet ${hwaddr};
|
||||
fixed-address ${addr};
|
||||
}
|
||||
''
|
||||
) dhcp.fixed-hosts
|
||||
)
|
||||
)}
|
||||
}
|
||||
''
|
||||
) dhcpNets
|
||||
)
|
||||
)}
|
||||
'';
|
||||
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 &&
|
||||
lib.hasSuffix ".in-addr.arpa" name
|
||||
) config.site.dns.localZones
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue