{ config, pkgs, lib, ... }: # mercury — Raspberry Pi 3B+ (aarch64). Network DNS + DHCP (+ adblock). # Boots from an SD image (see flake: nixosConfigurations.mercury), so there is # no disko / hardware-configuration here — the sd-image module provides them. { imports = [ ../../common.nix # shared base: user / ssh / nix / firewall ./secrets.nix # sops-nix: darman password (age key on boot part.) ../../services/network/unbound.nix # local recursive resolver (127.0.0.1:5335) ../../services/network/pihole.nix # DNS adblock + DHCP (declarative static leases) ../../services/vpn/tailscale.nix # tailnet node (headscale on neptun) ]; networking.hostName = "mercury"; # ---- Static networking ---- # A DNS/DHCP server must have a fixed address. Fill in the Pi's real values # (from `ip -brief a` / `ip route` on the running Pi). eth0 = the Pi's NIC. networking.useDHCP = false; networking.usePredictableInterfaceNames = false; # keep it named eth0 networking.interfaces.eth0.ipv4.addresses = [ { address = "10.0.0.10"; prefixLength = 24; } # the Pi's current IP ]; # Stable IPv6 (FRITZ!Box ULA prefix) so mercury is a fixed IPv6 DNS target. # SLAAC still provides the GUA + default route. Announce THIS address as the # DNSv6 server in the FRITZ!Box so IPv6 clients resolve .sol via pihole. networking.interfaces.eth0.ipv6.addresses = [ { address = "fd18:df17:9078:0::10"; prefixLength = 64; } ]; # VERIFY with `ip route` on the Pi — assuming the router is .1. networking.defaultGateway = { address = "10.0.0.1"; interface = "eth0"; }; # Resolvers for the Pi's OWN lookups (not the LAN service). Kept public so the # host resolves during boot without depending on its own pihole/unbound. networking.nameservers = [ "1.1.1.1" "9.9.9.9" ]; # Never take MagicDNS on THIS host. headscale pushes override_local_dns, so # accepting it would repoint mercury's resolv.conf at 100.100.100.100 and # make the LAN's DNS server depend on tailscaled to resolve anything — the # same boot-time DNS deadlock this host already hit once (see CLAUDE.md), # except a failure here takes the whole network's DNS down with it. services.tailscale.extraUpFlags = [ "--accept-dns=false" ]; # ---- pihole web admin password (from sops) ---- # The pihole container reads FTLCONF_* env vars. Render an env file from the # sops secret and feed it to the container — password stays out of repo/store. sops.secrets.pihole_webpassword = { }; sops.templates."pihole.env".content = "FTLCONF_webserver_api_password=${config.sops.placeholder.pihole_webpassword}"; virtualisation.oci-containers.containers.pihole.environmentFiles = [ config.sops.templates."pihole.env".path ]; # Do not modify after first flash. system.stateVersion = "26.05"; }