Skip to content

Use per-name dnsmasq local=+address= records for the authoritative internal fzymgc.house zone

Date: 2026-06-19 Status: Superseded by hl-fyy5 Decision: hl-qra1 Deciders: Sean Brandt

Internal clients resolving *.fzymgc.house received correct internal A records but leaked public Cloudflare AAAA records: the Firewalla’s dnsmasq overlay used hostsdir/expand-hosts/domain= with no authority directive, so any unoverridden record type forwarded to the public upstream (Control D → Cloudflare). Concrete failure: kubectl oidc-login (Go HTTP client) hung at OIDC discovery against auth.fzymgc.house on the leaked Cloudflare AAAA (2606:4700::/32); kubernetes-mcp-server stuck for the same reason.

ADR hl-2t4o chose a zone-wide local=/fzymgc.house/ authority directive. In practice that was wrong on this pipeline (attempted as hl-zh3w.2, rolled back): on the Firewalla’s multi-instance dnsmasq + per-device DNAT + ctrld/Control D/unbound/DoH fan-out, zone-wide local=/domain/ both failed to reliably suppress the forward for client queries and NXDOMAINed the entire zone on resolver paths lacking the hostsdir data — notably the Tailscale MagicDNS path went NXDOMAIN for all of fzymgc.house. Separately, upstream router-hosts shipped a purpose-built feature for this (rh#325/#326, v0.10.4).

Make dnsmasq authoritative per-name, not zone-wide. The router-hosts daemon (>=0.10.4), via its dnsmasq_conf_path output loaded by Firewalla dnsmasq from dnsmasq_local, emits for each managed name/alias a pair:

local=/<fqdn>/
address=/<fqdn>/<ip>

address= answers A from the managed IP; the companion per-name local=/<fqdn>/ makes dnsmasq authoritative for that exact name, so the other record type (AAAA) returns NODATA instead of forwarding. This is daemon-native (replaces the interim hand-built 10-fzymgc-address.conf snapshot) and per-name only — it does not make dnsmasq authoritative for the whole zone, so unmanaged names resolve normally. Shipped in selfhosted-cluster #1338. auth.fzymgc.house stays publicly reachable via Cloudflare Tunnel; only the internal view changes.

  • Surgical / per-name: suppresses the leak only for router-hosts-managed names; no zone-wide NXDOMAIN side effects (the failure mode of hl-2t4o).
  • Version-robust: the local=+address= pair returns NODATA on both pre-2.86 dnsmasq (where address= alone suffices) and 2.86+ (where address= alone forwards the non-matching type and the per-name local= restores authority) — the pre-2.86-vs-2.86 nuance hl-2t4o missed.
  • Daemon-native, single source of truth: emitted by router-hosts from its host inventory; versioned; no hand-built snapshot to drift.
  • Upstream-supported: router-hosts dnsmasq_conf_path (rh#325/#326); adopted in #1338.
  • Domain-agnostic: covers all router-hosts names regardless of domain, so it also fixes the latent fzymgc.net leak (hl-dhmy).
  • Zone-wide local=/fzymgc.house/ (hl-2t4o, now superseded): one line, but makes dnsmasq authoritative for the entire zone → NXDOMAINs unmanaged names and, on this multi-instance/forwarding pipeline, both failed to suppress the client-query forward and NXDOMAINed the Tailscale MagicDNS path zone-wide. Rolled back.
  • Per-name address= only, no local= (rejected): NODATA for AAAA on pre-2.86, but on dnsmasq >=2.86 address= alone forwards the non-matching type and the leak returns; the companion local=/name/ is what makes it version-robust.
  • --filter-AAAA (rejected, carried from hl-2t4o): global per-instance; kills all IPv6 incl. legitimate external answers.
  • Full --auth-zone SOA mode (rejected, carried from hl-2t4o): heavier SOA/zone-transfer management; no consumers.
  • Hand-built address= snapshot (interim only): worked during the incident but is static and drifts; replaced by the daemon-native output.
  • Positive: A-only internal names return NODATA for AAAA (no Cloudflare leak) per-name; Go oidc-login/kubectl no longer stall; unmanaged fzymgc.house names resolve normally (no zone-wide NXDOMAIN); the Tailscale MagicDNS path is unaffected; daemon-native (no snapshot drift); the same mechanism covers fzymgc.net (hl-dhmy).
  • Negative: requires router-hosts >=0.10.4 + the dnsmasq_conf_path config + the dnsmasq_local bind-mount (#1338); a newly-added router-hosts name gets its address= record applied on the next dnsmasq reload (the in-container on_success hook cannot reload the host’s firerouter_dns — a known follow-up; existing names unaffected).
  • Neutral: auth.fzymgc.house remains publicly reachable via Cloudflare Tunnel — only the internal view changes (unchanged from hl-2t4o).
  • Supersedes: hl-2t4o