Internal DNS (unbound authoritative resolver)
Operational guide for the fzymgc.house internal DNS resolver chain running on the Firewalla router.
Architecture
Section titled “Architecture”Client DNS for fzymgc.house follows a fixed chain, not a flat forward:
Client -> dnsmasq (192.168.20.1:53, interceptor only) -> unbound (127.0.0.1:8953, authoritative for .house) -> Control D over DoT (everything else)- unbound is authoritative for every
.housename across all RR types. Any type not explicitly listed for a name (including type 65/HTTPS) gets NODATA — this closes the split-horizon ECH leak that previously let clients see a public HTTPS record for an internal-only name. - dnsmasq is a pure interceptor in front of unbound: it forwards
.housequeries to unbound and carries no zone records of its own. - Everything that isn’t
.houseis forwarded by unbound to Control D over DoT.
Full design and rationale: docs/engineering/specs/2026-07-07-authoritative-house-resolver-design.md.
unbound operational commands
Section titled “unbound operational commands”Status and reload
Section titled “Status and reload”unbound-control needs LD_LIBRARY_PATH pointed at the unbound run directory so it can find libhiredis.so:
LD_LIBRARY_PATH=/home/pi/.firewalla/run/unbound /home/pi/.firewalla/run/unbound/unbound-control \ -c /home/pi/.firewalla/run/unbound/unbound.conf status
LD_LIBRARY_PATH=/home/pi/.firewalla/run/unbound /home/pi/.firewalla/run/unbound/unbound-control \ -c /home/pi/.firewalla/run/unbound/unbound.conf reloadNo unbound-checkconf on the box
Section titled “No unbound-checkconf on the box”There is no unbound-checkconf shipped on the Firewalla. Do not assume config changes are valid without a test run. Validate any config change by starting a throwaway unbound instance on port 18953 before touching the live one, and only apply the change once the throwaway instance loads cleanly.
Zone regeneration model
Section titled “Zone regeneration model”router-hosts(the Firewalla-side gRPC daemon managed fromtf/router-hosts) regenerates the unbound zone file (unbound_local/10-fzymgc-authoritative.conf) both on daemon startup and on every host-data change — the zone is generated from inventory, never hand-edited.- A systemd path unit,
router-hosts-unbound-reload.path, watches theunbound_local/directory and firesunbound-control reloadon any change under it, including an atomic rename (temp file + rename, not an in-place edit). - Net effect: editing the
tf/router-hostsinventory and merging tomainis sufficient to update the live zone — no manual reload step is required.
Client-side verification runbook
Section titled “Client-side verification runbook”MUST run this from a real client on the LAN, not from the Firewalla box itself — the box does not take the same dnsmasq → unbound path a client does.
for n in id mcp-gw grafana vault k8s-cluster; do echo -n "$n: "; dig $n.fzymgc.house A +short; donedig id.fzymgc.house TYPE65 +short # want: empty (NODATA)curl -sS -o/dev/null -w"%{http_code} %{remote_ip}\n" https://mcp-gw.fzymgc.house/mcp/engram # want: 401 via 192.168.20.155Manual Firewalla Custom-DNS cleanup (post-rollout)
Section titled “Manual Firewalla Custom-DNS cleanup (post-rollout)”router-hosts/CLI cannot touch Firewalla-native Custom-DNS entries (precedent: hl-uegs). After the tf/router-hosts inventory import is applied and the zone is verified, manually delete these entries in the Firewalla app UI:
nas-ingress.fzymgc.houserouter.fzymgc.house
After deleting them, re-verify both names still resolve correctly via the generated zone (using the client-side runbook above).