diff --git a/hscontrol/mapper/builder.go b/hscontrol/mapper/builder.go index b85eb908..69171f7a 100644 --- a/hscontrol/mapper/builder.go +++ b/hscontrol/mapper/builder.go @@ -79,7 +79,23 @@ func (b *MapResponseBuilder) WithSelfNode() *MapResponseBuilder { tailnode, err := tailNode( nv, b.capVer, b.mapper.state, func(id types.NodeID) []netip.Prefix { - return policy.ReduceRoutes(nv, b.mapper.state.GetNodePrimaryRoutes(id), matchers) + // Get the peer node to check for exit routes + peer, ok := b.mapper.state.GetNodeByID(id) + if !ok { + return nil + } + + // Start with primary routes (subnet routes, but not exit routes) + routes := policy.ReduceRoutes(nv, b.mapper.state.GetNodePrimaryRoutes(id), matchers) + + // Also filter exit routes through policy + // Only add exit routes if the viewing node (self in this case) has permission to use them + if exitRoutes := peer.ExitRoutes(); len(exitRoutes) > 0 { + filteredExitRoutes := policy.ReduceRoutes(nv, exitRoutes, matchers) + routes = append(routes, filteredExitRoutes...) + } + + return routes }, b.mapper.cfg) if err != nil { @@ -254,7 +270,23 @@ func (b *MapResponseBuilder) buildTailPeers(peers views.Slice[types.NodeView]) ( tailPeers, err := tailNodes( changedViews, b.capVer, b.mapper.state, func(id types.NodeID) []netip.Prefix { - return policy.ReduceRoutes(node, b.mapper.state.GetNodePrimaryRoutes(id), matchers) + // Get the peer node to check for exit routes + peer, ok := b.mapper.state.GetNodeByID(id) + if !ok { + return nil + } + + // Start with primary routes (subnet routes, but not exit routes) + routes := policy.ReduceRoutes(node, b.mapper.state.GetNodePrimaryRoutes(id), matchers) + + // Also filter exit routes through policy + // Only add exit routes if the viewing node has permission to use them + if exitRoutes := peer.ExitRoutes(); len(exitRoutes) > 0 { + filteredExitRoutes := policy.ReduceRoutes(node, exitRoutes, matchers) + routes = append(routes, filteredExitRoutes...) + } + + return routes }, b.mapper.cfg) if err != nil { diff --git a/hscontrol/mapper/tail.go b/hscontrol/mapper/tail.go index 3a518d94..ab57e41b 100644 --- a/hscontrol/mapper/tail.go +++ b/hscontrol/mapper/tail.go @@ -88,9 +88,9 @@ func tailNode( } tags = lo.Uniq(tags) + // Get filtered routes (includes both primary routes and exit routes if allowed by policy) routes := primaryRouteFunc(node.ID()) allowed := append(addrs, routes...) - allowed = append(allowed, node.ExitRoutes()...) tsaddr.SortPrefixes(allowed) tNode := tailcfg.Node{ diff --git a/hscontrol/mapper/tail_test.go b/hscontrol/mapper/tail_test.go index ac96028e..8a142d16 100644 --- a/hscontrol/mapper/tail_test.go +++ b/hscontrol/mapper/tail_test.go @@ -137,10 +137,8 @@ func TestTailNode(t *testing.T) { ), Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")}, AllowedIPs: []netip.Prefix{ - tsaddr.AllIPv4(), netip.MustParsePrefix("192.168.0.0/24"), netip.MustParsePrefix("100.64.0.1/32"), - tsaddr.AllIPv6(), }, PrimaryRoutes: []netip.Prefix{ netip.MustParsePrefix("192.168.0.0/24"),