From 4471e403aaee9981f4e7f091c7e29956a9aa17bf Mon Sep 17 00:00:00 2001 From: shayne Date: Fri, 20 Jan 2023 13:40:56 -0500 Subject: [PATCH] ipn/ipnlocal: [serve] listen on all-interfaces for macOS sandboxed (#6771) On macOS (AppStore and macsys), we need to bind to ""/all-interfaces due to the network sandbox. Ideally we would only bind to the Tailscale interface, but macOS errors out if we try to to listen on privileged ports binding only to a specific interface. We also implement the lc.Control hook, same as we do for peerapi. It doesn't solve our problem but it's better that we do and would likely be required when Apple gets around to fixing per-interface priviliged port binding. Fixes: #6364 Signed-off-by: Shayne Sweeney --- ipn/ipnlocal/serve.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/ipn/ipnlocal/serve.go b/ipn/ipnlocal/serve.go index 0e4f23464..c350efb27 100644 --- a/ipn/ipnlocal/serve.go +++ b/ipn/ipnlocal/serve.go @@ -33,6 +33,7 @@ import ( "tailscale.com/types/logger" "tailscale.com/util/mak" "tailscale.com/util/strs" + "tailscale.com/version" ) // serveHTTPContextKey is the context.Value key for a *serveHTTPContext. @@ -91,7 +92,38 @@ func (s *serveListener) Close() error { // Listen is retried until the context is canceled. func (s *serveListener) Run() { for { - ln, err := net.Listen("tcp", s.ap.String()) + ip := s.ap.Addr() + ipStr := ip.String() + + var lc net.ListenConfig + if initListenConfig != nil { + // On macOS, this sets the lc.Control hook to + // setsockopt the interface index to bind to. This is + // required by the network sandbox to allow binding to + // a specific interface. Without this hook, the system + // chooses a default interface to bind to. + if err := initListenConfig(&lc, ip, s.b.prevIfState, s.b.dialer.TUNName()); err != nil { + s.logf("serve failed to init listen config %v, backing off: %v", s.ap, err) + s.bo.BackOff(s.ctx, err) + continue + } + // On macOS (AppStore or macsys) and if we're binding to a privileged port, + if version.IsSandboxedMacOS() && s.ap.Port() < 1024 { + // On macOS, we need to bind to ""/all-interfaces due to + // the network sandbox. Ideally we would only bind to the + // Tailscale interface, but macOS errors out if we try to + // to listen on privileged ports binding only to a specific + // interface. (#6364) + ipStr = "" + } + } + + tcp4or6 := "tcp4" + if ip.Is6() { + tcp4or6 = "tcp6" + } + + ln, err := lc.Listen(s.ctx, tcp4or6, net.JoinHostPort(ipStr, fmt.Sprint(s.ap.Port()))) if err != nil { if s.shouldWarnAboutListenError(err) { s.logf("serve failed to listen on %v, backing off: %v", s.ap, err)