ipn/ipnlocal, net/netns: add node cap to disable netns interface binding on netext Apple clients

updates tailscale/corp#31571

It appears that on the latest macOS, iOS and tVOS versions, the work
that netns is doing to bind outgoing connections to the default interface (and all
of the trimmings and workarounds in netmon et al that make that work) are
not needed. The kernel is extension-aware and doing nothing, is the right
thing.  This is, however, not the case for tailscaled (which is not a
special process).

To allow us to test this assertion (and where it might break things), we add a
new node cap that turns this behaviour off only for network-extension equipped clients,
making it possible to turn this off tailnet-wide, without breaking any tailscaled
macos nodes.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
Jonathan Nobels
2025-10-27 17:37:42 -04:00
parent edb11e0e60
commit c59cf24b51
4 changed files with 30 additions and 11 deletions

View File

@@ -6177,9 +6177,10 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
b.setDebugLogsByCapabilityLocked(nm)
}
// See the netns package for documentation on what this capability does.
netns.SetBindToInterfaceByRoute(nm.HasCap(tailcfg.CapabilityBindToInterfaceByRoute))
netns.SetDisableBindConnToInterface(nm.HasCap(tailcfg.CapabilityDebugDisableBindConnToInterface))
// See the netns package for documentation on what these capability do.
netns.SetBindToInterfaceByRoute(b.logf, nm.HasCap(tailcfg.CapabilityBindToInterfaceByRoute))
netns.SetDisableBindConnToInterface(b.logf, nm.HasCap(tailcfg.CapabilityDebugDisableBindConnToInterface))
netns.SetDisableBindConnToInterfaceAppleExt(b.logf, nm.HasCap(tailcfg.CapabilityDebugDisableBindConnToInterfaceAppleExt))
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked(b.pm.CurrentPrefs())
if buildfeatures.HasServe {

View File

@@ -39,20 +39,35 @@ var bindToInterfaceByRoute atomic.Bool
// setting the TS_BIND_TO_INTERFACE_BY_ROUTE.
//
// Currently, this only changes the behaviour on macOS and Windows.
func SetBindToInterfaceByRoute(v bool) {
func SetBindToInterfaceByRoute(logf logger.Logf, v bool) {
logf("netns: bindToInterfaceByRoute to %v", v)
bindToInterfaceByRoute.Store(v)
}
var disableBindConnToInterface atomic.Bool
// SetDisableBindConnToInterface disables the (normal) behavior of binding
// connections to the default network interface.
// connections to the default network interface on Darwin nodes.
//
// Currently, this only has an effect on Darwin.
func SetDisableBindConnToInterface(v bool) {
// Unless you intended to disable this for tailscaled on macos (which is likely
// to break things), you probably wanted to set
// SetDisableBindConnToInterfaceAppleExt which will disable explicit interface
// binding only when tailscaled is running inside a network extension process.
func SetDisableBindConnToInterface(logf logger.Logf, v bool) {
logf("netns: disableBindConnToInterface set to %v", v)
disableBindConnToInterface.Store(v)
}
var disableBindConnToInterfaceAppleExt atomic.Bool
// SetDisableBindConnToInterfaceAppleExt disables the (normal) behavior of binding
// connections to the default network interface but only on Apple clients where
// tailscaled is running inside a network extension.
func SetDisableBindConnToInterfaceAppleExt(logf logger.Logf, v bool) {
logf("netns: disableBindConnToInterfaceAppleExt set to %v", v)
disableBindConnToInterfaceAppleExt.Store(v)
}
// Listener returns a new net.Listener with its Control hook func
// initialized as necessary to run in logical network namespace that
// doesn't route back into Tailscale.

View File

@@ -21,6 +21,7 @@ import (
"tailscale.com/net/netmon"
"tailscale.com/net/tsaddr"
"tailscale.com/types/logger"
"tailscale.com/version"
)
func control(logf logger.Logf, netMon *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
@@ -36,13 +37,11 @@ var errInterfaceStateInvalid = errors.New("interface state invalid")
// controlLogf binds c to a particular interface as necessary to dial the
// provided (network, address).
func controlLogf(logf logger.Logf, netMon *netmon.Monitor, network, address string, c syscall.RawConn) error {
if isLocalhost(address) {
// Don't bind to an interface for localhost connections.
if disableBindConnToInterface.Load() || (version.IsMacGUIVariant() && disableBindConnToInterfaceAppleExt.Load()) {
return nil
}
if disableBindConnToInterface.Load() {
logf("netns_darwin: binding connection to interfaces disabled")
if isLocalhost(address) {
return nil
}

View File

@@ -2465,6 +2465,10 @@ const (
// of connections to the default network interface on Darwin nodes.
CapabilityDebugDisableBindConnToInterface NodeCapability = "https://tailscale.com/cap/debug-disable-bind-conn-to-interface"
// CapabilityDebugDisableBindConnToInterface disables the automatic binding
// of connections to the default network interface on Darwin nodes using network extensions
CapabilityDebugDisableBindConnToInterfaceAppleExt NodeCapability = "https://tailscale.com/cap/debug-disable-bind-conn-to-interface-apple-ext"
// CapabilityTailnetLock indicates the node may initialize tailnet lock.
CapabilityTailnetLock NodeCapability = "https://tailscale.com/cap/tailnet-lock"