From 70a851c71d59c67defa19f9742fda61e24bba6e3 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 13 Jul 2026 21:56:08 +0200 Subject: [PATCH] feat(mercury): switch to pihole-ftl (declarative static leases) - services/pihole.nix: DNS adblock + DHCP, upstream unbound, .sol domain, jupiter static lease 00:e0:4c:3c:a3:1f -> 10.0.0.20 (declarative!) - drop services/adguardhome.nix; mercury imports pihole - admin password set via 'pihole setpassword' post-boot (not in repo) --- hosts/mercury/configuration.nix | 2 +- services/adguardhome.nix | 67 --------------------------------- services/pihole.nix | 46 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 68 deletions(-) delete mode 100644 services/adguardhome.nix create mode 100644 services/pihole.nix diff --git a/hosts/mercury/configuration.nix b/hosts/mercury/configuration.nix index d6233e5..7be2f35 100644 --- a/hosts/mercury/configuration.nix +++ b/hosts/mercury/configuration.nix @@ -8,7 +8,7 @@ ../../common.nix # shared base: user / ssh / nix / firewall ./secrets.nix # sops-nix: darman password (age key on boot part.) ../../services/unbound.nix # local recursive resolver (127.0.0.1:5335) - ../../services/adguardhome.nix # DNS adblock + DHCP, forwards to unbound + ../../services/pihole.nix # DNS adblock + DHCP (declarative static leases) ]; networking.hostName = "mercury"; diff --git a/services/adguardhome.nix b/services/adguardhome.nix deleted file mode 100644 index e3f9db6..0000000 --- a/services/adguardhome.nix +++ /dev/null @@ -1,67 +0,0 @@ -{ ... }: - -# AdGuard Home — network DNS (adblock) + DHCP. -# Forwards to the local unbound recursive resolver (services/unbound.nix). -# -# Password note: the AdGuard NixOS module has NO secret/passwordFile hook, and -# its config must be writable at runtime — so a sops secret can't be injected -# cleanly. Instead `mutableSettings = true`: the `settings` below are merged -# into AdGuard's state on start, and the ADMIN PASSWORD is set once via the web -# setup wizard on first boot (stored only in the Pi's runtime state — never in -# the repo or nix store). Trade-off: UI edits to other fields also persist. -# DHCP static leases likewise live in AdGuard's leases.json (add via UI). -{ - services.adguardhome = { - enable = true; - openFirewall = true; # opens the web + DNS ports - mutableSettings = true; # merge settings; keep UI-set password + leases - # allowDHCP is implied by settings.dhcp.enabled (grants NET_RAW/NET_BIND). - - settings = { - http.address = "0.0.0.0:3000"; # web UI; set the admin password here on first boot - - dns = { - bind_hosts = [ "0.0.0.0" ]; - port = 53; - # Recursive resolution via local unbound — no public upstream. - upstream_dns = [ "127.0.0.1:5335" ]; - bootstrap_dns = [ "1.1.1.1" "9.9.9.9" ]; - upstream_mode = "load_balance"; - }; - - filtering.filtering_enabled = true; - filters = [ - { - enabled = true; - id = 1; - name = "AdGuard DNS filter"; - url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt"; - } - { - enabled = true; - id = 2; - name = "AdAway Default Blocklist"; - url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt"; - } - ]; - - # ---- DHCP (replaces pihole's) ---- - dhcp = { - enabled = true; - interface_name = "eth0"; - local_domain_name = "sol"; # clients resolve as .sol - dhcpv4 = { - gateway_ip = "10.0.0.1"; - subnet_mask = "255.255.255.0"; - range_start = "10.0.0.50"; - range_end = "10.0.0.200"; - lease_duration = 86400; - }; - }; - # Static lease: add jupiter (10.0.0.20) via the UI once — persists in - # leases.json. Needs jupiter's eth MAC (`ip link` on jupiter). - }; - }; - - networking.firewall.allowedUDPPorts = [ 67 ]; # DHCP (DNS/web via openFirewall) -} diff --git a/services/pihole.nix b/services/pihole.nix new file mode 100644 index 0000000..a0b547f --- /dev/null +++ b/services/pihole.nix @@ -0,0 +1,46 @@ +{ ... }: + +# Pi-hole (pihole-FTL v6) — network DNS (adblock) + DHCP, with the embedded +# web UI. Forwards to the local unbound recursive resolver (services/unbound.nix). +# +# `settings` is the raw pihole.toml. Static DHCP leases ARE declarative here +# (settings.dhcp.hosts, dnsmasq "MAC,IP,hostname" format) — the reason we chose +# pihole over AdGuard. +# +# ⚠️ The pihole-ftl NixOS module + pihole.toml v6 schema are new; VALIDATE this +# at runtime (VM or the Pi) before trusting it — eval only checks the module, +# not the freeform TOML keys. +{ + services.pihole-ftl = { + enable = true; + openFirewallDNS = true; + openFirewallDHCP = true; + openFirewallWebserver = true; + + lists = [ + { url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"; type = "block"; } + ]; + + settings = { + dns = { + upstreams = [ "127.0.0.1#5335" ]; # local unbound (recursive) + domain = "sol"; # local domain -> jupiter.sol etc. + }; + + dhcp = { + active = true; + start = "10.0.0.50"; + end = "10.0.0.200"; + router = "10.0.0.1"; + leaseTime = "1h"; + # Static leases (declarative). Format: "MAC,IP,hostname". + hosts = [ "00:e0:4c:3c:a3:1f,10.0.0.20,jupiter" ]; + }; + + # Embedded web UI on :80. Set the admin password once after first boot: + # sudo pihole setpassword (kept out of the repo) + # (plain "80" so the module's openFirewall port parser is happy.) + webserver.port = "80"; + }; + }; +}