wgengine/netlog: embed the StableNodeID of the authoring node (#6105)
This allows network messages to be annotated with which node it came from. Signed-off-by: Joe Tsai <joetsai@digital-static.net>pull/6108/head
parent
81fd259133
commit
a3602c28bd
|
@ -9,11 +9,17 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/ipproto"
|
"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.
|
// Message is the log message that captures network traffic.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
NodeID tailcfg.StableNodeID `json:"nodeId"` // e.g., "n123456CNTRL"
|
||||||
|
|
||||||
Start time.Time `json:"start"` // inclusive
|
Start time.Time `json:"start"` // inclusive
|
||||||
End time.Time `json:"end"` // inclusive
|
End time.Time `json:"end"` // inclusive
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"tailscale.com/logtail"
|
"tailscale.com/logtail"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/smallzstd"
|
"tailscale.com/smallzstd"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/netlogtype"
|
"tailscale.com/types/netlogtype"
|
||||||
"tailscale.com/wgengine/router"
|
"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.
|
// is a non-tailscale IP address to contact for that particular tailscale node.
|
||||||
// The IP protocol and source port are always zero.
|
// The IP protocol and source port are always zero.
|
||||||
// The sock is used to populated the PhysicalTraffic field in Message.
|
// 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()
|
nl.mu.Lock()
|
||||||
defer nl.mu.Unlock()
|
defer nl.mu.Unlock()
|
||||||
if nl.logger != nil {
|
if nl.logger != nil {
|
||||||
|
@ -110,8 +111,8 @@ func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device)
|
||||||
}
|
}
|
||||||
logger := logtail.NewLogger(logtail.Config{
|
logger := logtail.NewLogger(logtail.Config{
|
||||||
Collection: "tailtraffic.log.tailscale.io",
|
Collection: "tailtraffic.log.tailscale.io",
|
||||||
PrivateID: nodeID,
|
PrivateID: nodeLogID,
|
||||||
CopyPrivateID: domainID,
|
CopyPrivateID: domainLogID,
|
||||||
Stderr: io.Discard,
|
Stderr: io.Discard,
|
||||||
// TODO(joetsai): Set Buffer? Use an in-memory buffer for now.
|
// TODO(joetsai): Set Buffer? Use an in-memory buffer for now.
|
||||||
NewZstdEncoder: func() logtail.Encoder {
|
NewZstdEncoder: func() logtail.Encoder {
|
||||||
|
@ -161,7 +162,7 @@ func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device)
|
||||||
addrs := nl.addrs
|
addrs := nl.addrs
|
||||||
prefixes := nl.prefixes
|
prefixes := nl.prefixes
|
||||||
nl.mu.Unlock()
|
nl.mu.Unlock()
|
||||||
recordStatistics(logger, start, end, tunStats, sockStats, addrs, prefixes)
|
recordStatistics(logger, nodeID, start, end, tunStats, sockStats, addrs, prefixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
|
@ -174,8 +175,8 @@ func (nl *Logger) Startup(nodeID, domainID logtail.PrivateID, tun, sock Device)
|
||||||
return nil
|
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) {
|
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{Start: start.UTC(), End: end.UTC()}
|
m := netlogtype.Message{NodeID: nodeID, Start: start.UTC(), End: end.UTC()}
|
||||||
|
|
||||||
classifyAddr := func(a netip.Addr) (isTailscale, withinRoute bool) {
|
classifyAddr := func(a netip.Addr) (isTailscale, withinRoute bool) {
|
||||||
// NOTE: There could be mis-classifications where an address is treated
|
// NOTE: There could be mis-classifications where an address is treated
|
||||||
|
|
|
@ -58,7 +58,7 @@ func TestResourceCheck(t *testing.T) {
|
||||||
var l Logger
|
var l Logger
|
||||||
var d fakeDevice
|
var d fakeDevice
|
||||||
for i := 0; i < 10; i++ {
|
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{})
|
l.ReconfigRoutes(&router.Config{})
|
||||||
must.Do(l.Shutdown(context.Background()))
|
must.Do(l.Shutdown(context.Background()))
|
||||||
c.Assert(d.toggled, qt.Equals, 2*(i+1))
|
c.Assert(d.toggled, qt.Equals, 2*(i+1))
|
||||||
|
|
|
@ -953,7 +953,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
||||||
nid := cfg.NetworkLogging.NodeID
|
nid := cfg.NetworkLogging.NodeID
|
||||||
tid := cfg.NetworkLogging.DomainID
|
tid := cfg.NetworkLogging.DomainID
|
||||||
e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public())
|
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)
|
e.logf("wgengine: Reconfig: error starting up network logger: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"tailscale.com/logtail"
|
"tailscale.com/logtail"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
// It only supports the set of things Tailscale uses.
|
// It only supports the set of things Tailscale uses.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Name string
|
Name string
|
||||||
|
NodeID tailcfg.StableNodeID
|
||||||
PrivateKey key.NodePrivate
|
PrivateKey key.NodePrivate
|
||||||
Addresses []netip.Prefix
|
Addresses []netip.Prefix
|
||||||
MTU uint16
|
MTU uint16
|
||||||
|
|
|
@ -62,6 +62,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
|
||||||
|
|
||||||
// Setup log IDs for data plane audit logging.
|
// Setup log IDs for data plane audit logging.
|
||||||
if nm.SelfNode != nil {
|
if nm.SelfNode != nil {
|
||||||
|
cfg.NodeID = nm.SelfNode.StableID
|
||||||
canNetworkLog := slices.Contains(nm.SelfNode.Capabilities, tailcfg.CapabilityDataPlaneAuditLogs)
|
canNetworkLog := slices.Contains(nm.SelfNode.Capabilities, tailcfg.CapabilityDataPlaneAuditLogs)
|
||||||
if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID != "" && nm.DomainAuditLogID != "" {
|
if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID != "" && nm.DomainAuditLogID != "" {
|
||||||
nodeID, errNode := logtail.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID)
|
nodeID, errNode := logtail.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"tailscale.com/logtail"
|
"tailscale.com/logtail"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"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.
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||||
var _ConfigCloneNeedsRegeneration = Config(struct {
|
var _ConfigCloneNeedsRegeneration = Config(struct {
|
||||||
Name string
|
Name string
|
||||||
|
NodeID tailcfg.StableNodeID
|
||||||
PrivateKey key.NodePrivate
|
PrivateKey key.NodePrivate
|
||||||
Addresses []netip.Prefix
|
Addresses []netip.Prefix
|
||||||
MTU uint16
|
MTU uint16
|
||||||
|
|
Loading…
Reference in New Issue