mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-20 07:41:31 +02:00
policy/v2: reorder ACL self grants to match Tailscale rule ordering
When an ACL has non-autogroup destinations (groups, users, tags, hosts) alongside autogroup:self, emit non-self grants before self grants to match Tailscale's filter rule ordering. ACLs with only autogroup destinations (self + member) preserve the policy-defined order. This fixes ACL-A17, ACL-SF07, and ACL-SF11 compat test failures. Updates #2180
This commit is contained in:
@@ -1891,17 +1891,64 @@ type Grant struct {
|
|||||||
func aclToGrants(acl ACL) []Grant {
|
func aclToGrants(acl ACL) []Grant {
|
||||||
ret := make([]Grant, 0, len(acl.Destinations))
|
ret := make([]Grant, 0, len(acl.Destinations))
|
||||||
|
|
||||||
|
// Check if the ACL has any non-autogroup destinations. If so,
|
||||||
|
// reorder to place non-self grants before self grants. This matches
|
||||||
|
// Tailscale's behavior where autogroup-only ACLs (self + member)
|
||||||
|
// preserve policy order, but ACLs with groups, users, tags, or
|
||||||
|
// hosts emit non-self rules first.
|
||||||
|
hasNonAutogroup := false
|
||||||
for _, dst := range acl.Destinations {
|
for _, dst := range acl.Destinations {
|
||||||
g := Grant{
|
if _, ok := dst.Alias.(*AutoGroup); !ok {
|
||||||
Sources: acl.Sources,
|
hasNonAutogroup = true
|
||||||
Destinations: Aliases{dst.Alias},
|
|
||||||
InternetProtocols: []ProtocolPort{{
|
break
|
||||||
Protocol: acl.Protocol,
|
}
|
||||||
Ports: dst.Ports,
|
}
|
||||||
}},
|
|
||||||
|
if hasNonAutogroup {
|
||||||
|
// Non-self destinations first, self destinations second.
|
||||||
|
for _, dst := range acl.Destinations {
|
||||||
|
if ag, ok := dst.Alias.(*AutoGroup); ok && ag.Is(AutoGroupSelf) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, Grant{
|
||||||
|
Sources: acl.Sources,
|
||||||
|
Destinations: Aliases{dst.Alias},
|
||||||
|
InternetProtocols: []ProtocolPort{{
|
||||||
|
Protocol: acl.Protocol,
|
||||||
|
Ports: dst.Ports,
|
||||||
|
}},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = append(ret, g)
|
for _, dst := range acl.Destinations {
|
||||||
|
ag, ok := dst.Alias.(*AutoGroup)
|
||||||
|
if !ok || !ag.Is(AutoGroupSelf) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, Grant{
|
||||||
|
Sources: acl.Sources,
|
||||||
|
Destinations: Aliases{dst.Alias},
|
||||||
|
InternetProtocols: []ProtocolPort{{
|
||||||
|
Protocol: acl.Protocol,
|
||||||
|
Ports: dst.Ports,
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// All-autogroup ACL: preserve policy order.
|
||||||
|
for _, dst := range acl.Destinations {
|
||||||
|
ret = append(ret, Grant{
|
||||||
|
Sources: acl.Sources,
|
||||||
|
Destinations: Aliases{dst.Alias},
|
||||||
|
InternetProtocols: []ProtocolPort{{
|
||||||
|
Protocol: acl.Protocol,
|
||||||
|
Ports: dst.Ports,
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
Reference in New Issue
Block a user