From 4ffa6d72b27a36d680f198c2fdfc3fcf64e17436 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Thu, 17 Jul 2025 18:00:46 +1000 Subject: [PATCH 1/2] feat: support OS DNS configurator & Router on darwin: --- net/dns/manager_darwin.go | 4 ++-- net/tsaddr/tsaddr.go | 32 ++++++++++++-------------------- wgengine/magicsock/endpoint.go | 3 ++- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/net/dns/manager_darwin.go b/net/dns/manager_darwin.go index 2571cf8de919d..9b7e2523623fe 100644 --- a/net/dns/manager_darwin.go +++ b/net/dns/manager_darwin.go @@ -55,7 +55,7 @@ func (c *darwinConfigurator) SetDNS(cfg OSConfig) error { // Add a dummy file to /etc/resolver with a "search ..." directive if we have // search suffixes to add. if len(cfg.SearchDomains) > 0 { - const searchFile = "search.tailscale" // fake DNS suffix+TLD to put our search + const searchFile = "search.coder" // fake DNS suffix+TLD to put our search mak.Set(&keep, searchFile, true) var sbuf bytes.Buffer sbuf.WriteString(macResolverFileHeader) @@ -86,7 +86,7 @@ func (c *darwinConfigurator) GetBaseConfig() (OSConfig, error) { return OSConfig{}, ErrGetBaseConfigNotSupported } -const macResolverFileHeader = "# Added by tailscaled\n" +const macResolverFileHeader = "# Added by Coder Desktop\n" // removeResolverFiles deletes all files in /etc/resolver for which the shouldDelete // func returns true. diff --git a/net/tsaddr/tsaddr.go b/net/tsaddr/tsaddr.go index d35fce09994c3..df98fadc66740 100644 --- a/net/tsaddr/tsaddr.go +++ b/net/tsaddr/tsaddr.go @@ -73,8 +73,10 @@ const ( CoderServiceIPv6String = "fd60:627a:a42b::53" ) +// These are all unfortunately Coder IP ranges, not Tailscale IP ranges. + // IsTailscaleIP reports whether ip is an IP address in a range that -// Tailscale assigns from. +// Coder assigns from. func IsTailscaleIP(ip netip.Addr) bool { if ip.Is4() { return CGNATRange().Contains(ip) && !ChromeOSVMRange().Contains(ip) @@ -83,38 +85,28 @@ func IsTailscaleIP(ip netip.Addr) bool { } // TailscaleULARange returns the IPv6 Unique Local Address range that -// is the superset range that Tailscale assigns out of. +// is the superset range that Coder assigns out of. func TailscaleULARange() netip.Prefix { - tsUlaRange.Do(func() { mustPrefix(&tsUlaRange.v, "fd7a:115c:a1e0::/48") }) + tsUlaRange.Do(func() { mustPrefix(&tsUlaRange.v, "fd60:627a:a42b::/48") }) return tsUlaRange.v } -// TailscaleViaRange returns the IPv6 Unique Local Address subset range -// TailscaleULARange that's used for IPv4 tunneling via IPv6. +// Unused by Coder func TailscaleViaRange() netip.Prefix { - // Mnemonic: "b1a" sounds like "via". - tsViaRange.Do(func() { mustPrefix(&tsViaRange.v, "fd7a:115c:a1e0:b1a::/64") }) + tsViaRange.Do(func() { mustPrefix(&tsViaRange.v, "fd60:627a:a42b::/128") }) return tsViaRange.v } -// Tailscale4To6Range returns the subset of TailscaleULARange used for -// auto-translated Tailscale ipv4 addresses. +// Unused by Coder func Tailscale4To6Range() netip.Prefix { - // This IP range has no significance, beyond being a subset of - // TailscaleULARange. The bits from /48 to /104 were picked at - // random. - ula4To6Range.Do(func() { mustPrefix(&ula4To6Range.v, "fd7a:115c:a1e0:ab12:4843:cd96:6200::/104") }) + // This needs to be a /104 prefix, so it can fit IPv4 addresses + ula4To6Range.Do(func() { mustPrefix(&ula4To6Range.v, "fd60:627a:a42b::/104") }) return ula4To6Range.v } -// TailscaleEphemeral6Range returns the subset of TailscaleULARange -// used for ephemeral IPv6-only Tailscale nodes. +// Unused by Coder func TailscaleEphemeral6Range() netip.Prefix { - // This IP range has no significance, beyond being a subset of - // TailscaleULARange. The bits from /48 to /64 were picked at - // random, with the only criterion being to not be the conflict - // with the Tailscale4To6Range above. - ulaEph6Range.Do(func() { mustPrefix(&ulaEph6Range.v, "fd7a:115c:a1e0:efe3::/64") }) + ulaEph6Range.Do(func() { mustPrefix(&ulaEph6Range.v, "fd60:627a:a42b::/128") }) return ulaEph6Range.v } diff --git a/wgengine/magicsock/endpoint.go b/wgengine/magicsock/endpoint.go index c967130d8109d..ce8dab8cabccd 100644 --- a/wgengine/magicsock/endpoint.go +++ b/wgengine/magicsock/endpoint.go @@ -24,6 +24,7 @@ import ( "tailscale.com/disco" "tailscale.com/ipn/ipnstate" "tailscale.com/net/stun" + "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" "tailscale.com/tstime/mono" "tailscale.com/types/key" @@ -1064,7 +1065,7 @@ func (de *endpoint) handleCallMeMaybe(m *disco.CallMeMaybe) { } var newEPs []netip.AddrPort for _, ep := range m.MyNumber { - if ep.Addr().Is6() && ep.Addr().IsLinkLocalUnicast() { + if (ep.Addr().Is6() && ep.Addr().IsLinkLocalUnicast()) || tsaddr.IsTailscaleIP(ep.Addr()) { // We send these out, but ignore them for now. // TODO: teach the ping code to ping on all interfaces // for these. From 3676969baeefb9e7c7a48e5d00ea39a2c7302599 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 21 Jul 2025 15:15:11 +1000 Subject: [PATCH 2/2] more --- net/interfaces/interfaces.go | 14 ++++++-------- net/netmon/netmon_darwin.go | 5 +++++ net/netns/netns_darwin.go | 20 ++++++++++---------- net/tsaddr/tsaddr.go | 3 --- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/net/interfaces/interfaces.go b/net/interfaces/interfaces.go index 7923853173d9d..e5372390bbf83 100644 --- a/net/interfaces/interfaces.go +++ b/net/interfaces/interfaces.go @@ -24,7 +24,7 @@ import ( // which HTTP proxy the system should use. var LoginEndpointForProxyDetermination = "https://controlplane.tailscale.com/" -// Tailscale returns the current machine's Tailscale interface, if any. +// Tailscale returns the current machine's Coder interface, if any. // If none is found, all zero values are returned. // A non-nil error is only returned on a problem listing the system interfaces. func Tailscale() ([]netip.Addr, *net.Interface, error) { @@ -58,12 +58,10 @@ func Tailscale() ([]netip.Addr, *net.Interface, error) { } // maybeTailscaleInterfaceName reports whether s is an interface -// name that might be used by Tailscale. +// name that might be used by Coder. func maybeTailscaleInterfaceName(s string) bool { - return s == "Tailscale" || - strings.HasPrefix(s, "wg") || - strings.HasPrefix(s, "ts") || - strings.HasPrefix(s, "tailscale") || + return s == "Coder" || + strings.HasPrefix(s, "coder") || strings.HasPrefix(s, "utun") } @@ -496,8 +494,8 @@ func isTailscaleInterface(name string, ips []netip.Prefix) bool { // macOS NetworkExtensions and utun devices. return true } - return name == "Tailscale" || // as it is on Windows - strings.HasPrefix(name, "tailscale") // TODO: use --tun flag value, etc; see TODO in method doc + return name == "Coder" || // as it is on Windows + strings.HasPrefix(name, "coder") // TODO: use --tun flag value, etc; see TODO in method doc } // getPAC, if non-nil, returns the current PAC file URL. diff --git a/net/netmon/netmon_darwin.go b/net/netmon/netmon_darwin.go index cc630112523fa..c7ce21ea8025d 100644 --- a/net/netmon/netmon_darwin.go +++ b/net/netmon/netmon_darwin.go @@ -137,6 +137,11 @@ func (m *darwinRouteMon) skipRouteMessage(msg *route.RouteMessage) bool { // dst = fe80::b476:66ff:fe30:c8f6%15 return true } + if msg.Type == unix.RTM_GET { + // Skip RTM_GET messages, which are used to query the routing table + // See nets_darwin.go + return true + } return false } diff --git a/net/netns/netns_darwin.go b/net/netns/netns_darwin.go index b32a744b7ee8e..6b4921c526bed 100644 --- a/net/netns/netns_darwin.go +++ b/net/netns/netns_darwin.go @@ -54,7 +54,14 @@ func controlLogf(logf logger.Logf, netMon *netmon.Monitor, network, address stri return nil } - return bindConnToInterface(c, network, address, idx, logf) + // Verify that we didn't just choose the Coder interface. + _, tsif, err2 := interfaces.Tailscale() + if err2 == nil && tsif != nil && tsif.Index == idx { + return fmt.Errorf("netns_darwin: refusing bind to Coder interface %q (index %d) for address %q", tsif.Name, tsif.Index, address) + } + + // Let the OS decide + return nil } func getInterfaceIndex(logf logger.Logf, netMon *netmon.Monitor, address string) (int, error) { @@ -98,7 +105,8 @@ func getInterfaceIndex(logf logger.Logf, netMon *netmon.Monitor, address string) // If the address doesn't parse, use the default index. addr, err := netip.ParseAddr(host) if err != nil { - logf("[unexpected] netns: error parsing address %q: %v", host, err) + // Log removed to prevent noise when the address is `:0` + // logf("[unexpected] netns: error parsing address %q: %v", host, err) return defaultIdx() } @@ -108,14 +116,6 @@ func getInterfaceIndex(logf logger.Logf, netMon *netmon.Monitor, address string) return defaultIdx() } - // Verify that we didn't just choose the Tailscale interface; - // if so, we fall back to binding from the default. - _, tsif, err2 := interfaces.Tailscale() - if err2 == nil && tsif != nil && tsif.Index == idx { - logf("[unexpected] netns: interfaceIndexFor returned Tailscale interface") - return defaultIdx() - } - return idx, err } diff --git a/net/tsaddr/tsaddr.go b/net/tsaddr/tsaddr.go index df98fadc66740..26b544368b64c 100644 --- a/net/tsaddr/tsaddr.go +++ b/net/tsaddr/tsaddr.go @@ -78,9 +78,6 @@ const ( // IsTailscaleIP reports whether ip is an IP address in a range that // Coder assigns from. func IsTailscaleIP(ip netip.Addr) bool { - if ip.Is4() { - return CGNATRange().Contains(ip) && !ChromeOSVMRange().Contains(ip) - } return TailscaleULARange().Contains(ip) }