From d40b25326ccdb111ce4e99893164dd6742328a52 Mon Sep 17 00:00:00 2001 From: Dylan Bargatze Date: Wed, 9 Jul 2025 18:06:58 -0400 Subject: [PATCH] tailcfg, wgengine/magicsock: disable all UDP relay usage if disable-relay-client is set (#16492) If the NodeAttrDisableRelayClient node attribute is set, ensures that a node cannot allocate endpoints on a UDP relay server itself, and cannot use newly-discovered paths (via disco/CallMeMaybeVia) that traverse a UDP relay server. Fixes tailscale/corp#30180 Signed-off-by: Dylan Bargatze --- tailcfg/tailcfg.go | 18 ++++++++++-------- wgengine/magicsock/magicsock.go | 10 +++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index d97f60a8a..6c88217de 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -2607,14 +2607,16 @@ const ( // only needs to be present in [NodeCapMap] to take effect. NodeAttrDisableRelayServer NodeCapability = "disable-relay-server" - // NodeAttrDisableRelayClient prevents the node from allocating UDP relay - // server endpoints itself; the node may still bind into and relay traffic - // using endpoints allocated by its peers. This attribute can be added to - // the node dynamically; if added while the node is already running, the - // node will be unable to allocate UDP relay server endpoints after it next - // updates its network map. There are no expected values for this key in - // [NodeCapMap]; the key only needs to be present in [NodeCapMap] to take - // effect. + // NodeAttrDisableRelayClient prevents the node from both allocating UDP + // relay server endpoints itself, and from using endpoints allocated by + // its peers. This attribute can be added to the node dynamically; if added + // while the node is already running, the node will be unable to allocate + // endpoints after it next updates its network map, and will be immediately + // unable to use new paths via a UDP relay server. Setting this attribute + // dynamically does not remove any existing paths, including paths that + // traverse a UDP relay server. There are no expected values for this key + // in [NodeCapMap]; the key only needs to be present in [NodeCapMap] to + // take effect. NodeAttrDisableRelayClient NodeCapability = "disable-relay-client" // NodeAttrMagicDNSPeerAAAA is a capability that tells the node's MagicDNS diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 1978867fa..582e74c8b 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -355,7 +355,7 @@ type Conn struct { self tailcfg.NodeView // from last onNodeViewsUpdate peers views.Slice[tailcfg.NodeView] // from last onNodeViewsUpdate, sorted by Node.ID; Note: [netmap.NodeMutation]'s rx'd in onNodeMutationsUpdate are never applied filt *filter.Filter // from last onFilterUpdate - relayClientEnabled bool // whether we can allocate UDP relay endpoints on UDP relay servers + relayClientEnabled bool // whether we can allocate UDP relay endpoints on UDP relay servers or receive CallMeMaybeVia messages from peers lastFlags debugFlags // at time of last onNodeViewsUpdate privateKey key.NodePrivate // WireGuard private key for this node everHadKey bool // whether we ever had a non-zero private key @@ -2149,6 +2149,14 @@ func (c *Conn) handleDiscoMessage(msg []byte, src epAddr, shouldBeRelayHandshake c.logf("magicsock: disco: ignoring %s from %v; %v is unknown", msgType, sender.ShortString(), derpNodeSrc.ShortString()) return } + // If the "disable-relay-client" node attr is set for this node, it + // can't be a UDP relay client, so drop any CallMeMaybeVia messages it + // receives. + if isVia && !c.relayClientEnabled { + c.logf("magicsock: disco: ignoring %s from %v; disable-relay-client node attr is set", msgType, sender.ShortString()) + return + } + ep.mu.Lock() relayCapable := ep.relayCapable lastBest := ep.bestAddr