wgcfg.Config.NetworkLogging carried the network flow logging identity inside the WireGuard config, where it was unrelated to WireGuard; it lived there mainly so that identity changes would defeat Reconfig's ErrNoChanges check and reach the netlog startup/shutdown logic. Remove the field and move the whole netlog lifecycle into a new feature/netlog package, installed on the engine via the new wgengine.HookNewNetLogger hook, like other feature/* packages. The logging identity now comes from LocalBackend's current netmap via the widened NetLogSource interface (replacing Engine.SetNetLogNodeSource), so nmcfg no longer parses audit log IDs into the config. The engine still calls the hook before its ErrNoChanges return and before router.Set (to capture initial packets), and again after router.Set (to capture final packets), preserving the previous ordering. Core wgengine no longer imports wgengine/netlog, so minimal builds drop it entirely. tailscaled keeps netlog via feature/condregister, and tsnet imports feature/condregister/netlog explicitly to keep netlog enabled by default in tsnet-based binaries (tsidp, k8s-operator). This is pulled out of a future change that removes wgcfg.Config.Peers, to make that PR smaller. Updates #12542 Updates #12614 Change-Id: I41ca7dfe43c51e977c41b5f8e934bd1f0e6e6e24 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
125 lines
3.5 KiB
Go
125 lines
3.5 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package nmcfg converts a controlclient.NetMap into a wgcfg config.
|
|
package nmcfg
|
|
|
|
import (
|
|
"bufio"
|
|
"cmp"
|
|
"fmt"
|
|
"net/netip"
|
|
"strings"
|
|
|
|
"tailscale.com/net/tsaddr"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/key"
|
|
"tailscale.com/types/logger"
|
|
"tailscale.com/types/netmap"
|
|
"tailscale.com/wgengine/wgcfg"
|
|
)
|
|
|
|
func nodeDebugName(n tailcfg.NodeView) string {
|
|
name, _, _ := strings.Cut(cmp.Or(n.Name(), n.Hostinfo().Hostname()), ".")
|
|
return name
|
|
}
|
|
|
|
// cidrIsSubnet reports whether cidr is a non-default-route subnet
|
|
// exported by node that is not one of its own self addresses.
|
|
func cidrIsSubnet(node tailcfg.NodeView, cidr netip.Prefix) bool {
|
|
if cidr.Bits() == 0 {
|
|
return false
|
|
}
|
|
if !cidr.IsSingleIP() {
|
|
return true
|
|
}
|
|
if tsaddr.IsTailscaleIP(cidr.Addr()) {
|
|
return false
|
|
}
|
|
for _, selfCIDR := range node.Addresses().All() {
|
|
if cidr == selfCIDR {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// WGCfg returns the NetworkMaps's WireGuard configuration.
|
|
func WGCfg(pk key.NodePrivate, nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags, exitNode tailcfg.StableNodeID) (*wgcfg.Config, error) {
|
|
cfg := &wgcfg.Config{
|
|
PrivateKey: pk,
|
|
Addresses: nm.GetAddresses().AsSlice(),
|
|
Peers: make([]wgcfg.Peer, 0, len(nm.Peers)),
|
|
}
|
|
|
|
var skippedExitNode, skippedSubnetRouter, skippedExpired []tailcfg.NodeView
|
|
|
|
for _, peer := range nm.Peers {
|
|
if peer.DiscoKey().IsZero() && peer.HomeDERP() == 0 && !peer.IsWireGuardOnly() {
|
|
// Peer predates both DERP and active discovery, we cannot
|
|
// communicate with it.
|
|
logf("[v1] wgcfg: skipped peer %s, doesn't offer DERP or disco", peer.Key().ShortString())
|
|
continue
|
|
}
|
|
// Skip expired peers; we'll end up failing to connect to them
|
|
// anyway, since control intentionally breaks node keys for
|
|
// expired peers so that we can't discover endpoints via DERP.
|
|
if peer.Expired() {
|
|
skippedExpired = append(skippedExpired, peer)
|
|
continue
|
|
}
|
|
|
|
cfg.Peers = append(cfg.Peers, wgcfg.Peer{
|
|
PublicKey: peer.Key(),
|
|
DiscoKey: peer.DiscoKey(),
|
|
})
|
|
cpeer := &cfg.Peers[len(cfg.Peers)-1]
|
|
|
|
didExitNodeLog := false
|
|
cpeer.V4MasqAddr = peer.SelfNodeV4MasqAddrForThisPeer().Clone()
|
|
cpeer.V6MasqAddr = peer.SelfNodeV6MasqAddrForThisPeer().Clone()
|
|
cpeer.IsJailed = peer.IsJailed()
|
|
for _, allowedIP := range peer.AllowedIPs().All() {
|
|
if allowedIP.Bits() == 0 && peer.StableID() != exitNode {
|
|
if didExitNodeLog {
|
|
// Don't log about both the IPv4 /0 and IPv6 /0.
|
|
continue
|
|
}
|
|
didExitNodeLog = true
|
|
skippedExitNode = append(skippedExitNode, peer)
|
|
continue
|
|
} else if cidrIsSubnet(peer, allowedIP) {
|
|
if (flags & netmap.AllowSubnetRoutes) == 0 {
|
|
skippedSubnetRouter = append(skippedSubnetRouter, peer)
|
|
continue
|
|
}
|
|
}
|
|
cpeer.AllowedIPs = append(cpeer.AllowedIPs, allowedIP)
|
|
}
|
|
}
|
|
|
|
logList := func(title string, nodes []tailcfg.NodeView) {
|
|
if len(nodes) == 0 {
|
|
return
|
|
}
|
|
logf("[v1] wgcfg: %s from %d nodes: %s", title, len(nodes), logger.ArgWriter(func(bw *bufio.Writer) {
|
|
const max = 5
|
|
for i, n := range nodes {
|
|
if i == max {
|
|
fmt.Fprintf(bw, "... +%d", len(nodes)-max)
|
|
return
|
|
}
|
|
if i > 0 {
|
|
bw.WriteString(", ")
|
|
}
|
|
fmt.Fprintf(bw, "%s (%s)", nodeDebugName(n), n.StableID())
|
|
}
|
|
}))
|
|
}
|
|
logList("skipped unselected exit nodes", skippedExitNode)
|
|
logList("did not accept subnet routes", skippedSubnetRouter)
|
|
logList("skipped expired peers", skippedExpired)
|
|
|
|
return cfg, nil
|
|
}
|