On a Tailscale host, boxes inherit the tailnet's DNS — flaky resolution, and tailnet names resolve from inside a box #33
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:cli
scope:drill
scope:host
scope:installer
scope:templates
scope:tiers
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/box#33
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The drill host runs Tailscale. That turns out to interact with claudebox's network in two ways — one that makes boxes flaky, and one that quietly weakens the isolation the tool is for.
What was observed
A cold
claudebox newfailed withcloud-init status: error. The box could not resolve anything:Routing was fine — only name resolution was dead. And
drill/doctor.shconfirmed the host was otherwise clean (dns.modeunset, no NIC filtering, the six shipped ACL rules). Meanwhile the Incus journal shows its dnsmasq re-reading the host's resolver over and over, and flipping upstreams:Problem 1 — box DNS inherits a resolver that moves under it (reliability)
Incus's dnsmasq forwards box queries to whatever is in the host's
/etc/resolv.confat that moment. On a Tailscale host that file is not stable:tailscaledrewrites it to point at MagicDNS (100.100.100.100), and it flips back on network changes, restarts, and route updates.So a box's DNS depends on the host's tailnet state. If Tailscale is restarting, degraded, or its DNS is briefly not answering, every lookup inside every box fails — and a cold mint dies in cloud-init, several minutes in, with an error that points at Debian's mirrors rather than at the real cause.
Status: strongly consistent with the evidence, not yet proven by a repro. The alternative explanation is a transient upstream failure that happened to land mid-mint.
Problem 2 — the tailnet is reachable by NAME, even though it is blocked by ADDRESS (isolation)
This one is structural, and it does not need a repro.
host/setup-host.shdrops egress to100.64.0.0/10— the CGNAT range, which is exactly Tailscale's address space. So a box cannot connect to a tailnet peer. Good, and clearly deliberate.But the same setup opens a carve-out to the gateway (
allow 10.87.0.1/32) so that dnsmasq can serve DNS — and that dnsmasq forwards to Tailscale's MagicDNS. Which means a box can ask the host's resolver about the tailnet and get answers:100.xaddresses*.corp.example.com) becomes resolvable from inside a box that is supposed to see nothing but the public internetThe box cannot reach any of it. But "isolated" for this tool has always meant reaches the public internet and nothing else — and name-level reconnaissance of a private network is not nothing. It is the same shape as the sibling-box DNS enumeration in #16: connection blocked, enumeration wide open, and nothing in the design says otherwise.
An agent running inside a box is exactly the sort of thing that might go looking.
Proposed fix — give claudenet its own resolver, and stop inheriting the host's
Pin dnsmasq's upstream so it does not follow the host's resolver at all:
no-resolvmakes dnsmasq ignore/etc/resolv.confentirely; the explicitserver=lines give it a stable public upstream. That fixes both problems at once:Open questions for whoever implements it:
raw.dnsmasqthe right lever, or does the Incus bridge expose a first-classdns.nameservers-style key? Verify on a live Incus — do not take this issue's word for it. (This repo has already shipped one design decision based on an unverified assumption; seedrill/RUNS.md.)1.1.1.1picks a vendor. ABOX_DNS-style setting with a sane default is probably right.100.64.0.0/10) is detected on the host? Unconditional is simpler and strictly more predictable — a box's DNS should not be a function of the host's VPN posture, whatever that posture is.Scope
Not just Tailscale. Any host-level VPN or split-DNS resolver (Tailscale, ZeroTier, corporate VPN clients,
systemd-resolvedwith per-link DNS) will be inherited by boxes the same way. Tailscale is simply the one that surfaced it, because100.64.0.0/10is already in the ACL — the ACL author clearly thought about tailnet connectivity and, understandably, not about tailnet names.Provenance
Surfaced while running the drill for the #15 audit, on a Tailscale-connected host. Related: #16 (DNS enumeration of sibling boxes — same class, different target), #26 (the forensics run that captured the dnsmasq upstream flapping).