mirror of
https://github.com/juanfont/headscale.git
synced 2026-02-07 20:04:00 +01:00
policy: autogroup:internet does not generate packet filters
According to Tailscale SaaS behavior, autogroup:internet is handled by exit node routing via AllowedIPs, not by packet filtering. ACL rules with autogroup:internet as destination should produce no filter rules for any node. Previously, Headscale expanded autogroup:internet to public CIDR ranges and distributed filters to exit nodes (because 0.0.0.0/0 "covers" internet destinations). This was incorrect. Add detection for AutoGroupInternet in filter compilation to skip filter generation for this autogroup. Update test expectations accordingly.
This commit is contained in:
parent
4996ce5cc2
commit
9008ce77fb
@ -891,9 +891,11 @@ func TestReduceNodesFromPolicy(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
node: n(1, "100.64.0.1", "mobile", "mobile"),
|
||||
// autogroup:internet does not generate packet filters - it's handled
|
||||
// by exit node routing via AllowedIPs, not by packet filtering.
|
||||
// Only server is visible through the mobile -> server:80 rule.
|
||||
want: types.Nodes{
|
||||
n(2, "100.64.0.2", "server", "server"),
|
||||
n(3, "100.64.0.3", "exit", "server", "0.0.0.0/0", "::/0"),
|
||||
},
|
||||
wantMatchers: 1,
|
||||
},
|
||||
|
||||
@ -353,10 +353,12 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
// Merged: Both ACL rules combined (same SrcIPs and IPProto)
|
||||
// Only the internal:* rule generates filters.
|
||||
// autogroup:internet does NOT generate packet filters - it's handled
|
||||
// by exit node routing via AllowedIPs, not by packet filtering.
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
DstPorts: append([]tailcfg.NetPortRange{
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "100.64.0.100/32",
|
||||
Ports: tailcfg.PortRangeAny,
|
||||
@ -365,7 +367,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
IP: "fd7a:115c:a1e0::100/128",
|
||||
Ports: tailcfg.PortRangeAny,
|
||||
},
|
||||
}, hsExitNodeDestForTest...),
|
||||
},
|
||||
IPProto: []int{v2.ProtocolTCP, v2.ProtocolUDP, v2.ProtocolICMP, v2.ProtocolIPv6ICMP},
|
||||
},
|
||||
},
|
||||
|
||||
@ -61,6 +61,12 @@ func (pol *Policy) compileFilterRules(
|
||||
continue
|
||||
}
|
||||
|
||||
// autogroup:internet does not generate packet filters - it's handled
|
||||
// by exit node routing via AllowedIPs, not by packet filtering.
|
||||
if ag, isAutoGroup := dest.Alias.(*AutoGroup); isAutoGroup && ag.Is(AutoGroupInternet) {
|
||||
continue
|
||||
}
|
||||
|
||||
ips, err := dest.Resolve(pol, users, nodes)
|
||||
if err != nil {
|
||||
log.Trace().Caller().Err(err).Msgf("resolving destination ips")
|
||||
@ -259,6 +265,12 @@ func (pol *Policy) compileACLWithAutogroupSelf(
|
||||
continue
|
||||
}
|
||||
|
||||
// autogroup:internet does not generate packet filters - it's handled
|
||||
// by exit node routing via AllowedIPs, not by packet filtering.
|
||||
if ag, isAutoGroup := dest.Alias.(*AutoGroup); isAutoGroup && ag.Is(AutoGroupInternet) {
|
||||
continue
|
||||
}
|
||||
|
||||
ips, err := dest.Resolve(pol, users, nodes)
|
||||
if err != nil {
|
||||
log.Trace().Caller().Err(err).Msgf("resolving destination ips")
|
||||
|
||||
@ -1124,31 +1124,15 @@ func TestTailscaleRoutesCompatExitNodes(t *testing.T) {
|
||||
"user1": wildcardFilter,
|
||||
},
|
||||
},
|
||||
// TODO: Fix autogroup:internet to not generate filters
|
||||
//
|
||||
// B8: autogroup:internet generates no filters
|
||||
//
|
||||
// TAILSCALE BEHAVIOR:
|
||||
// - autogroup:internet is handled by exit node routing, not packet filters
|
||||
// - ALL nodes should get null/empty filters for autogroup:internet destination
|
||||
// - Traffic is routed through exit nodes via AllowedIPs, not filtered
|
||||
//
|
||||
// HEADSCALE BEHAVIOR:
|
||||
// - Exit nodes (exit-node, multi-router) incorrectly receive filters
|
||||
// - Because 0.0.0.0/0 "covers" autogroup:internet destinations
|
||||
//
|
||||
// ROOT CAUSE:
|
||||
// Headscale treats autogroup:internet like a regular destination and
|
||||
// distributes filters to nodes whose routes cover it (exit nodes)
|
||||
//
|
||||
// FIX REQUIRED:
|
||||
// autogroup:internet should never generate packet filters
|
||||
// autogroup:internet is handled by exit node routing via AllowedIPs,
|
||||
// not by packet filtering. ALL nodes should get null/empty filters.
|
||||
{
|
||||
name: "B8_autogroup_internet_no_filters",
|
||||
policy: makeRoutesPolicy(`
|
||||
{"action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:internet:*"]}
|
||||
`),
|
||||
/* EXPECTED (Tailscale):
|
||||
wantFilters: map[string][]tailcfg.FilterRule{
|
||||
"client1": nil,
|
||||
"client2": nil,
|
||||
@ -1160,26 +1144,6 @@ func TestTailscaleRoutesCompatExitNodes(t *testing.T) {
|
||||
"big-router": nil,
|
||||
"user1": nil,
|
||||
},
|
||||
*/
|
||||
// ACTUAL (Headscale):
|
||||
// Non-exit nodes correctly get nil.
|
||||
// INCORRECT: exit-node and multi-router get filters with expanded public
|
||||
// CIDR ranges from util.TheInternet() (all public IPs excluding CGNAT,
|
||||
// private ranges, and Tailscale ULA). The exact CIDRs are complex and
|
||||
// impractical to list here. Skipping comparison for exit nodes but
|
||||
// documenting the difference: Tailscale returns nil, Headscale returns
|
||||
// filters with SrcIPs=member IPs and DstPorts=expanded public CIDRs.
|
||||
wantFilters: map[string][]tailcfg.FilterRule{
|
||||
"client1": nil,
|
||||
"client2": nil,
|
||||
"subnet-router": nil,
|
||||
"ha-router1": nil,
|
||||
"ha-router2": nil,
|
||||
"big-router": nil,
|
||||
"user1": nil,
|
||||
// exit-node and multi-router omitted - they incorrectly receive filters
|
||||
// with expanded autogroup:internet CIDRs in Headscale (Tailscale: nil)
|
||||
},
|
||||
},
|
||||
// B3: Exit node advertises exit routes (verify RoutableIPs)
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user