mirror of
https://github.com/juanfont/headscale.git
synced 2026-07-12 10:59:08 +09:00
types: consider subnet routes as source identity in ACL matching
CanAccess and CanAccessRoute now check approved subnet routes alongside Tailscale IPs when matching ACL source sets. Fixes #3157 Fixes #3169
This commit is contained in:
+49
-13
@@ -324,23 +324,39 @@ func (node *Node) CanAccess(matchers []matcher.Match, node2 *Node) bool {
|
||||
allowedIPs := node2.IPs()
|
||||
|
||||
for _, matcher := range matchers {
|
||||
if !matcher.SrcsContainsIPs(src...) {
|
||||
continue
|
||||
if matcher.SrcsContainsIPs(src...) {
|
||||
if matcher.DestsContainsIP(allowedIPs...) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the node has access to routes that might be part of a
|
||||
// smaller subnet that is served from node2 as a subnet router.
|
||||
if matcher.DestsOverlapsPrefixes(node2.SubnetRoutes()...) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the dst is "the internet" and node2 is an exit node, allow access.
|
||||
if matcher.DestsIsTheInternet() && node2.IsExitNode() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if matcher.DestsContainsIP(allowedIPs...) {
|
||||
return true
|
||||
}
|
||||
// A subnet router acts on behalf of its advertised subnets.
|
||||
// If the node's approved subnet routes overlap the source set,
|
||||
// check whether node2 (or its subnets) is in the destination set.
|
||||
if srcRoutes := node.SubnetRoutes(); len(srcRoutes) > 0 &&
|
||||
matcher.SrcsOverlapsPrefixes(srcRoutes...) {
|
||||
if matcher.DestsContainsIP(allowedIPs...) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the node has access to routes that might be part of a
|
||||
// smaller subnet that is served from node2 as a subnet router.
|
||||
if matcher.DestsOverlapsPrefixes(node2.SubnetRoutes()...) {
|
||||
return true
|
||||
}
|
||||
if matcher.DestsOverlapsPrefixes(node2.SubnetRoutes()...) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the dst is "the internet" and node2 is an exit node, allow access.
|
||||
if matcher.DestsIsTheInternet() && node2.IsExitNode() {
|
||||
return true
|
||||
if matcher.DestsIsTheInternet() && node2.IsExitNode() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +365,7 @@ func (node *Node) CanAccess(matchers []matcher.Match, node2 *Node) bool {
|
||||
|
||||
func (node *Node) CanAccessRoute(matchers []matcher.Match, route netip.Prefix) bool {
|
||||
src := node.IPs()
|
||||
subnetRoutes := node.SubnetRoutes()
|
||||
|
||||
for _, matcher := range matchers {
|
||||
if matcher.SrcsContainsIPs(src...) && matcher.DestsOverlapsPrefixes(route) {
|
||||
@@ -358,6 +375,25 @@ func (node *Node) CanAccessRoute(matchers []matcher.Match, route netip.Prefix) b
|
||||
if matcher.SrcsOverlapsPrefixes(route) && matcher.DestsContainsIP(src...) {
|
||||
return true
|
||||
}
|
||||
|
||||
// A subnet router acts on behalf of its advertised subnets.
|
||||
// If the node's approved subnet routes overlap the source set
|
||||
// and the route overlaps the destination set, the router needs
|
||||
// this route to forward traffic from its local subnet.
|
||||
if len(subnetRoutes) > 0 {
|
||||
if matcher.SrcsOverlapsPrefixes(subnetRoutes...) &&
|
||||
matcher.DestsOverlapsPrefixes(route) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Reverse: traffic from the route's subnet is destined for
|
||||
// this node's subnets; the router needs the route for return
|
||||
// traffic.
|
||||
if matcher.SrcsOverlapsPrefixes(route) &&
|
||||
matcher.DestsOverlapsPrefixes(subnetRoutes...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user