control/controlhttp: add health warning for macOS filtering blocking Tailscale (#12546)

Updates #9658
Updates #12545

Change-Id: I6612b9b65eb193a1a651e219b5198c7c20ed94e1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Co-authored-by: Andrea Gottardo <andrea@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2024-06-19 13:22:14 -07:00 committed by GitHub
parent 1023b2a82c
commit 0004827681
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -32,12 +32,14 @@
"net/http/httptrace"
"net/netip"
"net/url"
"runtime"
"sort"
"sync/atomic"
"time"
"tailscale.com/control/controlbase"
"tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/net/dnscache"
"tailscale.com/net/dnsfallback"
"tailscale.com/net/netutil"
@ -396,12 +398,28 @@ func (a *Dialer) resolver() *dnscache.Resolver {
}
}
func isLoopback(a net.Addr) bool {
if ta, ok := a.(*net.TCPAddr); ok {
return ta.IP.IsLoopback()
}
return false
}
var macOSScreenTime = health.Register(&health.Warnable{
Code: "macos-screen-time",
Title: "Tailscale blocked by Screen Time",
Text: func(args health.Args) string {
return "macOS Screen Time seems to be blocking Tailscale. Try disabling Screen Time in System Settings > Screen Time > Content & Privacy > Access to Web Content."
},
ImpactsConnectivity: true,
})
// tryURLUpgrade connects to u, and tries to upgrade it to a net.Conn. If addr
// is valid, then no DNS is used and the connection will be made to the
// provided address.
//
// Only the provided ctx is used, not a.ctx.
func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr, init []byte) (net.Conn, error) {
func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr, init []byte) (_ net.Conn, retErr error) {
var dns *dnscache.Resolver
// If we were provided an address to dial, then create a resolver that just
@ -423,6 +441,30 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr,
dialer = stdDialer.DialContext
}
// On macOS, see if Screen Time is blocking things.
if runtime.GOOS == "darwin" {
var proxydIntercepted atomic.Bool // intercepted by macOS webfilterproxyd
origDialer := dialer
dialer = func(ctx context.Context, network, address string) (net.Conn, error) {
c, err := origDialer(ctx, network, address)
if err != nil {
return nil, err
}
if isLoopback(c.LocalAddr()) && isLoopback(c.RemoteAddr()) {
proxydIntercepted.Store(true)
}
return c, nil
}
defer func() {
if retErr != nil && proxydIntercepted.Load() {
a.HealthTracker.SetUnhealthy(macOSScreenTime, nil)
retErr = fmt.Errorf("macOS Screen Time is blocking network access: %w", retErr)
} else {
a.HealthTracker.SetHealthy(macOSScreenTime)
}
}()
}
tr := http.DefaultTransport.(*http.Transport).Clone()
defer tr.CloseIdleConnections()
tr.Proxy = a.getProxyFunc()