Skip to content

Internal DNS (unbound authoritative resolver)

Operational guide for the fzymgc.house internal DNS resolver chain running on the Firewalla router.

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 .house name 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 .house queries to unbound and carries no zone records of its own.
  • Everything that isn’t .house is forwarded by unbound to Control D over DoT.

Full design and rationale: docs/engineering/specs/2026-07-07-authoritative-house-resolver-design.md.

unbound-control needs LD_LIBRARY_PATH pointed at the unbound run directory so it can find libhiredis.so:

Terminal window
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 reload

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.

  • router-hosts (the Firewalla-side gRPC daemon managed from tf/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 the unbound_local/ directory and fires unbound-control reload on any change under it, including an atomic rename (temp file + rename, not an in-place edit).
  • Net effect: editing the tf/router-hosts inventory and merging to main is sufficient to update the live zone — no manual reload step is required.

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.

Terminal window
for n in id mcp-gw grafana vault k8s-cluster; do echo -n "$n: "; dig $n.fzymgc.house A +short; done
dig 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.155

Manual 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.house
  • router.fzymgc.house

After deleting them, re-verify both names still resolve correctly via the generated zone (using the client-side runbook above).