From 5a23638f75b0a95fc8775a8d7c5caeb61fcda50a Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 13 Jul 2026 20:13:01 +0200 Subject: [PATCH] feat(mercury): AdGuard Home DNS adblock + DHCP, forwards to unbound - services/adguardhome.nix: upstream unbound, .sol local domain, DHCP 50-200 - nix-owned config (mutableSettings=false); adlists + dns declared - mercury imports adguard + unbound; drop the pick-one placeholder block - TODO: bcrypt admin password, jupiter static lease via UI (needs its MAC) --- hosts/mercury/configuration.nix | 29 +++----------- services/adguardhome.nix | 68 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 services/adguardhome.nix diff --git a/hosts/mercury/configuration.nix b/hosts/mercury/configuration.nix index 4b9896f..8ad9c5b 100644 --- a/hosts/mercury/configuration.nix +++ b/hosts/mercury/configuration.nix @@ -6,8 +6,8 @@ { imports = [ ../../common.nix # shared base: user / ssh / nix / firewall - ../../services/unbound.nix # local recursive resolver (127.0.0.1:5335) - # ../../services/.nix # add once the adblock DNS engine is chosen (below) + ../../services/unbound.nix # local recursive resolver (127.0.0.1:5335) + ../../services/adguardhome.nix # DNS adblock + DHCP, forwards to unbound ]; networking.hostName = "mercury"; @@ -22,29 +22,12 @@ ]; # VERIFY with `ip route` on the Pi — assuming the router is .1. networking.defaultGateway = { address = "10.0.0.1"; interface = "eth0"; }; - # Upstream resolvers for the box itself (the adblock DNS forwards to these). + # Resolvers for the Pi's OWN lookups (not the LAN service). Kept public so the + # host resolves during boot without depending on its own AdGuard/unbound. networking.nameservers = [ "1.1.1.1" "9.9.9.9" ]; - # ---- DNS + adblock + DHCP — pick ONE, then open its ports below ---- - # - # Option A: pihole (native module) - # services.pihole-ftl = { - # enable = true; - # # dhcp, lists, upstreams, local records... - # }; - # - # Option B: AdGuard Home (native module, fully declarative) - # services.adguardhome = { - # enable = true; - # settings = { - # dns.upstream_dns = [ "1.1.1.1" "9.9.9.9" ]; - # # filters (adlists), rewrites (local DNS), dhcp static leases... - # }; - # }; - # - # Then open the ports this service needs: - # networking.firewall.allowedTCPPorts = [ 53 80 ]; # DNS + web UI - # networking.firewall.allowedUDPPorts = [ 53 67 ]; # DNS + DHCP + # DNS adblock + DHCP (AdGuard) and the recursive resolver (unbound) come from + # the imported service modules. AdGuard forwards to unbound at 127.0.0.1:5335. # Do not modify after first flash. system.stateVersion = "26.05"; diff --git a/services/adguardhome.nix b/services/adguardhome.nix new file mode 100644 index 0000000..8628a20 --- /dev/null +++ b/services/adguardhome.nix @@ -0,0 +1,68 @@ +{ ... }: + +# AdGuard Home — network DNS (adblock) + DHCP. +# Forwards to the local unbound recursive resolver (services/unbound.nix). +# Config is nix-owned (mutableSettings = false): the web UI can view but not +# persist changes — edit here and redeploy. EXCEPTION: DHCP static leases live +# in AdGuard's separate leases.json, so add those in the UI (they persist). +{ + services.adguardhome = { + enable = true; + openFirewall = true; # opens the web + DNS ports + mutableSettings = false; # AdGuardHome.yaml is authoritative from nix + # allowDHCP is implied by settings.dhcp.enabled (grants NET_RAW/NET_BIND). + + settings = { + # Web UI on :3000. Admin login — replace with YOUR bcrypt hash: + # nix run nixpkgs#apacheHttpd -- htpasswd -B -n -b admin 'yourpassword' + # (take the part after "admin:"). For repo hygiene, move this to sops later. + http.address = "0.0.0.0:3000"; + users = [ + { name = "admin"; password = "$2y$10$REPLACE-WITH-BCRYPT-HASH"; } + ]; + + 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) +}