policy/v2: surface autogroup:internet via grants on exit nodes

A grant of the form `{src: alice, dst: autogroup:internet, via:
tag:exit1}` was loading without error but stripping every exit node
from alice's view: `tailscale exit-node list` returned "no exit nodes
found".

Two sites skipped autogroup:internet at the compile / steering layer:
compileViaForNode's *AutoGroup arm produced no FilterRule for the
via-tagged exit node, and ViaRoutesForPeer's *AutoGroup arm produced
no Include/Exclude. With pm.needsPerNodeFilter true, the exit node's
matchers were empty, BuildPeerMap could not link source to exit, and
RoutesForPeer's ReduceRoutes stripped 0.0.0.0/0 and ::/0 from
AllowedIPs.

The skip belongs at the wire-format layer (ReduceFilterRules), not at
the compile layer that also feeds internal matchers. Lift
autogroup:internet handling into both *AutoGroup arms with the same
shape used for *Prefix destinations: emit a TheInternet rule on
via-tagged exit advertisers; surface peer.ExitRoutes() in Include
when the peer carries the via tag, Exclude otherwise.
ReduceFilterRules continues to keep the rule on exit-route
advertisers' wire output and strip it elsewhere, preserving SaaS
PacketFilter encoding.

Also drop compileViaForNode's early len(SubnetRoutes)==0 return:
SubnetRoutes excludes exit routes, so the early return pre-empted the
autogroup:internet branch on nodes that only advertise exit routes.

Existing tests pinning the buggy behaviour (TestViaRoutesForPeer
subtests, TestCompileViaGrant case) flipped to the new contract.

Fixes #3233
This commit is contained in:
Kristoffer Dalby 2026-04-30 11:40:29 +00:00
parent ecaf56e0a0
commit 2b7f15abaa
5 changed files with 177 additions and 25 deletions

View file

@ -903,10 +903,16 @@ func (pm *PolicyManager) ViaRoutesForPeer(viewer, peer types.NodeView) types.Via
matchedPrefixes = append(matchedPrefixes, dstPrefix)
}
case *AutoGroup:
// autogroup:internet via grants do NOT affect AllowedIPs or
// route steering for exit nodes. Tailscale SaaS handles exit
// traffic forwarding through the client's exit node selection
// mechanism, not through AllowedIPs.
// Per-viewer steering for autogroup:internet: a peer
// advertising approved exit routes is the via-tagged
// node's analogue of "advertises the destination".
// The downstream Include/Exclude split below restricts
// alice to exit nodes carrying the via tag.
if d.Is(AutoGroupInternet) && peer.IsExitNode() {
matchedPrefixes = append(
matchedPrefixes, peer.ExitRoutes()...,
)
}
}
}