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)
This commit is contained in:
erik
2026-07-13 20:13:01 +02:00
parent 587fcbfa3d
commit 5a23638f75
2 changed files with 74 additions and 23 deletions
+5 -22
View File
@@ -7,7 +7,7 @@
imports = [ imports = [
../../common.nix # shared base: user / ssh / nix / firewall ../../common.nix # shared base: user / ssh / nix / firewall
../../services/unbound.nix # local recursive resolver (127.0.0.1:5335) ../../services/unbound.nix # local recursive resolver (127.0.0.1:5335)
# ../../services/<dns>.nix # add once the adblock DNS engine is chosen (below) ../../services/adguardhome.nix # DNS adblock + DHCP, forwards to unbound
]; ];
networking.hostName = "mercury"; networking.hostName = "mercury";
@@ -22,29 +22,12 @@
]; ];
# VERIFY with `ip route` on the Pi — assuming the router is .1. # VERIFY with `ip route` on the Pi — assuming the router is .1.
networking.defaultGateway = { address = "10.0.0.1"; interface = "eth0"; }; 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" ]; networking.nameservers = [ "1.1.1.1" "9.9.9.9" ];
# ---- DNS + adblock + DHCP — pick ONE, then open its ports below ---- # 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.
# 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
# Do not modify after first flash. # Do not modify after first flash.
system.stateVersion = "26.05"; system.stateVersion = "26.05";
+68
View File
@@ -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 <host>.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)
}