policy/v2: use per-node filter compilation for via grants
Via grants compile filter rules that depend on the node's route state (SubnetRoutes, ExitRoutes). Without per-node compilation, these rules were only included in the global filter path which explicitly skips via grants (compileFilterRules skips grants with non-empty Via fields). Add a needsPerNodeFilter flag that is true when the policy uses either autogroup:self or via grants. filterForNodeLocked now uses this flag instead of usesAutogroupSelf alone, ensuring via grant rules are compiled per-node through compileFilterRulesForNode/compileViaGrant. The filter cache also needs to account for route-dependent compilation: - nodesHavePolicyAffectingChanges now treats route changes as policy-affecting when needsPerNodeFilter is true, so SetNodes triggers updateLocked and clears caches through the normal flow. - invalidateGlobalPolicyCache now clears compiledFilterRulesMap (the unreduced per-node cache) alongside filterRulesMap when needsPerNodeFilter is true and routes changed. Updates #2180
This commit is contained in:
parent
9db5fb6393
commit
ccd284c0a5
@ -51,6 +51,12 @@ type PolicyManager struct {
|
|||||||
// Lazy map of per-node filter rules (reduced, for packet filters)
|
// Lazy map of per-node filter rules (reduced, for packet filters)
|
||||||
filterRulesMap map[types.NodeID][]tailcfg.FilterRule
|
filterRulesMap map[types.NodeID][]tailcfg.FilterRule
|
||||||
usesAutogroupSelf bool
|
usesAutogroupSelf bool
|
||||||
|
|
||||||
|
// needsPerNodeFilter is true when filter rules must be compiled
|
||||||
|
// per-node rather than globally. This is required when the policy
|
||||||
|
// uses autogroup:self (node-relative destinations) or via grants
|
||||||
|
// (per-router filter rules for steered traffic).
|
||||||
|
needsPerNodeFilter bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterAndPolicy combines the compiled filter rules with policy content for hashing.
|
// filterAndPolicy combines the compiled filter rules with policy content for hashing.
|
||||||
@ -78,6 +84,7 @@ func NewPolicyManager(b []byte, users []types.User, nodes views.Slice[types.Node
|
|||||||
compiledFilterRulesMap: make(map[types.NodeID][]tailcfg.FilterRule, nodes.Len()),
|
compiledFilterRulesMap: make(map[types.NodeID][]tailcfg.FilterRule, nodes.Len()),
|
||||||
filterRulesMap: make(map[types.NodeID][]tailcfg.FilterRule, nodes.Len()),
|
filterRulesMap: make(map[types.NodeID][]tailcfg.FilterRule, nodes.Len()),
|
||||||
usesAutogroupSelf: policy.usesAutogroupSelf(),
|
usesAutogroupSelf: policy.usesAutogroupSelf(),
|
||||||
|
needsPerNodeFilter: policy.usesAutogroupSelf() || policy.hasViaGrants(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = pm.updateLocked()
|
_, err = pm.updateLocked()
|
||||||
@ -91,8 +98,9 @@ func NewPolicyManager(b []byte, users []types.User, nodes views.Slice[types.Node
|
|||||||
// updateLocked updates the filter rules based on the current policy and nodes.
|
// updateLocked updates the filter rules based on the current policy and nodes.
|
||||||
// It must be called with the lock held.
|
// It must be called with the lock held.
|
||||||
func (pm *PolicyManager) updateLocked() (bool, error) {
|
func (pm *PolicyManager) updateLocked() (bool, error) {
|
||||||
// Check if policy uses autogroup:self
|
// Check if policy uses autogroup:self or via grants
|
||||||
pm.usesAutogroupSelf = pm.pol.usesAutogroupSelf()
|
pm.usesAutogroupSelf = pm.pol.usesAutogroupSelf()
|
||||||
|
pm.needsPerNodeFilter = pm.usesAutogroupSelf || pm.pol.hasViaGrants()
|
||||||
|
|
||||||
var filter []tailcfg.FilterRule
|
var filter []tailcfg.FilterRule
|
||||||
|
|
||||||
@ -482,7 +490,7 @@ func (pm *PolicyManager) filterForNodeLocked(node types.NodeView) ([]tailcfg.Fil
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pm.usesAutogroupSelf {
|
if !pm.needsPerNodeFilter {
|
||||||
// For global filters, reduce to only rules relevant to this node.
|
// For global filters, reduce to only rules relevant to this node.
|
||||||
// Cache the reduced filter per node for efficiency.
|
// Cache the reduced filter per node for efficiency.
|
||||||
if rules, ok := pm.filterRulesMap[node.ID()]; ok {
|
if rules, ok := pm.filterRulesMap[node.ID()]; ok {
|
||||||
@ -497,7 +505,8 @@ func (pm *PolicyManager) filterForNodeLocked(node types.NodeView) ([]tailcfg.Fil
|
|||||||
return reducedFilter, nil
|
return reducedFilter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// For autogroup:self, compile per-node rules then reduce them.
|
// Per-node compilation is needed when the policy uses autogroup:self
|
||||||
|
// (node-relative destinations) or via grants (per-router filter rules).
|
||||||
// Check if we have cached reduced rules for this node.
|
// Check if we have cached reduced rules for this node.
|
||||||
if rules, ok := pm.filterRulesMap[node.ID()]; ok {
|
if rules, ok := pm.filterRulesMap[node.ID()]; ok {
|
||||||
return rules, nil
|
return rules, nil
|
||||||
@ -660,6 +669,14 @@ func (pm *PolicyManager) nodesHavePolicyAffectingChanges(newNodes views.Slice[ty
|
|||||||
if newNode.HasPolicyChange(oldNode) {
|
if newNode.HasPolicyChange(oldNode) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Via grants and autogroup:self compile filter rules per-node
|
||||||
|
// that depend on the node's route state (SubnetRoutes, ExitRoutes).
|
||||||
|
// Route changes are policy-affecting in this context because they
|
||||||
|
// alter which filter rules get generated for the via-designated node.
|
||||||
|
if pm.needsPerNodeFilter && newNode.HasNetworkChanges(oldNode) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -3084,3 +3084,19 @@ func (p *Policy) usesAutogroupSelf() bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasViaGrants returns true if any grant in the policy has a
|
||||||
|
// non-empty Via field, requiring per-node filter compilation.
|
||||||
|
func (p *Policy) hasViaGrants() bool {
|
||||||
|
if p == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, grant := range p.Grants {
|
||||||
|
if len(grant.Via) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user