mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	* notifier: use convenience funcs Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: reduce routes based on policy Fixes #2365 Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * hsic: more helper methods Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: more test cases Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * integration: add route with filter acl integration test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * integration: correct route reduce test, now failing Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * mapper: compare peer routes against node Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * hs: more output to debug strings Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * types/node: slice.ContainsFunc Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: more reduce route test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * changelog: add entry for route filter Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
		
			
				
	
	
		
			129 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			129 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/samber/lo"
 | 
						|
	"tailscale.com/net/tsaddr"
 | 
						|
	"tailscale.com/tailcfg"
 | 
						|
)
 | 
						|
 | 
						|
// ReduceNodes returns the list of peers authorized to be accessed from a given node.
 | 
						|
func ReduceNodes(
 | 
						|
	node *types.Node,
 | 
						|
	nodes types.Nodes,
 | 
						|
	matchers []matcher.Match,
 | 
						|
) types.Nodes {
 | 
						|
	var result types.Nodes
 | 
						|
 | 
						|
	for index, peer := range nodes {
 | 
						|
		if peer.ID == node.ID {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if node.CanAccess(matchers, nodes[index]) || peer.CanAccess(matchers, node) {
 | 
						|
			result = append(result, peer)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return result
 | 
						|
}
 | 
						|
 | 
						|
// ReduceRoutes returns a reduced list of routes for a given node that it can access.
 | 
						|
func ReduceRoutes(
 | 
						|
	node *types.Node,
 | 
						|
	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
 | 
						|
}
 | 
						|
 | 
						|
// ReduceFilterRules takes a node and a set of rules and removes all rules and destinations
 | 
						|
// that are not relevant to that particular node.
 | 
						|
func ReduceFilterRules(node *types.Node, rules []tailcfg.FilterRule) []tailcfg.FilterRule {
 | 
						|
	ret := []tailcfg.FilterRule{}
 | 
						|
 | 
						|
	for _, rule := range rules {
 | 
						|
		// record if the rule is actually relevant for the given node.
 | 
						|
		var dests []tailcfg.NetPortRange
 | 
						|
	DEST_LOOP:
 | 
						|
		for _, dest := range rule.DstPorts {
 | 
						|
			expanded, err := util.ParseIPSet(dest.IP, nil)
 | 
						|
			// Fail closed, if we can't parse it, then we should not allow
 | 
						|
			// access.
 | 
						|
			if err != nil {
 | 
						|
				continue DEST_LOOP
 | 
						|
			}
 | 
						|
 | 
						|
			if node.InIPSet(expanded) {
 | 
						|
				dests = append(dests, dest)
 | 
						|
				continue DEST_LOOP
 | 
						|
			}
 | 
						|
 | 
						|
			// If the node exposes routes, ensure they are note removed
 | 
						|
			// when the filters are reduced.
 | 
						|
			if node.Hostinfo != nil {
 | 
						|
				if len(node.Hostinfo.RoutableIPs) > 0 {
 | 
						|
					for _, routableIP := range node.Hostinfo.RoutableIPs {
 | 
						|
						if expanded.OverlapsPrefix(routableIP) {
 | 
						|
							dests = append(dests, dest)
 | 
						|
							continue DEST_LOOP
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if len(dests) > 0 {
 | 
						|
			ret = append(ret, tailcfg.FilterRule{
 | 
						|
				SrcIPs:   rule.SrcIPs,
 | 
						|
				DstPorts: dests,
 | 
						|
				IPProto:  rule.IPProto,
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
// AutoApproveRoutes approves any route that can be autoapproved from
 | 
						|
// the nodes perspective according to the given policy.
 | 
						|
// It reports true if any routes were approved.
 | 
						|
func AutoApproveRoutes(pm PolicyManager, node *types.Node) bool {
 | 
						|
	if pm == nil {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	var newApproved []netip.Prefix
 | 
						|
	for _, route := range node.AnnouncedRoutes() {
 | 
						|
		if pm.NodeCanApproveRoute(node, route) {
 | 
						|
			newApproved = append(newApproved, route)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if newApproved != nil {
 | 
						|
		newApproved = append(newApproved, node.ApprovedRoutes...)
 | 
						|
		tsaddr.SortPrefixes(newApproved)
 | 
						|
		newApproved = slices.Compact(newApproved)
 | 
						|
		newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool {
 | 
						|
			return route.IsValid()
 | 
						|
		})
 | 
						|
		node.ApprovedRoutes = newApproved
 | 
						|
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	return false
 | 
						|
}
 |