From b0fbd85592c87c9d1a19b4961bd55355b1f28bd8 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 7 Apr 2024 14:25:11 -0700 Subject: [PATCH] net/tsdial: partially fix "tailscale nc" (UserDial) on macOS At least in the case of dialing a Tailscale IP. Updates #4529 Change-Id: I9fd667d088a14aec4a56e23aabc2b1ffddafa3fe Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/cli/ssh.go | 6 ++---- net/tsdial/tsdial.go | 18 ++++++++++++++---- version/prop.go | 9 +++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/cmd/tailscale/cli/ssh.go b/cmd/tailscale/cli/ssh.go index 229ac85b1..46dfb689f 100644 --- a/cmd/tailscale/cli/ssh.go +++ b/cmd/tailscale/cli/ssh.go @@ -106,10 +106,8 @@ func runSSH(ctx context.Context, args []string) error { "-o", "CanonicalizeHostname no", // https://github.com/tailscale/tailscale/issues/10348 ) - // TODO(bradfitz): nc is currently broken on macOS: - // https://github.com/tailscale/tailscale/issues/4529 - // So don't use it for now. MagicDNS is usually working on macOS anyway - // and they're not in userspace mode, so 'nc' isn't very useful. + // MagicDNS is usually working on macOS anyway and they're not in userspace + // mode, so 'nc' isn't very useful. if runtime.GOOS != "darwin" { socketArg := "" if rootArgs.socket != "" && rootArgs.socket != paths.DefaultTailscaledSocket() { diff --git a/net/tsdial/tsdial.go b/net/tsdial/tsdial.go index 1ae644b3c..85f763997 100644 --- a/net/tsdial/tsdial.go +++ b/net/tsdial/tsdial.go @@ -21,10 +21,12 @@ import ( "tailscale.com/net/netknob" "tailscale.com/net/netmon" "tailscale.com/net/netns" + "tailscale.com/net/tsaddr" "tailscale.com/types/logger" "tailscale.com/types/netmap" "tailscale.com/util/clientmetric" "tailscale.com/util/mak" + "tailscale.com/version" ) // Dialer dials out of tailscaled, while taking care of details while @@ -337,6 +339,14 @@ func (d *Dialer) UserDial(ctx context.Context, network, addr string) (net.Conn, } return d.NetstackDialTCP(ctx, ipp) } + // Workaround for macOS for now: dial Tailscale IPs with peer dialer. + // TODO(bradfitz): fix dialing subnet routers, public IPs via exit nodes, + // etc. This is a temporary partial for macOS. We need to plumb ART tables & + // prefs & host routing table updates around in more places. We just don't + // know from the limited context here how to dial properly. + if version.IsMacGUIVariant() && tsaddr.IsTailscaleIP(ipp.Addr()) { + return d.getPeerDialer().DialContext(ctx, network, ipp.String()) + } // TODO(bradfitz): netns, etc var stdDialer net.Dialer return stdDialer.DialContext(ctx, network, ipp.String()) @@ -365,14 +375,14 @@ func (d *Dialer) dialPeerAPI(ctx context.Context, network, addr string) (net.Con return d.getPeerDialer().DialContext(ctx, network, addr) } -// getPeerDialer returns the *net.Dialer to use to dial peers to use -// PeerAPI. +// getPeerDialer returns the *net.Dialer to use to dial peers (e.g. for peerapi, +// or "tailscale nc") // // This is not used in netstack mode. // // The primary function of this is to work on macOS & iOS's in the -// Network/System Extension so it can mark the dialer as staying -// within the network namespace/sandbox. +// Network/System Extension so it can mark the dialer as staying within the +// network namespace/sandbox. func (d *Dialer) getPeerDialer() *net.Dialer { d.peerDialerOnce.Do(func() { d.peerDialer = &net.Dialer{ diff --git a/version/prop.go b/version/prop.go index 3156b3605..b09bee7bb 100644 --- a/version/prop.go +++ b/version/prop.go @@ -40,6 +40,15 @@ func OS() string { return runtime.GOOS } +// IsMacGUIVariant reports whether runtime.GOOS=="darwin" and this one of the +// two GUI variants (that is, not tailscaled-on-macOS). +// This predicate should not be used to determine sandboxing properties. It's +// meant for callers to determine whether the NetworkExtension-like auto-netns +// is in effect. +func IsMacGUIVariant() bool { + return IsMacAppStore() || IsMacSysExt() +} + // IsSandboxedMacOS reports whether this process is a sandboxed macOS // process (either the app or the extension). It is true for the Mac App Store // and macsys (System Extension) version on macOS, and false for