Skip to content

Split-DNS answers that contain a tailnet node's Tailscale IP are unreachable #17

Open

Description

@woodjme

Problem

A custom domain whose DNS record points at a tailnet node's Tailscale IP resolves, but can't be reached through tailmix.
The node's MagicDNS name works fine.

This is a common homelab setup: a public A record such as app.example.com -> 100.101.102.103 (the node's real tailnet IP), usually so the service can get a normal TLS certificate.
The tailnet has a split-DNS entry for example.com.

With tailscaled this works because the whole CGNAT range is routed into the tunnel.
With tailmix:

  • dig @100.127.0.53 app.example.com returns 100.101.102.103
  • dig @100.127.0.53 node.tailnet.ts.net returns the effective IP, e.g. 100.127.0.180
  • connecting to app.example.com times out, because 100.101.102.103 has no tailmix route and leaves via the physical interface
  • tailmix routes bind -p home 100.101.102.103/32 is rejected with route ... overlaps a tailmix reserved range

So there is no supported way to reach the name, short of pinning it in /etc/hosts, which breaks if the effective IP changes.

Cause

dns/forwarder.go returns split-DNS responses unchanged, while only MagicDNS records get effective IPs (tunDNSLiveConfig).
docs/design.md says split-DNS answers follow ordinary host IP routing and need a separate IP route policy.
But routeOverlapsReserved forbids such a policy for any CGNAT or Tailscale ULA address, so for these answers that route can never exist.

Suggested fix

Each DNS route's forwarder is already tied to one profile, and a canonical address is unambiguous within one profile.
The forwarder could rewrite A/AAAA records whose address is a canonical self, peer or Service IP of that profile to the matching host NAT or effective IP.
That is the same translation MagicDNS answers already get.

#14 implements essentially this.
I built the same change independently on v0.1.20 and tested it on macOS; it fixes the case above with no effect on other profiles.
It's on my fork in case it's useful as a reference: woodjme@f92a270 (branch dns-translate-split-answers).

Two details that may be worth adopting either way:

  • cap the TTL of rewritten records at the MagicDNS TTL (600s), so caches don't outlive the effective IP's lease
  • clear the AD bit on rewritten responses, since upstream DNSSEC validation no longer covers them

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions