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
Context
Section titled “Context”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).
Decision
Section titled “Decision”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.
Rationale
Section titled “Rationale”- 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 (whereaddress=alone suffices) and 2.86+ (whereaddress=alone forwards the non-matching type and the per-namelocal=restores authority) — the pre-2.86-vs-2.86 nuancehl-2t4omissed. - Daemon-native, single source of truth: emitted by
router-hostsfrom its host inventory; versioned; no hand-built snapshot to drift. - Upstream-supported:
router-hostsdnsmasq_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.netleak (hl-dhmy).
Alternatives Considered
Section titled “Alternatives Considered”- 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, nolocal=(rejected): NODATA forAAAAon pre-2.86, but on dnsmasq >=2.86address=alone forwards the non-matching type and the leak returns; the companionlocal=/name/is what makes it version-robust. --filter-AAAA(rejected, carried fromhl-2t4o): global per-instance; kills all IPv6 incl. legitimate external answers.- Full
--auth-zoneSOA mode (rejected, carried fromhl-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.
Consequences
Section titled “Consequences”- Positive: A-only internal names return NODATA for
AAAA(no Cloudflare leak) per-name; Gooidc-login/kubectlno longer stall; unmanagedfzymgc.housenames resolve normally (no zone-wide NXDOMAIN); the Tailscale MagicDNS path is unaffected; daemon-native (no snapshot drift); the same mechanism coversfzymgc.net(hl-dhmy). - Negative: requires
router-hosts>=0.10.4 + thednsmasq_conf_pathconfig + thednsmasq_localbind-mount (#1338); a newly-added router-hosts name gets itsaddress=record applied on the next dnsmasq reload (the in-containeron_successhook cannot reload the host’sfirerouter_dns— a known follow-up; existing names unaffected). - Neutral:
auth.fzymgc.houseremains publicly reachable via Cloudflare Tunnel — only the internal view changes (unchanged fromhl-2t4o).
References
Section titled “References”- Supersedes: hl-2t4o