headscale: move the tailnet to orbit.sol, route all DNS through pihole
Three connected changes, all triggered by the same outage. base_domain leaves mgaction.town. That zone has a wildcard A+AAAA pointing at neptun, and DNS wildcards match multi-label names, so jupiter.hosts.mgaction.town resolved publicly to NEPTUN and Caddy proxied to itself -- a silent loop rather than a lookup failure. Nesting the tailnet inside the LAN domain as orbit.sol keeps the theme and resolves unambiguously, since tailscale matches routes by longest suffix. override_local_dns = true with pihole as the only global nameserver, so roaming devices get ad blocking and .sol names off-LAN. With it false, globalResolvers land in the netmap's FallbackResolvers, which a phone with carrier DNS never consults. No public fallback is listed on purpose: tailscale treats the list as a set, so a second entry would let queries slip past the filter whenever mercury is slow. The cost is that mercury is now a single point of failure for tailnet DNS. neptun and mercury opt out individually. mercury would otherwise resolve through itself. neptun must not depend on a Pi behind a domestic line to renew the certificates for the control server every other node needs -- and it is circular besides, since tailscaled has to resolve vpn.mgaction.town to connect at all. Instead neptun runs a dnsmasq stub forwarding just orbit.sol to MagicDNS on 100.100.100.100, which tailscaled answers whenever it is running regardless of --accept-dns. That resolves jupiter live, so the hardcoded /etc/hosts pin is gone. Also sets dns.nameservers.split explicitly: nixpkgs renders its own dns.split option one level too high, but headscale reads dns.nameservers.split (hscontrol/types/config.go:722) and so does headplane, whose DNS page dies on the missing key with "Cannot convert undefined or null to object". The module's option is dead as written. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -226,11 +226,24 @@ another way in.
|
||||
- mercury's own `resolv.conf` is deliberately public resolvers, not its own
|
||||
pihole (`resolveLocalQueries = false`, see `CLAUDE.md`) — so `.sol` names do
|
||||
not resolve *on mercury itself*. That is expected, not a fault.
|
||||
- LAN `.sol` names resolve on tailnet members only because headscale sets
|
||||
`override_local_dns = false`. With upstream's default of `true`, every node's
|
||||
`resolv.conf` is replaced with MagicDNS and `.sol` returns NXDOMAIN
|
||||
everywhere — along with losing ad blocking, since queries stop reaching
|
||||
pihole at all.
|
||||
- **mercury is load-bearing for the whole tailnet's DNS.** headscale sets
|
||||
`override_local_dns = true` with pihole as the only global nameserver, so
|
||||
every node — including a phone on mobile data — resolves through it and gets
|
||||
ad blocking and `.sol` names anywhere. The flip side is that mercury (or the
|
||||
home connection) going down costs name resolution on every device, not just
|
||||
`.sol`. Recovery on a stranded device is turning Tailscale off.
|
||||
There is deliberately no public fallback in `nameservers.global`: tailscale
|
||||
treats that list as a set, so a second entry would let queries slip past the
|
||||
filter whenever mercury is slow.
|
||||
neptun and mercury opt out with `--accept-dns=false` — mercury because it
|
||||
would otherwise resolve through itself, neptun because a public reverse
|
||||
proxy must not depend on a Pi at home to renew its certificates.
|
||||
- Tailnet names are `*.orbit.sol`, LAN names are `*.sol`. Both work everywhere
|
||||
on the tailnet because tailscale matches DNS routes by **longest suffix**, so
|
||||
`orbit.sol` reaches MagicDNS even though everything else goes to pihole.
|
||||
Never name a LAN host `orbit`: pihole's `address=/<host>.sol/<ip>` lines match
|
||||
a name *and everything beneath it*, which would swallow the entire tailnet
|
||||
zone.
|
||||
|
||||
## Adding a service
|
||||
|
||||
|
||||
Reference in New Issue
Block a user