From a2c0758ba62f6b0e2bc54daacb6db2c8fc634a08 Mon Sep 17 00:00:00 2001 From: claude-hdb Date: Tue, 14 Jul 2026 12:15:45 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20pin=20claudenet's=20resolver=20=E2=80=94?= =?UTF-8?q?=20a=20box's=20DNS=20is=20not=20a=20function=20of=20the=20host'?= =?UTF-8?q?s=20VPN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bridge's dnsmasq forwarded to whatever sat in the host's /etc/resolv.conf at that moment. On a Tailscale host that is MagicDNS: box DNS flapped with the tailnet (killing cold mints), and tailnet peer names and split-DNS zones resolved from inside a box — name-level reconnaissance of a private network, the same class as the sibling enumeration dns.mode=none already closes. setup-host.sh now sets raw.dnsmasq to no-resolv + pinned public upstreams (BOX_DNS overrides the default 1.1.1.1 8.8.8.8), answering the issue's three open questions from live measurement: raw.dnsmasq is the lever (no first-class upstream key on the bridge; verified by doctor --pin-dns followed by a box resolving), upstreams are a setting with a sane default, and the pin is unconditional. The doctor's unpinned-state messages now point at setup-host.sh as the durable fix, keeping --pin-dns as the quick test. Closes #33 --- drill/doctor.sh | 5 +++-- host/setup-host.sh | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drill/doctor.sh b/drill/doctor.sh index ee20e23..0dfced1 100755 --- a/drill/doctor.sh +++ b/drill/doctor.sh @@ -182,9 +182,10 @@ else no "the host resolves via a CGNAT/Tailscale resolver ($hostns), and boxes INHERIT it — see issue #33" inf "· box DNS breaks whenever the tailnet's resolver does (this is what kills cold mints)" inf "· and tailnet names RESOLVE from inside a box, though its ACL blocks connecting to them" - inf "fix + test: bash drill/doctor.sh --pin-dns" + inf "fix: re-run ~/.local/share/claudebox/host/setup-host.sh (it pins the resolver)" + inf " or quick-test the pin alone: bash drill/doctor.sh --pin-dns" else - inf "boxes inherit the host's resolver (unpinned). Fine while it is stable; see issue #33." + inf "boxes inherit the host's resolver (unpinned — setup-host.sh pins this now; re-run it)" fi fi diff --git a/host/setup-host.sh b/host/setup-host.sh index 644d968..6cb8434 100755 --- a/host/setup-host.sh +++ b/host/setup-host.sh @@ -46,6 +46,20 @@ incus network set claudenet security.acls=claude-isolate \ # forwarding for public names is unaffected (verified live). incus network set claudenet dns.mode=none +# A box's resolver must not be a function of the host's VPN posture (#33). +# The bridge's dnsmasq forwards to whatever sits in the HOST's /etc/resolv.conf +# at that moment. On a Tailscale/VPN host that is MagicDNS: box DNS flaps with +# the tailnet (this is what killed cold mints), and tailnet peer names and +# split-DNS zones RESOLVE from inside a box — name-level reconnaissance of a +# private network, the same shape as the sibling enumeration closed above. +# no-resolv detaches dnsmasq from the host's resolver entirely; server= pins a +# stable public upstream (override: BOX_DNS="ip ip…"). raw.dnsmasq is the +# lever — the bridge has no first-class upstream key. Verified live on the +# drill host: pin applied, box resolves, cold mint survives. +BOX_DNS="${BOX_DNS:-1.1.1.1 8.8.8.8}" +incus network set claudenet raw.dnsmasq \ + "$(printf 'no-resolv\n'; for s in $BOX_DNS; do printf 'server=%s\n' "$s"; done)" + # Sibling isolation itself is NOT an ACL rule — an L3 ACL never sees frames # switched between two ports of one bridge. It lives in claudebox-firewall.sh # as an nftables bridge-family rule. See the comment there; it is the reason -- 2.45.2