diff --git a/types/netlogtype/netlogtype.go b/types/netlogtype/netlogtype.go index c0f985eef..dab74572f 100644 --- a/types/netlogtype/netlogtype.go +++ b/types/netlogtype/netlogtype.go @@ -9,11 +9,17 @@ import ( "net/netip" "time" + "tailscale.com/tailcfg" "tailscale.com/types/ipproto" ) +// TODO(joetsai): Remove "omitempty" if "omitzero" is ever supported in both +// the v1 and v2 "json" packages. + // Message is the log message that captures network traffic. type Message struct { + NodeID tailcfg.StableNodeID `json:"nodeId"` // e.g., "n123456CNTRL" + Start time.Time `json:"start"` // inclusive End time.Time `json:"end"` // inclusive diff --git a/wgengine/netlog/logger.go b/wgengine/netlog/logger.go index 69534cc15..df2b0a54a 100644 --- a/wgengine/netlog/logger.go +++ b/wgengine/netlog/logger.go @@ -22,6 +22,7 @@ import ( "tailscale.com/logtail" "tailscale.com/net/tsaddr" "tailscale.com/smallzstd" + "tailscale.com/tailcfg" "tailscale.com/types/netlogtype" "tailscale.com/wgengine/router" ) @@ -91,7 +92,7 @@ var testClient *http.Client // is a non-tailscale IP address to contact for that particular tailscale node. // The IP protocol and source port are always zero. // The sock is used to populated the PhysicalTraffic field in Message. -func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device) error { +func (nl *Logger) Startup(nodeID tailcfg.StableNodeID, nodeLogID, domainLogID logtail.PrivateID, tun, sock Device) error { nl.mu.Lock() defer nl.mu.Unlock() if nl.logger != nil { @@ -110,8 +111,8 @@ func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device) } logger := logtail.NewLogger(logtail.Config{ Collection: "tailtraffic.log.tailscale.io", - PrivateID: nodeID, - CopyPrivateID: domainID, + PrivateID: nodeLogID, + CopyPrivateID: domainLogID, Stderr: io.Discard, // TODO(joetsai): Set Buffer? Use an in-memory buffer for now. NewZstdEncoder: func() logtail.Encoder { @@ -161,7 +162,7 @@ func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device) addrs := nl.addrs prefixes := nl.prefixes nl.mu.Unlock() - recordStatistics(logger, start, end, tunStats, sockStats, addrs, prefixes) + recordStatistics(logger, nodeID, start, end, tunStats, sockStats, addrs, prefixes) } if ctx.Err() != nil { @@ -174,8 +175,8 @@ func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device) return nil } -func recordStatistics(logger *logtail.Logger, start, end time.Time, tunStats, sockStats map[netlogtype.Connection]netlogtype.Counts, addrs map[netip.Addr]bool, prefixes map[netip.Prefix]bool) { - m := netlogtype.Message{Start: start.UTC(), End: end.UTC()} +func recordStatistics(logger *logtail.Logger, nodeID tailcfg.StableNodeID, start, end time.Time, tunStats, sockStats map[netlogtype.Connection]netlogtype.Counts, addrs map[netip.Addr]bool, prefixes map[netip.Prefix]bool) { + m := netlogtype.Message{NodeID: nodeID, Start: start.UTC(), End: end.UTC()} classifyAddr := func(a netip.Addr) (isTailscale, withinRoute bool) { // NOTE: There could be mis-classifications where an address is treated diff --git a/wgengine/netlog/logger_test.go b/wgengine/netlog/logger_test.go index 5da4ad5ea..d7dd146f7 100644 --- a/wgengine/netlog/logger_test.go +++ b/wgengine/netlog/logger_test.go @@ -58,7 +58,7 @@ func TestResourceCheck(t *testing.T) { var l Logger var d fakeDevice for i := 0; i < 10; i++ { - must.Do(l.Startup(logtail.PrivateID{}, logtail.PrivateID{}, &d, nil)) + must.Do(l.Startup("", logtail.PrivateID{}, logtail.PrivateID{}, &d, nil)) l.ReconfigRoutes(&router.Config{}) must.Do(l.Shutdown(context.Background())) c.Assert(d.toggled, qt.Equals, 2*(i+1)) diff --git a/wgengine/userspace.go b/wgengine/userspace.go index ae03b5558..48ca20395 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -953,7 +953,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, nid := cfg.NetworkLogging.NodeID tid := cfg.NetworkLogging.DomainID e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public()) - if err := e.networkLogger.Startup(nid, tid, e.tundev, e.magicConn); err != nil { + if err := e.networkLogger.Startup(cfg.NodeID, nid, tid, e.tundev, e.magicConn); err != nil { e.logf("wgengine: Reconfig: error starting up network logger: %v", err) } } diff --git a/wgengine/wgcfg/config.go b/wgengine/wgcfg/config.go index 3598856be..96eeda1bd 100644 --- a/wgengine/wgcfg/config.go +++ b/wgengine/wgcfg/config.go @@ -9,6 +9,7 @@ import ( "net/netip" "tailscale.com/logtail" + "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -18,6 +19,7 @@ import ( // It only supports the set of things Tailscale uses. type Config struct { Name string + NodeID tailcfg.StableNodeID PrivateKey key.NodePrivate Addresses []netip.Prefix MTU uint16 diff --git a/wgengine/wgcfg/nmcfg/nmcfg.go b/wgengine/wgcfg/nmcfg/nmcfg.go index acd02eb04..604e8b309 100644 --- a/wgengine/wgcfg/nmcfg/nmcfg.go +++ b/wgengine/wgcfg/nmcfg/nmcfg.go @@ -62,6 +62,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags, // Setup log IDs for data plane audit logging. if nm.SelfNode != nil { + cfg.NodeID = nm.SelfNode.StableID canNetworkLog := slices.Contains(nm.SelfNode.Capabilities, tailcfg.CapabilityDataPlaneAuditLogs) if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID != "" && nm.DomainAuditLogID != "" { nodeID, errNode := logtail.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID) diff --git a/wgengine/wgcfg/wgcfg_clone.go b/wgengine/wgcfg/wgcfg_clone.go index abf319c22..e556fe5aa 100644 --- a/wgengine/wgcfg/wgcfg_clone.go +++ b/wgengine/wgcfg/wgcfg_clone.go @@ -10,6 +10,7 @@ import ( "net/netip" "tailscale.com/logtail" + "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -33,6 +34,7 @@ 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 { Name string + NodeID tailcfg.StableNodeID PrivateKey key.NodePrivate Addresses []netip.Prefix MTU uint16