mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-29 03:26:21 +09:00
9fbf7b9b60
Fixes #3408
0.29's BuildPeerMap returns node views, not node IDs, so the peer map
assertion compares IDs read off the views.
(cherry picked from commit e48bc46cc6)
103 lines
3.0 KiB
Go
103 lines
3.0 KiB
Go
package policy
|
|
|
|
import (
|
|
"net/netip"
|
|
"slices"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
"github.com/juanfont/headscale/hscontrol/util"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// ReduceRoutes returns a reduced list of routes for a given node that it can access.
|
|
func ReduceRoutes(
|
|
node types.NodeView,
|
|
routes []netip.Prefix,
|
|
matchers []matcher.Match,
|
|
) []netip.Prefix {
|
|
var result []netip.Prefix
|
|
|
|
for _, route := range routes {
|
|
if node.CanAccessRoute(matchers, route) {
|
|
result = append(result, route)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ApproveRoutesWithPolicy checks if the node can approve the announced routes
|
|
// and returns the new list of approved routes. The [PolicyManager] is consulted
|
|
// via [PolicyManager.NodeCanApproveRoute].
|
|
// The approved routes will include:
|
|
// 1. ALL previously approved routes (regardless of whether they're still advertised)
|
|
// 2. New routes from announcedRoutes that can be auto-approved by policy
|
|
// This ensures that:
|
|
// - Previously approved routes are ALWAYS preserved (auto-approval never removes routes)
|
|
// - New routes can be auto-approved according to policy
|
|
// - Routes can only be removed by explicit admin action (not by auto-approval).
|
|
func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApproved, announcedRoutes []netip.Prefix) ([]netip.Prefix, bool) {
|
|
if pm == nil {
|
|
return currentApproved, false
|
|
}
|
|
|
|
// Start with ALL currently approved routes - we never remove approved routes
|
|
newApproved := make([]netip.Prefix, len(currentApproved))
|
|
copy(newApproved, currentApproved)
|
|
|
|
// Then, check for new routes that can be auto-approved
|
|
for _, route := range announcedRoutes {
|
|
// Skip if already approved
|
|
if slices.Contains(newApproved, route) {
|
|
continue
|
|
}
|
|
|
|
// Check if this new route can be auto-approved by policy
|
|
canApprove := pm.NodeCanApproveRoute(nv, route)
|
|
if canApprove {
|
|
newApproved = append(newApproved, route)
|
|
}
|
|
}
|
|
|
|
// Sort and deduplicate
|
|
slices.SortFunc(newApproved, netip.Prefix.Compare)
|
|
newApproved = slices.Compact(newApproved)
|
|
newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool {
|
|
return route.IsValid()
|
|
})
|
|
|
|
// Sort the current approved for comparison
|
|
sortedCurrent := make([]netip.Prefix, len(currentApproved))
|
|
copy(sortedCurrent, currentApproved)
|
|
slices.SortFunc(sortedCurrent, netip.Prefix.Compare)
|
|
|
|
// Only update if the routes actually changed
|
|
if !slices.Equal(sortedCurrent, newApproved) {
|
|
// Log what changed
|
|
var added, kept []netip.Prefix
|
|
|
|
for _, route := range newApproved {
|
|
if !slices.Contains(sortedCurrent, route) {
|
|
added = append(added, route)
|
|
} else {
|
|
kept = append(kept, route)
|
|
}
|
|
}
|
|
|
|
if len(added) > 0 {
|
|
log.Debug().
|
|
EmbedObject(nv).
|
|
Strs("routes.added", util.PrefixesToString(added)).
|
|
Strs("routes.kept", util.PrefixesToString(kept)).
|
|
Int("routes.total", len(newApproved)).
|
|
Msg("Routes auto-approved by policy")
|
|
}
|
|
|
|
return newApproved, true
|
|
}
|
|
|
|
return newApproved, false
|
|
}
|