WIP: rebase fork onto upstream/main (v1.103.0) #15
@@ -734,9 +734,11 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
|
||||
tailscale.com/feature/c2n from tailscale.com/tsnet
|
||||
tailscale.com/feature/condlite/expvar from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/feature/condregister/netlog from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/useproxy from tailscale.com/tsnet
|
||||
tailscale.com/feature/netlog from tailscale.com/feature/condregister/netlog
|
||||
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
|
||||
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
|
||||
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
|
||||
@@ -921,7 +923,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/feature/netlog
|
||||
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
||||
|
||||
@@ -62,6 +62,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature/buildfeatures from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/feature/condlite/expvar from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/feature/condregister/netlog from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condregister/useproxy from tailscale.com/feature/condregister
|
||||
tailscale.com/health from tailscale.com/control/controlclient+
|
||||
@@ -195,7 +196,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
|
||||
|
||||
@@ -75,6 +75,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/feature/condregister/awsparamstore from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/feature/condregister/identityfederation from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/feature/condregister/netlog from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condregister/oauthkey from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister+
|
||||
tailscale.com/feature/condregister/useproxy from tailscale.com/cmd/tailscale/cli+
|
||||
@@ -216,7 +217,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
|
||||
|
||||
@@ -298,6 +298,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature/clientupdate from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condlite/expvar from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/feature/condregister/netlog from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condregister/useproxy from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/conn25 from tailscale.com/feature/condregister
|
||||
@@ -306,6 +307,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature/drive from tailscale.com/feature/condregister
|
||||
L tailscale.com/feature/linkspeed from tailscale.com/feature/condregister
|
||||
L tailscale.com/feature/linuxdnsfight from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/netlog from tailscale.com/feature/condregister/netlog
|
||||
tailscale.com/feature/portlist from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
|
||||
tailscale.com/feature/posture from tailscale.com/feature/condregister
|
||||
@@ -506,7 +508,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/feature/netlog
|
||||
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||
|
||||
@@ -151,9 +151,11 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
|
||||
tailscale.com/feature/c2n from tailscale.com/tsnet
|
||||
tailscale.com/feature/condlite/expvar from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/feature/condregister/netlog from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/useproxy from tailscale.com/tsnet
|
||||
tailscale.com/feature/netlog from tailscale.com/feature/condregister/netlog
|
||||
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
|
||||
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
|
||||
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
|
||||
@@ -319,7 +321,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/feature/netlog
|
||||
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package condregister
|
||||
|
||||
import _ "tailscale.com/feature/condregister/netlog"
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package netlog registers support for network flow logging if it's
|
||||
// not disabled via the ts_omit_netlog or ts_omit_logtail build tags.
|
||||
// It is pulled out separately from the main condregister package so
|
||||
// that tsnet can link it without linking every conditional feature.
|
||||
package netlog
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !ts_omit_netlog && !ts_omit_logtail
|
||||
|
||||
package netlog
|
||||
|
||||
import _ "tailscale.com/feature/netlog"
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package netlog registers the network flow logging feature and wires
|
||||
// it into the WireGuard engine. The logger implementation itself lives
|
||||
// in tailscale.com/wgengine/netlog.
|
||||
package netlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/types/logid"
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/netlog"
|
||||
"tailscale.com/wgengine/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
feature.Register("netlog")
|
||||
wgengine.HookNewNetLogger.Set(newNetLogger)
|
||||
}
|
||||
|
||||
// uploadTimeout is the maximum time to wait when shutting down the
|
||||
// network logger as it uploads the final network log messages.
|
||||
const uploadTimeout = 5 * time.Second
|
||||
|
||||
// netLogger implements [wgengine.NetLogger] on top of [netlog.Logger],
|
||||
// tracking the logging identity and running state across engine
|
||||
// reconfigurations. One netLogger exists per engine.
|
||||
type netLogger struct {
|
||||
deps wgengine.NetLoggerDeps
|
||||
logger netlog.Logger
|
||||
|
||||
// The fields below are only accessed from Reconfig and
|
||||
// ReconfigDone, which the engine serializes under its internal
|
||||
// lock.
|
||||
|
||||
// nodeID and domainID are the logging IDs from the last Reconfig,
|
||||
// or zero if logging was not enabled then.
|
||||
nodeID, domainID logid.PrivateID
|
||||
// logExitFlows is whether exit node flows were logged as of the
|
||||
// last Reconfig.
|
||||
logExitFlows bool
|
||||
// shouldRun is whether the last Reconfig wanted the logger
|
||||
// running. ReconfigDone consults it to shut down a stopping
|
||||
// logger after the router has been reconfigured.
|
||||
shouldRun bool
|
||||
}
|
||||
|
||||
func newNetLogger(deps wgengine.NetLoggerDeps) wgengine.NetLogger {
|
||||
return &netLogger{deps: deps}
|
||||
}
|
||||
|
||||
func (nl *netLogger) Reconfig(routerCfg *router.Config, routerChanged bool) (changed bool) {
|
||||
logf := nl.deps.Logf
|
||||
src := nl.deps.Source()
|
||||
|
||||
var nodeID, domainID logid.PrivateID
|
||||
var logExitFlows bool
|
||||
if src != nil {
|
||||
if nid, did, lef, ok := src.NetLogIDs(); ok {
|
||||
nodeID, domainID, logExitFlows = nid, did, lef
|
||||
}
|
||||
}
|
||||
idsValid := !nodeID.IsZero() && !domainID.IsZero()
|
||||
idsWereValid := !nl.nodeID.IsZero() && !nl.domainID.IsZero()
|
||||
changed = nodeID != nl.nodeID || domainID != nl.domainID || logExitFlows != nl.logExitFlows
|
||||
idsChanged := idsValid && idsWereValid && changed
|
||||
nl.nodeID, nl.domainID, nl.logExitFlows = nodeID, domainID, logExitFlows
|
||||
|
||||
nl.shouldRun = idsValid && !routerCfg.Equal(&router.Config{}) && !envknob.NoLogsNoSupport()
|
||||
|
||||
// Shut down the logger if the IDs changed while it was running,
|
||||
// letting the startup logic below bring it back up with the new
|
||||
// identity.
|
||||
if idsChanged && nl.logger.Running() {
|
||||
logf("wgengine: Reconfig: shutting down network logger")
|
||||
nl.shutdown()
|
||||
}
|
||||
|
||||
// Start the logger before the engine configures the router so
|
||||
// that it captures initial packets.
|
||||
if nl.shouldRun && !nl.logger.Running() {
|
||||
logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nodeID.Public(), domainID.Public())
|
||||
if err := nl.logger.Startup(logf, src, nodeID, domainID, nl.deps.Tun, nl.deps.Sock, nl.deps.NetMon, nl.deps.Health, nl.deps.Bus, logExitFlows); err != nil {
|
||||
logf("wgengine: Reconfig: error starting up network logger: %v", err)
|
||||
}
|
||||
nl.logger.ReconfigRoutes(routerCfg)
|
||||
} else if routerChanged && nl.logger.Running() {
|
||||
nl.logger.ReconfigRoutes(routerCfg)
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (nl *netLogger) ReconfigDone() {
|
||||
// Shut down a stopping logger only now, after the engine has
|
||||
// configured the router, so that it captures final packets.
|
||||
if !nl.shouldRun && nl.logger.Running() {
|
||||
nl.deps.Logf("wgengine: Reconfig: shutting down network logger")
|
||||
nl.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func (nl *netLogger) Shutdown() {
|
||||
nl.shutdown()
|
||||
}
|
||||
|
||||
// shutdown stops the logger, waiting up to uploadTimeout for it to
|
||||
// flush its final messages.
|
||||
func (nl *netLogger) shutdown() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), uploadTimeout)
|
||||
defer cancel()
|
||||
if err := nl.logger.Shutdown(ctx); err != nil {
|
||||
nl.deps.Logf("wgengine: error shutting down network logger: %v", err)
|
||||
}
|
||||
}
|
||||
+36
-7
@@ -103,7 +103,6 @@ import (
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/netlog"
|
||||
"tailscale.com/wgengine/router"
|
||||
"tailscale.com/wgengine/wgcfg"
|
||||
"tailscale.com/wgengine/wgcfg/nmcfg"
|
||||
@@ -636,7 +635,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
||||
e.SetPeerByIPPacketFunc(b.lookupPeerByIP)
|
||||
e.SetPeerForIPFunc(b.peerForIP)
|
||||
e.SetPeerSessionStateFunc(b.onPeerWireGuardState)
|
||||
e.SetNetLogNodeSource(netLogNodeSource{b})
|
||||
e.SetNetLogSource(netLogNodeSource{b})
|
||||
e.SetWGPeerLookup(b.lookupPeerWireGuardString)
|
||||
b.dialer.SetResolveMagicDNS(b.resolveMagicDNS)
|
||||
|
||||
@@ -8017,9 +8016,10 @@ func (n noiseRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
||||
return n.lb.DoNoiseRequest(req)
|
||||
}
|
||||
|
||||
// netLogNodeSource adapts LocalBackend's nodeBackend to [netlog.NodeSource].
|
||||
// Each method consults [LocalBackend.currentNode] so that profile rotations
|
||||
// are picked up automatically without re-installing the source.
|
||||
// netLogNodeSource adapts LocalBackend to [wgengine.NetLogSource].
|
||||
// Each method consults [LocalBackend.currentNode] or the current netmap
|
||||
// so that profile rotations are picked up automatically without
|
||||
// re-installing the source.
|
||||
type netLogNodeSource struct {
|
||||
b *LocalBackend
|
||||
}
|
||||
@@ -8048,8 +8048,37 @@ func (s netLogNodeSource) NodeByAddr(addr netip.Addr) (_ tailcfg.NodeView, _ tai
|
||||
return nv, up, true
|
||||
}
|
||||
|
||||
// Compile-time assertion that netLogNodeSource implements [netlog.NodeSource].
|
||||
var _ netlog.NodeSource = netLogNodeSource{}
|
||||
// NetLogIDs implements [wgengine.NetLogSource], returning the network
|
||||
// flow logging identity from the current netmap. ok is false if the
|
||||
// netmap does not enable network flow logging for this node.
|
||||
func (s netLogNodeSource) NetLogIDs() (nodeID, domainID logid.PrivateID, logExitFlows bool, ok bool) {
|
||||
nm := s.b.NetMap()
|
||||
if nm == nil || !nm.SelfNode.Valid() {
|
||||
return
|
||||
}
|
||||
if !nm.SelfNode.HasCap(tailcfg.CapabilityDataPlaneAuditLogs) {
|
||||
return
|
||||
}
|
||||
if nm.SelfNode.DataPlaneAuditLogID() == "" || nm.DomainAuditLogID == "" {
|
||||
return
|
||||
}
|
||||
nodeID, errNode := logid.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID())
|
||||
if errNode != nil {
|
||||
s.b.logf("[v1] netlog: unable to parse node audit log ID: %v", errNode)
|
||||
}
|
||||
domainID, errDomain := logid.ParsePrivateID(nm.DomainAuditLogID)
|
||||
if errDomain != nil {
|
||||
s.b.logf("[v1] netlog: unable to parse domain audit log ID: %v", errDomain)
|
||||
}
|
||||
if errNode != nil || errDomain != nil {
|
||||
return logid.PrivateID{}, logid.PrivateID{}, false, false
|
||||
}
|
||||
return nodeID, domainID, nm.SelfNode.HasCap(tailcfg.NodeAttrLogExitFlows), true
|
||||
}
|
||||
|
||||
// Compile-time assertion that netLogNodeSource implements
|
||||
// [wgengine.NetLogSource].
|
||||
var _ wgengine.NetLogSource = netLogNodeSource{}
|
||||
|
||||
// lookupPeerWireGuardString returns the Tailscale-conventional short string
|
||||
// (e.g. "[IMTBr]") for the peer whose wireguard-go-formatted public key
|
||||
|
||||
@@ -47,7 +47,6 @@ import (
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/netlog"
|
||||
"tailscale.com/wgengine/router"
|
||||
"tailscale.com/wgengine/wgcfg"
|
||||
"tailscale.com/wgengine/wgint"
|
||||
@@ -1989,7 +1988,7 @@ func (e *mockEngine) PeerKeyForIP(netip.Addr) (_ key.NodePublic, _ netip.Prefix,
|
||||
}
|
||||
func (e *mockEngine) SetPeerSessionStateFunc(func(key.NodePublic, wgengine.PeerWireGuardState)) {
|
||||
}
|
||||
func (e *mockEngine) SetNetLogNodeSource(netlog.NodeSource) {}
|
||||
func (e *mockEngine) SetNetLogSource(wgengine.NetLogSource) {}
|
||||
func (e *mockEngine) SetWGPeerLookup(func(wgString string) (tsString string, ok bool)) {}
|
||||
func (e *mockEngine) ProbeLocks() {}
|
||||
|
||||
|
||||
+3
-1
@@ -147,9 +147,11 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
|
||||
tailscale.com/feature/c2n from tailscale.com/tsnet
|
||||
tailscale.com/feature/condlite/expvar from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/feature/condregister/netlog from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/oauthkey from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/tsnet
|
||||
tailscale.com/feature/condregister/useproxy from tailscale.com/tsnet
|
||||
tailscale.com/feature/netlog from tailscale.com/feature/condregister/netlog
|
||||
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
|
||||
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
|
||||
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
|
||||
@@ -314,7 +316,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/netlog from tailscale.com/feature/netlog
|
||||
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
||||
|
||||
@@ -163,6 +163,7 @@ import (
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/envknob"
|
||||
_ "tailscale.com/feature/c2n"
|
||||
_ "tailscale.com/feature/condregister/netlog"
|
||||
_ "tailscale.com/feature/condregister/oauthkey"
|
||||
_ "tailscale.com/feature/condregister/portmapper"
|
||||
_ "tailscale.com/feature/condregister/useproxy"
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package wgengine
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/tstun"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/logid"
|
||||
"tailscale.com/util/eventbus"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/router"
|
||||
)
|
||||
|
||||
// NetLogSource provides the network flow logging feature what it needs
|
||||
// from the rest of the system (in practice, ipnlocal.LocalBackend):
|
||||
// on-demand node lookups and the current audit logging identity.
|
||||
type NetLogSource interface {
|
||||
// SelfNode returns the current self node and its owning user profile.
|
||||
// The views are invalid if there is no current node.
|
||||
SelfNode() (tailcfg.NodeView, tailcfg.UserProfileView)
|
||||
|
||||
// NodeByAddr returns the node assigned the given address along with
|
||||
// its owning user profile.
|
||||
// ok is false if no node is known to own addr.
|
||||
NodeByAddr(addr netip.Addr) (node tailcfg.NodeView, user tailcfg.UserProfileView, ok bool)
|
||||
|
||||
// NetLogIDs returns the audit logging IDs from the current netmap,
|
||||
// along with whether exit node flows should be logged.
|
||||
// ok is false if network flow logging should not run.
|
||||
NetLogIDs() (nodeID, domainID logid.PrivateID, logExitFlows bool, ok bool)
|
||||
}
|
||||
|
||||
// NetLogger is the engine's handle on the network flow logger
|
||||
// lifecycle, implemented by the feature/netlog package.
|
||||
//
|
||||
// Reconfig and ReconfigDone are only called from [Engine.Reconfig],
|
||||
// serialized by the engine's internal lock. Shutdown may be called
|
||||
// concurrently with them from [Engine.Close].
|
||||
type NetLogger interface {
|
||||
// Reconfig is called near the top of every [Engine.Reconfig],
|
||||
// before its ErrNoChanges early return (so identity-only changes
|
||||
// still take effect) and before the router is configured (so a
|
||||
// starting logger captures initial packets). It reports whether
|
||||
// network logging state changed, which suppresses the engine's
|
||||
// ErrNoChanges early return.
|
||||
Reconfig(routerCfg *router.Config, routerChanged bool) (changed bool)
|
||||
|
||||
// ReconfigDone is called at the end of [Engine.Reconfig], after
|
||||
// the router is configured, so that a logger stopping as a result
|
||||
// of the preceding Reconfig call captures final packets. It may
|
||||
// block to flush pending messages.
|
||||
ReconfigDone()
|
||||
|
||||
// Shutdown stops the logger. It may block to flush pending
|
||||
// messages, bounded by an internal upload timeout.
|
||||
Shutdown()
|
||||
}
|
||||
|
||||
// NetLoggerDeps are the engine-owned dependencies passed to
|
||||
// [HookNewNetLogger] to construct a [NetLogger].
|
||||
type NetLoggerDeps struct {
|
||||
Logf logger.Logf
|
||||
Tun *tstun.Wrapper
|
||||
Sock *magicsock.Conn
|
||||
NetMon *netmon.Monitor
|
||||
Health *health.Tracker
|
||||
Bus *eventbus.Bus
|
||||
|
||||
// Source returns the [NetLogSource] installed via
|
||||
// [Engine.SetNetLogSource], or nil if none has been installed yet.
|
||||
// It is a func because the source is installed after the engine
|
||||
// (and thus the NetLogger) is constructed.
|
||||
Source func() NetLogSource
|
||||
}
|
||||
|
||||
// HookNewNetLogger is set by the feature/netlog package to construct
|
||||
// each engine's [NetLogger]. If unset, network flow logging is not
|
||||
// linked into the binary and the engine skips all NetLogger calls.
|
||||
var HookNewNetLogger feature.Hook[func(NetLoggerDeps) NetLogger]
|
||||
+42
-66
@@ -58,7 +58,6 @@ import (
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/netlog"
|
||||
"tailscale.com/wgengine/netstack/gro"
|
||||
"tailscale.com/wgengine/router"
|
||||
"tailscale.com/wgengine/wgcfg"
|
||||
@@ -66,10 +65,6 @@ import (
|
||||
"tailscale.com/wgengine/wglog"
|
||||
)
|
||||
|
||||
// networkLoggerUploadTimeout is the maximum timeout to wait when
|
||||
// shutting down the network logger as it uploads the last network log messages.
|
||||
const networkLoggerUploadTimeout = 5 * time.Second
|
||||
|
||||
type userspaceEngine struct {
|
||||
// eventBus will eventually become required, but for now may be nil.
|
||||
eventBus *eventbus.Bus
|
||||
@@ -156,13 +151,16 @@ type userspaceEngine struct {
|
||||
// value of the ICMP identifier and sequence number concatenated.
|
||||
icmpEchoResponseCallback map[uint32]func()
|
||||
|
||||
// networkLogger logs statistics about network connections.
|
||||
networkLogger netlog.Logger
|
||||
// netlogger is the network flow logger handle constructed via
|
||||
// [HookNewNetLogger], or nil if the feature/netlog package is not
|
||||
// linked into the binary.
|
||||
netlogger NetLogger
|
||||
|
||||
// netLogSource is the [netlog.NodeSource] installed via
|
||||
// [Engine.SetNetLogNodeSource]; it is read when starting up the network
|
||||
// logger from inside Reconfig. It may be nil if no source was installed.
|
||||
netLogSource syncs.AtomicValue[netlog.NodeSource]
|
||||
// netLogSource is the [NetLogSource] installed via
|
||||
// [Engine.SetNetLogSource]; the netlogger reads it on each
|
||||
// Reconfig via [NetLoggerDeps.Source]. It may be nil if no source
|
||||
// was installed.
|
||||
netLogSource syncs.AtomicValue[NetLogSource]
|
||||
|
||||
// wgPeerLookup is the lookup function installed via
|
||||
// [Engine.SetWGPeerLookup]; it is consulted by [wgLogger] to translate
|
||||
@@ -454,6 +452,18 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
|
||||
closePool.add(e.magicConn)
|
||||
e.magicConn.SetNetworkUp(e.netMon.InterfaceState().AnyInterfaceUp())
|
||||
|
||||
if newNetLogger, ok := HookNewNetLogger.GetOk(); ok {
|
||||
e.netlogger = newNetLogger(NetLoggerDeps{
|
||||
Logf: logf,
|
||||
Tun: e.tundev,
|
||||
Sock: e.magicConn,
|
||||
NetMon: e.netMon,
|
||||
Health: e.health,
|
||||
Bus: e.eventBus,
|
||||
Source: e.netLogSource.Load,
|
||||
})
|
||||
}
|
||||
|
||||
tsTUNDev.SetDiscoKey(e.magicConn.DiscoPublicKey())
|
||||
|
||||
if conf.RespondToPing {
|
||||
@@ -758,9 +768,9 @@ func (e *userspaceEngine) SetPeerSessionStateFunc(fn func(key.NodePublic, PeerWi
|
||||
})
|
||||
}
|
||||
|
||||
// SetNetLogNodeSource installs the [netlog.NodeSource] used by the engine's
|
||||
// network logger.
|
||||
func (e *userspaceEngine) SetNetLogNodeSource(src netlog.NodeSource) {
|
||||
// SetNetLogSource installs the [NetLogSource] consulted by the engine's
|
||||
// network flow logger.
|
||||
func (e *userspaceEngine) SetNetLogSource(src NetLogSource) {
|
||||
e.netLogSource.Store(src)
|
||||
}
|
||||
|
||||
@@ -864,17 +874,18 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
||||
|
||||
listenPortChanged := listenPort != e.magicConn.LocalPort()
|
||||
peerMTUChanged := peerMTUEnable != e.magicConn.PeerMTUEnabled()
|
||||
if !engineChanged && !routerChanged && !dnsChanged && !listenPortChanged && !isSubnetRouterChanged && !peerMTUChanged {
|
||||
return ErrNoChanges
|
||||
|
||||
// Let the network flow logger react before the early return below,
|
||||
// so that logging identity changes take effect even when nothing
|
||||
// else changed, and before the router is configured, so that a
|
||||
// starting logger captures initial packets.
|
||||
netlogChanged := false
|
||||
if e.netlogger != nil {
|
||||
netlogChanged = e.netlogger.Reconfig(routerCfg, routerChanged)
|
||||
}
|
||||
newLogIDs := cfg.NetworkLogging
|
||||
oldLogIDs := e.lastCfgFull.NetworkLogging
|
||||
netLogIDsNowValid := !newLogIDs.NodeID.IsZero() && !newLogIDs.DomainID.IsZero()
|
||||
netLogIDsWasValid := !oldLogIDs.NodeID.IsZero() && !oldLogIDs.DomainID.IsZero()
|
||||
netLogIDsChanged := netLogIDsNowValid && netLogIDsWasValid && newLogIDs != oldLogIDs
|
||||
netLogRunning := netLogIDsNowValid && !routerCfg.Equal(&router.Config{})
|
||||
if !buildfeatures.HasNetLog || envknob.NoLogsNoSupport() {
|
||||
netLogRunning = false
|
||||
|
||||
if !engineChanged && !routerChanged && !dnsChanged && !listenPortChanged && !isSubnetRouterChanged && !peerMTUChanged && !netlogChanged {
|
||||
return ErrNoChanges
|
||||
}
|
||||
|
||||
// TODO(bradfitz,danderson): maybe delete this isDNSIPOverTailscale
|
||||
@@ -982,36 +993,8 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown the network logger because the IDs changed.
|
||||
// Let it be started back up by subsequent logic.
|
||||
if buildfeatures.HasNetLog && netLogIDsChanged && e.networkLogger.Running() {
|
||||
e.logf("wgengine: Reconfig: shutting down network logger")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), networkLoggerUploadTimeout)
|
||||
defer cancel()
|
||||
if err := e.networkLogger.Shutdown(ctx); err != nil {
|
||||
e.logf("wgengine: Reconfig: error shutting down network logger: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Startup the network logger.
|
||||
// Do this before configuring the router so that we capture initial packets.
|
||||
if buildfeatures.HasNetLog && netLogRunning && !e.networkLogger.Running() {
|
||||
nid := cfg.NetworkLogging.NodeID
|
||||
tid := cfg.NetworkLogging.DomainID
|
||||
logExitFlowEnabled := cfg.NetworkLogging.LogExitFlowEnabled
|
||||
e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public())
|
||||
src := e.netLogSource.Load()
|
||||
if src == nil {
|
||||
e.logf("wgengine: Reconfig: no NodeSource installed; network logger not started")
|
||||
} else if err := e.networkLogger.Startup(e.logf, src, nid, tid, e.tundev, e.magicConn, e.netMon, e.health, e.eventBus, logExitFlowEnabled); err != nil {
|
||||
e.logf("wgengine: Reconfig: error starting up network logger: %v", err)
|
||||
}
|
||||
e.networkLogger.ReconfigRoutes(routerCfg)
|
||||
}
|
||||
|
||||
if routerChanged {
|
||||
e.logf("wgengine: Reconfig: configuring router")
|
||||
e.networkLogger.ReconfigRoutes(routerCfg)
|
||||
err := e.router.Set(routerCfg)
|
||||
e.health.SetRouterHealth(err)
|
||||
if err != nil {
|
||||
@@ -1050,16 +1033,11 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown the network logger.
|
||||
// Do this after configuring the router so that we capture final packets.
|
||||
// This attempts to flush out any log messages and may block.
|
||||
if !netLogRunning && e.networkLogger.Running() {
|
||||
e.logf("wgengine: Reconfig: shutting down network logger")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), networkLoggerUploadTimeout)
|
||||
defer cancel()
|
||||
if err := e.networkLogger.Shutdown(ctx); err != nil {
|
||||
e.logf("wgengine: Reconfig: error shutting down network logger: %v", err)
|
||||
}
|
||||
// Let the network flow logger finish reacting after the router is
|
||||
// configured, so that a stopping logger captures final packets.
|
||||
// This may block to flush pending log messages.
|
||||
if e.netlogger != nil {
|
||||
e.netlogger.ReconfigDone()
|
||||
}
|
||||
|
||||
if buildfeatures.HasBird && isSubnetRouterChanged && e.birdClient != nil {
|
||||
@@ -1252,10 +1230,8 @@ func (e *userspaceEngine) Close() {
|
||||
}
|
||||
close(e.waitCh)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), networkLoggerUploadTimeout)
|
||||
defer cancel()
|
||||
if err := e.networkLogger.Shutdown(ctx); err != nil {
|
||||
e.logf("wgengine: Close: error shutting down network logger: %v", err)
|
||||
if e.netlogger != nil {
|
||||
e.netlogger.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"slices"
|
||||
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logid"
|
||||
)
|
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner -type=Config,Peer
|
||||
@@ -20,15 +19,6 @@ type Config struct {
|
||||
PrivateKey key.NodePrivate
|
||||
Addresses []netip.Prefix
|
||||
Peers []Peer
|
||||
|
||||
// NetworkLogging enables network logging.
|
||||
// It is disabled if either ID is the zero value.
|
||||
// LogExitFlowEnabled indicates whether or not exit flows should be logged.
|
||||
NetworkLogging struct {
|
||||
NodeID logid.PrivateID
|
||||
DomainID logid.PrivateID
|
||||
LogExitFlowEnabled bool
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Equal(o *Config) bool {
|
||||
@@ -36,7 +26,6 @@ func (c *Config) Equal(o *Config) bool {
|
||||
return c == o
|
||||
}
|
||||
return c.PrivateKey.Equal(o.PrivateKey) &&
|
||||
c.NetworkLogging == o.NetworkLogging &&
|
||||
slices.Equal(c.Addresses, o.Addresses) &&
|
||||
slices.EqualFunc(c.Peers, o.Peers, Peer.Equal)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ func TestConfigEqual(t *testing.T) {
|
||||
rt := reflect.TypeFor[Config]()
|
||||
for sf := range rt.Fields() {
|
||||
switch sf.Name {
|
||||
case "Name", "NodeID", "PrivateKey", "Addresses", "Peers",
|
||||
"NetworkLogging":
|
||||
case "Name", "NodeID", "PrivateKey", "Addresses", "Peers":
|
||||
// These are compared in [Config.Equal].
|
||||
default:
|
||||
t.Errorf("Have you added field %q to Config.Equal? Do so if not, and then update TestConfigEqual", sf.Name)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/logid"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/wgengine/wgcfg"
|
||||
)
|
||||
@@ -53,27 +52,6 @@ func WGCfg(pk key.NodePrivate, nm *netmap.NetworkMap, logf logger.Logf, flags ne
|
||||
Peers: make([]wgcfg.Peer, 0, len(nm.Peers)),
|
||||
}
|
||||
|
||||
// Setup log IDs for data plane audit logging.
|
||||
if nm.SelfNode.Valid() {
|
||||
canNetworkLog := nm.SelfNode.HasCap(tailcfg.CapabilityDataPlaneAuditLogs)
|
||||
logExitFlowEnabled := nm.SelfNode.HasCap(tailcfg.NodeAttrLogExitFlows)
|
||||
if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID() != "" && nm.DomainAuditLogID != "" {
|
||||
nodeID, errNode := logid.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID())
|
||||
if errNode != nil {
|
||||
logf("[v1] wgcfg: unable to parse node audit log ID: %v", errNode)
|
||||
}
|
||||
domainID, errDomain := logid.ParsePrivateID(nm.DomainAuditLogID)
|
||||
if errDomain != nil {
|
||||
logf("[v1] wgcfg: unable to parse domain audit log ID: %v", errDomain)
|
||||
}
|
||||
if errNode == nil && errDomain == nil {
|
||||
cfg.NetworkLogging.NodeID = nodeID
|
||||
cfg.NetworkLogging.DomainID = domainID
|
||||
cfg.NetworkLogging.LogExitFlowEnabled = logExitFlowEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var skippedExitNode, skippedSubnetRouter, skippedExpired []tailcfg.NodeView
|
||||
|
||||
for _, peer := range nm.Peers {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"net/netip"
|
||||
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logid"
|
||||
)
|
||||
|
||||
// Clone makes a deep copy of Config.
|
||||
@@ -32,14 +31,9 @@ func (src *Config) Clone() *Config {
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _ConfigCloneNeedsRegeneration = Config(struct {
|
||||
PrivateKey key.NodePrivate
|
||||
Addresses []netip.Prefix
|
||||
Peers []Peer
|
||||
NetworkLogging struct {
|
||||
NodeID logid.PrivateID
|
||||
DomainID logid.PrivateID
|
||||
LogExitFlowEnabled bool
|
||||
}
|
||||
PrivateKey key.NodePrivate
|
||||
Addresses []netip.Prefix
|
||||
Peers []Peer
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Peer.
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/netlog"
|
||||
"tailscale.com/wgengine/router"
|
||||
"tailscale.com/wgengine/wgcfg"
|
||||
"tailscale.com/wgengine/wgint"
|
||||
@@ -198,12 +197,13 @@ type Engine interface {
|
||||
// look up which peer should handle an outbound packet by destination IP.
|
||||
SetPeerByIPPacketFunc(func(netip.Addr) (_ key.NodePublic, ok bool))
|
||||
|
||||
// SetNetLogNodeSource installs the [netlog.NodeSource] used by the engine's
|
||||
// network logger to look up Tailscale node info on demand.
|
||||
// SetNetLogSource installs the [NetLogSource] consulted by the
|
||||
// engine's network flow logger for node lookups and the current
|
||||
// audit logging identity.
|
||||
//
|
||||
// It is expected to be called once during LocalBackend construction,
|
||||
// before any [Engine.Reconfig] call that starts up the network logger.
|
||||
SetNetLogNodeSource(netlog.NodeSource)
|
||||
SetNetLogSource(NetLogSource)
|
||||
|
||||
// SetWGPeerLookup installs the function used by the engine's
|
||||
// wireguard-go log wrapper to rewrite peer references in log lines
|
||||
|
||||
Reference in New Issue
Block a user