Skip to content

Router-hosts-generated unbound zone replaces dnsmasq overlay

Date: 2026-07-07 Status: Accepted Decision: hl-fyy5 Deciders: Sean Brandt

The Firewalla’s per-name dnsmasq local=/address= overlay (ADR hl-qra1) only covers A/AAAA; pre-2.90 dnsmasq forwards unrecognized RR types (e.g. type-65 HTTPS/ECH) regardless of local=, causing a third recurrence of the split-horizon leak (AAAA: hl-zh3w/hl-qra1; HTTPS/ECH: hl-hqyv, which broke id.fzymgc.house in Chromium and blocked all mcp-gw OAuth). The zone was also hand-applied during the incident and had a dual-source inventory gap (k8s-cluster.fzymgc.house NXDOMAIN).

Make router-hosts’s native unbound_conf_path output (per-name local-zone "<name>." static + local-data) the single generator of the fzymgc.house zone from a single complete inventory (tf/router-hosts); a host-side systemd .path unit reloads unbound on change; the dnsmasq per-name overlay retires so dnsmasq becomes a pure interceptor forwarding to unbound.

  • Reuses the already-hardened Go generator (dnsmasqconf.go pattern, rh#326-328) instead of a from-scratch Ansible/Jinja re-implementation that cannot regenerate between deploys.
  • Decouples generation (container, cannot systemctl) from activation (host path unit, correct regardless of writer).
  • Per-name static is the only mode empirically verified to NODATA missing RR types without NXDOMAINing unmanaged siblings or the Tailscale MagicDNS path (the hl-2t4o regression).
  • One declarative inventory eliminates the dual-source drift that caused the k8s-cluster gap.
  • router-hosts-native unbound_conf_path + host systemd .path-unit reload (chosen): hardened generator, regenerates on startup and every host mutation, activation works regardless of who writes.
  • Ansible/Jinja zone generation in this repo (rejected): reimplements the existing 110-line Go generator (two point releases to harden startup/regen semantics) and cannot regenerate between deploys.
  • Zone-wide local-zone static / typetransparent (rejected): typetransparent passes missing types to recursion (leak not closed); zone-wide static NXDOMAINs unmanaged siblings including MagicDNS (hl-2t4o regression).
  • Keep dual dnsmasq + unbound authority (rejected): dual sources drift (caused the k8s-cluster NXDOMAIN gap); if unbound is ever bypassed the dnsmasq layer silently reopens the RR-type leak.
  • Positive: closes the recurring split-horizon RR-type leak class for all RR types; zone activates automatically on every inventory change; single source of truth for .house names.
  • Negative: new external dependency on router-hosts rh#349 (two-PR rollout gated on that release); new host-level systemd components on the Firewalla; if unbound is down, dnsmasq forwards .house upstream (unchanged platform risk, now visible instead of masked).
  • Neutral: dnsmasq remains the client-facing interceptor but carries no .house records of its own.