diff --git a/control/controlclient/map.go b/control/controlclient/map.go index 2468226d1..b3f559a56 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -93,6 +93,16 @@ type mapSession struct { lastTKAInfo *tailcfg.TKAInfo lastNetmapSummary string // from NetworkMap.VeryConcise lastMaxExpiry time.Duration + + // breakPeersForFlowLogs is whether we've detected a situation + // that's incompatible with flow logs: the tailnet admin has enabled + // network flow logs, but the local machine has been configured to + // not log. In this case, to be conservative, we drop all peers + // but keep the map poll connection open. This way, the admin can + // still get back into the machine later if they first disable flow + // logs. But we otherwise disable connectivity over Tailscale. + // See https://github.com/tailscale/tailscale/issues/11793 + breakPeersForFlowLogs bool } // newMapSession returns a mostly unconfigured new mapSession. @@ -181,6 +191,8 @@ func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *t } } ms.controlKnobs.UpdateFromNodeAttributes(resp.Node.CapMap) + + ms.breakPeersForFlowLogs = envknob.NoLogsNoSupport() && resp.Node.CapMap.Contains(tailcfg.CapabilityDataPlaneAuditLogs) } // Call Node.InitDisplayNames on any changed nodes. @@ -219,6 +231,9 @@ func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *t } func (ms *mapSession) tryHandleIncrementally(res *tailcfg.MapResponse) bool { + if ms.breakPeersForFlowLogs { + return false + } if ms.controlKnobs != nil && ms.controlKnobs.DisableDeltaUpdates.Load() { return false } @@ -805,6 +820,10 @@ func (ms *mapSession) netmap() *netmap.NetworkMap { MaxKeyDuration: ms.lastMaxExpiry, } + if ms.breakPeersForFlowLogs { + nm.Peers = nil + } + if ms.lastTKAInfo != nil && ms.lastTKAInfo.Head != "" { if err := nm.TKAHead.UnmarshalText([]byte(ms.lastTKAInfo.Head)); err != nil { ms.logf("error unmarshalling TKAHead: %v", err) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 4c57502aa..f9bb3bb3d 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1013,6 +1013,8 @@ func (b *LocalBackend) peerCapsLocked(src netip.Addr) tailcfg.PeerCapMap { return nil } +var noLogsNoSupportNotifyOnce sync.Once + // SetControlClientStatus is the callback invoked by the control client whenever it posts a new status. // Among other things, this is where we update the netmap, packet filters, DNS and DERP maps. func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st controlclient.Status) { @@ -1216,20 +1218,11 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control if st.NetMap != nil { if envknob.NoLogsNoSupport() && st.NetMap.HasCap(tailcfg.CapabilityDataPlaneAuditLogs) { - msg := "tailnet requires logging to be enabled. Remove --no-logs-no-support from tailscaled command line." + msg := "tailnet requires logging to be enabled. Remove --no-logs-no-support from the tailscaled command line." health.SetLocalLogConfigHealth(errors.New(msg)) - // Connecting to this tailnet without logging is forbidden; boot us outta here. - b.mu.Lock() - prefs.WantRunning = false - p := prefs.View() - if err := b.pm.SetPrefs(p, ipn.NetworkProfile{ - MagicDNSName: st.NetMap.MagicDNSSuffix(), - DomainName: st.NetMap.DomainName(), - }); err != nil { - b.logf("Failed to save new controlclient state: %v", err) - } - b.mu.Unlock() - b.send(ipn.Notify{ErrMessage: &msg, Prefs: &p}) + noLogsNoSupportNotifyOnce.Do(func() { + b.send(ipn.Notify{ErrMessage: ptr.To(msg)}) + }) return } if netMap != nil {