Browse Source

wgengine/wgcfg: plumb down audit log IDs (#5855)

The node and domain audit log IDs are provided in the map response,
but are ultimately going to be used in wgengine since
that's the layer that manages the tstun.Wrapper.

Do the plumbing work to get this field passed down the stack.

Signed-off-by: Joe Tsai <[email protected]>
Joe Tsai 3 years ago
parent
commit
82f5f438e0

+ 19 - 14
control/controlclient/map.go

@@ -45,6 +45,7 @@ type mapSession struct {
 	collectServices        bool
 	previousPeers          []*tailcfg.Node // for delta-purposes
 	lastDomain             string
+	lastDomainAuditLogID   string
 	lastHealth             []string
 	lastPopBrowserURL      string
 	stickyDebug            tailcfg.Debug // accumulated opt.Bool values
@@ -113,6 +114,9 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
 	if resp.Domain != "" {
 		ms.lastDomain = resp.Domain
 	}
+	if resp.DomainDataPlaneAuditLogID != "" {
+		ms.lastDomainAuditLogID = resp.DomainDataPlaneAuditLogID
+	}
 	if resp.Health != nil {
 		ms.lastHealth = resp.Health
 	}
@@ -143,20 +147,21 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
 	}
 
 	nm := &netmap.NetworkMap{
-		NodeKey:         ms.privateNodeKey.Public(),
-		PrivateKey:      ms.privateNodeKey,
-		MachineKey:      ms.machinePubKey,
-		Peers:           resp.Peers,
-		UserProfiles:    make(map[tailcfg.UserID]tailcfg.UserProfile),
-		Domain:          ms.lastDomain,
-		DNS:             *ms.lastDNSConfig,
-		PacketFilter:    ms.lastParsedPacketFilter,
-		SSHPolicy:       ms.lastSSHPolicy,
-		CollectServices: ms.collectServices,
-		DERPMap:         ms.lastDERPMap,
-		Debug:           debug,
-		ControlHealth:   ms.lastHealth,
-		TKAEnabled:      ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled,
+		NodeKey:          ms.privateNodeKey.Public(),
+		PrivateKey:       ms.privateNodeKey,
+		MachineKey:       ms.machinePubKey,
+		Peers:            resp.Peers,
+		UserProfiles:     make(map[tailcfg.UserID]tailcfg.UserProfile),
+		Domain:           ms.lastDomain,
+		DomainAuditLogID: ms.lastDomainAuditLogID,
+		DNS:              *ms.lastDNSConfig,
+		PacketFilter:     ms.lastParsedPacketFilter,
+		SSHPolicy:        ms.lastSSHPolicy,
+		CollectServices:  ms.collectServices,
+		DERPMap:          ms.lastDERPMap,
+		Debug:            debug,
+		ControlHealth:    ms.lastHealth,
+		TKAEnabled:       ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled,
 	}
 	ms.netMapBuilding = nm
 

+ 5 - 0
types/netmap/netmap.go

@@ -76,6 +76,11 @@ type NetworkMap struct {
 	// Domain is the current Tailnet name.
 	Domain string
 
+	// DomainAuditLogID is an audit log ID provided by control and
+	// only populated if the domain opts into data-plane audit logging.
+	// If this is empty, then data-plane audit logging is disabled.
+	DomainAuditLogID string
+
 	UserProfiles map[tailcfg.UserID]tailcfg.UserProfile
 }
 

+ 8 - 0
wgengine/wgcfg/config.go

@@ -8,6 +8,7 @@ package wgcfg
 import (
 	"net/netip"
 
+	"tailscale.com/logtail"
 	"tailscale.com/types/key"
 )
 
@@ -22,6 +23,13 @@ type Config struct {
 	MTU        uint16
 	DNS        []netip.Addr
 	Peers      []Peer
+
+	// NetworkLogging enables network logging.
+	// It is disabled if either ID is the zero value.
+	NetworkLogging struct {
+		NodeID   logtail.PrivateID
+		DomainID logtail.PrivateID
+	}
 }
 
 type Peer struct {

+ 21 - 0
wgengine/wgcfg/nmcfg/nmcfg.go

@@ -11,6 +11,8 @@ import (
 	"net/netip"
 	"strings"
 
+	"golang.org/x/exp/slices"
+	"tailscale.com/logtail"
 	"tailscale.com/net/tsaddr"
 	"tailscale.com/tailcfg"
 	"tailscale.com/types/logger"
@@ -58,6 +60,25 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
 		Peers:      make([]wgcfg.Peer, 0, len(nm.Peers)),
 	}
 
+	// Setup log IDs for data plane audit logging.
+	if nm.SelfNode != nil {
+		canNetworkLog := slices.Contains(nm.SelfNode.Capabilities, tailcfg.CapabilityDataPlaneAuditLogs)
+		if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID != "" && nm.DomainAuditLogID != "" {
+			nodeID, errNode := logtail.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID)
+			if errNode != nil {
+				logf("[v1] wgcfg: unable to parse node audit log ID: %v", errNode)
+			}
+			domainID, errDomain := logtail.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
+			}
+		}
+	}
+
 	// Logging buffers
 	skippedUnselected := new(bytes.Buffer)
 	skippedIPs := new(bytes.Buffer)

+ 11 - 6
wgengine/wgcfg/wgcfg_clone.go

@@ -9,6 +9,7 @@ package wgcfg
 import (
 	"net/netip"
 
+	"tailscale.com/logtail"
 	"tailscale.com/types/key"
 )
 
@@ -31,12 +32,16 @@ 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
-	PrivateKey key.NodePrivate
-	Addresses  []netip.Prefix
-	MTU        uint16
-	DNS        []netip.Addr
-	Peers      []Peer
+	Name           string
+	PrivateKey     key.NodePrivate
+	Addresses      []netip.Prefix
+	MTU            uint16
+	DNS            []netip.Addr
+	Peers          []Peer
+	NetworkLogging struct {
+		NodeID   logtail.PrivateID
+		DomainID logtail.PrivateID
+	}
 }{})
 
 // Clone makes a deep copy of Peer.