99 lines
3.2 KiB
Nix
99 lines
3.2 KiB
Nix
# LAN DHCP (Kea) and DNS (Blocky). Fully declarative: one Kea subnet per VLAN
|
|
# with dhcp.enable, Blocky as the blocklist resolver every DHCP lease points
|
|
# at. Blocky's HTTP listener (:4000) serves Prometheus metrics, scraped by
|
|
# control over the mesh (firewall.nix scopes it to the mesh subnet).
|
|
{ settings }:
|
|
{ lib, ... }:
|
|
let
|
|
cfg = settings;
|
|
dhcpVlans = lib.filterAttrs (_: vlan: vlan.dhcp.enable) cfg.vlans;
|
|
in
|
|
{
|
|
services.kea.dhcp4 = {
|
|
enable = true;
|
|
settings = {
|
|
interfaces-config = {
|
|
interfaces = lib.mapAttrsToList (name: _: "vlan-${name}") dhcpVlans;
|
|
# The unit orders after network-online.target, which under networkd
|
|
# only waits for the WAN carrier (the vlan-* links are
|
|
# RequiredForOnline=no), so Kea can start before vlan-* have their
|
|
# addresses. By default it then logs the failed bind and runs with no
|
|
# socket at all: clients' DISCOVERs reach vlan-lan and nobody answers.
|
|
# Insist on every socket and keep retrying while networkd catches up;
|
|
# if it still cannot bind, exit and let systemd restart the unit.
|
|
service-sockets-require-all = true;
|
|
service-sockets-max-retries = 60;
|
|
service-sockets-retry-wait-time = 1000;
|
|
};
|
|
lease-database = {
|
|
type = "memfile";
|
|
persist = true;
|
|
name = "/var/lib/kea/dhcp4.leases";
|
|
};
|
|
valid-lifetime = 86400;
|
|
subnet4 = lib.mapAttrsToList (name: vlan: {
|
|
id = vlan.id;
|
|
subnet = vlan.subnet;
|
|
interface = "vlan-${name}";
|
|
valid-lifetime = vlan.dhcp.leaseTime;
|
|
pools = [ { pool = "${vlan.dhcp.pool.from} - ${vlan.dhcp.pool.to}"; } ];
|
|
reservations = lib.mapAttrsToList (host: res: {
|
|
hostname = host;
|
|
hw-address = res.hwAddress;
|
|
ip-address = res.ipAddress;
|
|
}) vlan.dhcp.reservations;
|
|
option-data = [
|
|
{
|
|
name = "routers";
|
|
data = vlan.address;
|
|
}
|
|
{
|
|
name = "domain-name-servers";
|
|
data = vlan.address;
|
|
}
|
|
];
|
|
}) dhcpVlans;
|
|
};
|
|
};
|
|
|
|
# The nixpkgs unit already has Restart=on-failure; space the restarts out so
|
|
# a persistent bind failure does not trip the start-rate limit.
|
|
systemd.services.kea-dhcp4-server.serviceConfig.RestartSec = 5;
|
|
|
|
services.blocky = {
|
|
enable = true;
|
|
settings = {
|
|
ports = {
|
|
dns = 53;
|
|
http = 4000;
|
|
};
|
|
upstreams.groups.default = [
|
|
"9.9.9.9"
|
|
"149.112.112.112"
|
|
"2620:fe::fe"
|
|
];
|
|
blocking = {
|
|
denylists.ads = [
|
|
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
|
|
];
|
|
clientGroupsBlock.default = [ "ads" ];
|
|
};
|
|
caching = {
|
|
minTime = "5m";
|
|
prefetching = true;
|
|
};
|
|
prometheus.enable = true;
|
|
};
|
|
};
|
|
|
|
# The router itself resolves via public resolvers, not via Blocky, so DNS
|
|
# for deploys/updates survives a broken local resolver. networkd would
|
|
# enable systemd-resolved by default, whose stub listener on 127.0.0.53:53
|
|
# makes Blocky's wildcard :53 bind fail — plain resolv.conf instead.
|
|
services.resolved.enable = false;
|
|
networking.nameservers = [
|
|
"9.9.9.9"
|
|
"1.1.1.1"
|
|
];
|
|
}
|