mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-07 21:47:46 +09:00
policy/v2: preserve non-wildcard source IPs alongside wildcard ranges
When an ACL source list contains a wildcard (*) alongside explicit sources (tags, groups, hosts, etc.), Tailscale preserves the individual IPs from non-wildcard sources in SrcIPs alongside the merged wildcard CGNAT ranges. Previously, headscale's IPSetBuilder would merge all sources into a single set, absorbing the explicit IPs into the wildcard range. Track non-wildcard resolved addresses separately during source resolution, then append their individual IP strings to the output when a wildcard is also present. This fixes the remaining 5 ACL compat test failures (K01 and M06 subtests). Updates #2180
This commit is contained in:
@@ -232,8 +232,13 @@ func (pol *Policy) compileGrantWithAutogroupSelf(
|
||||
var rules []tailcfg.FilterRule
|
||||
|
||||
var resolvedSrcs []ResolvedAddresses
|
||||
// Track non-wildcard source IPs separately. When the grant has a
|
||||
// wildcard (*) source plus explicit sources (tags, groups, etc.),
|
||||
// Tailscale preserves the explicit IPs alongside the wildcard
|
||||
// CGNAT ranges rather than merging them into the IPSet.
|
||||
var nonWildcardSrcs []ResolvedAddresses
|
||||
|
||||
for _, src := range grant.Sources {
|
||||
for i, src := range grant.Sources {
|
||||
if ag, ok := src.(*AutoGroup); ok && ag.Is(AutoGroupSelf) {
|
||||
return nil, errSelfInSources
|
||||
}
|
||||
@@ -245,6 +250,9 @@ func (pol *Policy) compileGrantWithAutogroupSelf(
|
||||
|
||||
if ips != nil {
|
||||
resolvedSrcs = append(resolvedSrcs, ips)
|
||||
if _, isWildcard := grant.Sources[i].(Asterix); !isWildcard {
|
||||
nonWildcardSrcs = append(nonWildcardSrcs, ips)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,8 +283,31 @@ func (pol *Policy) compileGrantWithAutogroupSelf(
|
||||
destPorts := pol.destinationsToNetPortRange(users, nodes, otherDests, ipp.Ports)
|
||||
|
||||
if len(destPorts) > 0 {
|
||||
srcIPStrs := srcIPsWithRoutes(srcResolved, hasWildcard, nodes)
|
||||
|
||||
// When sources include a wildcard (*) alongside
|
||||
// explicit sources (tags, groups, etc.), Tailscale
|
||||
// preserves the individual IPs from non-wildcard
|
||||
// sources alongside the merged wildcard CGNAT
|
||||
// ranges rather than absorbing them.
|
||||
if hasWildcard && len(nonWildcardSrcs) > 0 {
|
||||
seen := make(map[string]bool, len(srcIPStrs))
|
||||
for _, s := range srcIPStrs {
|
||||
seen[s] = true
|
||||
}
|
||||
|
||||
for _, ips := range nonWildcardSrcs {
|
||||
for _, s := range ips.Strings() {
|
||||
if !seen[s] {
|
||||
seen[s] = true
|
||||
srcIPStrs = append(srcIPStrs, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rules = append(rules, tailcfg.FilterRule{
|
||||
SrcIPs: srcIPsWithRoutes(srcResolved, hasWildcard, nodes),
|
||||
SrcIPs: srcIPStrs,
|
||||
DstPorts: destPorts,
|
||||
IPProto: ipp.Protocol.toIANAProtocolNumbers(),
|
||||
})
|
||||
|
||||
@@ -1897,6 +1897,7 @@ func aclToGrants(acl ACL) []Grant {
|
||||
// preserve policy order, but ACLs with groups, users, tags, or
|
||||
// hosts emit non-self rules first.
|
||||
hasNonAutogroup := false
|
||||
|
||||
for _, dst := range acl.Destinations {
|
||||
if _, ok := dst.Alias.(*AutoGroup); !ok {
|
||||
hasNonAutogroup = true
|
||||
|
||||
Reference in New Issue
Block a user