mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-24 14:08:27 +02:00
feat(cli): route Tailscale Split DNS and App Connector domains in macOS intercept mode
When a discovered VPN resolver is Tailscale's quad-100, read the Split DNS and App Connector routes from Tailscale's LocalAPI and route those suffixes to quad-100 instead of Control D. Master adaptation: newVPNDNSManager keeps master's logger argument and Refresh keeps its context argument. tailscaleRoutesFor takes the refresh context, and a retry builds its context from the manager's logger (backgroundCtx). setDNS closes the previous manager and wires the gated retry the same way as on v1.0.
This commit is contained in:
1 parent
1dd94eb02e
commit
4792b0eeab
9 files changed
+1157
-5
No files matched your search
@@ -292,6 +292,11 @@ dig @127.0.0.1 example.com
|
||||
|
||||
- **Split DNS for VPN internal domains**: In `--intercept-mode dns` mode, VPN search domains are auto-detected from virtual network adapters and forwarded to the VPN's DNS servers automatically. In `--intercept-mode hard` mode, VPN internal domains (e.g., `*.corp.local`) will NOT resolve unless configured as explicit upstream rules in ctrld's configuration.
|
||||
|
||||
- **Tailscale Split DNS and App Connectors (macOS)**: The sandboxed Tailscale app keeps Split DNS routes with custom resolvers and App Connector domains inside its own resolver (`100.100.100.100`); they do not appear in `scutil --dns`. When a discovered resolver is Tailscale's, ctrld also reads Tailscale's route list from its LocalAPI (`/localapi/v0/dns-config`, Tailscale v1.98.0 or later) and routes those suffixes to `100.100.100.100`. The list is read on each VPN DNS refresh; after changing Split DNS or App Connectors, reconnect Tailscale to apply them. Names routed this way are resolved by the tailnet and not filtered by Control D, as in standard mode. If the LocalAPI is unavailable, only `scutil --dns` domains are used.
|
||||
- **Loop guard**: a route is taken only when quad-100 can reach its resolvers without crossing pf's port-53 redirect. DoH and App Connector (peerAPI) resolvers always qualify. A plain port-53 resolver qualifies only on a Tailscale IPv4 address and only in non-exit mode, where the VPN DNS interface passthrough exists (section 9). Routes with any other resolver (public, LAN, IPv6, or any port-53 resolver in exit-node mode) keep going to Control D; routing them to quad-100 would loop back to ctrld.
|
||||
- **Endpoint trust**: root-owned credentials are tried first (Standalone `/Library/Tailscale`, then `/var/run/tailscaled.socket`). The App Store extension is used last, and only when the process holding the `sameuserproof` file is named exactly `IPNExtension`, the file sits in a user's `Library/Group Containers/*.tailscale.ipn.macos/`, and the running process passes a Tailscale code-signature check.
|
||||
- **Failed reads**: a failed LocalAPI read (for example a 503 while Tailscale starts) keeps the last good route set while quad-100 is still discovered, and retries after 5 s, 15 s and 60 s, then stops until the next network change.
|
||||
|
||||
- **macOS mDNSResponder interaction**: On macOS, ctrld uses a workaround ("mDNSResponder hack") that binds to `0.0.0.0:53` instead of `127.0.0.1:53` and refuses queries from non-localhost sources. In dns-intercept mode, pf's `rdr` rewrites the destination IP to `127.0.0.1:53` but preserves the original source IP (e.g., `192.168.2.73`). The mDNSResponder source-IP check is automatically bypassed in dns-intercept mode because the pf/WFP rules already ensure only legitimate intercepted DNS traffic reaches ctrld's listener.
|
||||
|
||||
- **Other WFP/pf users**: If other software (VPN, firewall, endpoint security) also uses WFP or pf for DNS interception, there may be priority conflicts. ctrld uses maximum sublayer weight on Windows and a named anchor on macOS to minimize this risk. See "VPN App Coexistence" below for macOS-specific defenses.
|
||||
|
||||
Reference in new issue
Block a user