policy/v2: add via exit steering golden captures and tests
Add golden test data for via exit route steering and fix via exit grant compilation to match Tailscale SaaS behavior. Includes MapResponse golden tests for via grant route steering verification. Updates #2180
This commit is contained in:
parent
bca6e6334d
commit
6a55f7d731
19 changed files with 241115 additions and 89 deletions
|
|
@ -363,9 +363,11 @@ func (pol *Policy) compileViaGrant(
|
|||
viaDstPrefixes = append(viaDstPrefixes, dstPrefix)
|
||||
}
|
||||
case *AutoGroup:
|
||||
if d.Is(AutoGroupInternet) && len(nodeExitRoutes) > 0 {
|
||||
viaDstPrefixes = append(viaDstPrefixes, nodeExitRoutes...)
|
||||
}
|
||||
// autogroup:internet via grants do NOT produce PacketFilter rules
|
||||
// on the exit node. Tailscale SaaS handles exit traffic forwarding
|
||||
// through the client's exit node selection mechanism (AllowedIPs +
|
||||
// ExitNodeOption), not through PacketFilter rules. Verified by
|
||||
// golden captures GRANT-V14 through GRANT-V36.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3710,7 +3710,11 @@ func TestCompileViaGrant(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "autogroup:internet with exit routes produces rules",
|
||||
// autogroup:internet via grants do NOT produce PacketFilter rules
|
||||
// on exit nodes. Tailscale SaaS handles exit traffic forwarding
|
||||
// through the client's exit node mechanism, not PacketFilter.
|
||||
// Verified by golden captures GRANT-V14 through GRANT-V36.
|
||||
name: "autogroup:internet with exit routes produces no rules",
|
||||
grant: Grant{
|
||||
Sources: Aliases{up("testuser@")},
|
||||
Destinations: Aliases{agp(string(AutoGroupInternet))},
|
||||
|
|
@ -3720,15 +3724,7 @@ func TestCompileViaGrant(t *testing.T) {
|
|||
node: exitNode,
|
||||
nodes: types.Nodes{exitNode, srcNode},
|
||||
pol: &Policy{},
|
||||
want: []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.10"},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{IP: "0.0.0.0/0", Ports: tailcfg.PortRangeAny},
|
||||
{IP: "::/0", Ports: tailcfg.PortRangeAny},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "autogroup:internet without exit routes returns nil",
|
||||
|
|
|
|||
|
|
@ -149,9 +149,118 @@ func setupGrantsCompatNodes(users types.Users) types.Nodes {
|
|||
IPv4: ptrAddr("100.85.66.106"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::7c37:426a"),
|
||||
Tags: []string{"tag:exit"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
},
|
||||
ApprovedRoutes: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
}
|
||||
|
||||
// --- New nodes for expanded via grant topology ---
|
||||
|
||||
nodeExitA := &types.Node{
|
||||
ID: 9,
|
||||
GivenName: "exit-a",
|
||||
IPv4: ptrAddr("100.124.195.93"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::7837:c35d"),
|
||||
Tags: []string{"tag:exit-a"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
},
|
||||
ApprovedRoutes: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
}
|
||||
|
||||
nodeExitB := &types.Node{
|
||||
ID: 10,
|
||||
GivenName: "exit-b",
|
||||
IPv4: ptrAddr("100.116.18.24"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::ff37:1218"),
|
||||
Tags: []string{"tag:exit-b"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
},
|
||||
ApprovedRoutes: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
}
|
||||
|
||||
nodeGroupA := &types.Node{
|
||||
ID: 11,
|
||||
GivenName: "group-a-client",
|
||||
IPv4: ptrAddr("100.107.162.14"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::a237:a20e"),
|
||||
Tags: []string{"tag:group-a"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
nodeGroupB := &types.Node{
|
||||
ID: 12,
|
||||
GivenName: "group-b-client",
|
||||
IPv4: ptrAddr("100.77.135.18"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::4b37:8712"),
|
||||
Tags: []string{"tag:group-b"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
nodeRouterA := &types.Node{
|
||||
ID: 13,
|
||||
GivenName: "router-a",
|
||||
IPv4: ptrAddr("100.109.43.124"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::a537:2b7c"),
|
||||
Tags: []string{"tag:router-a"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("10.44.0.0/16")},
|
||||
},
|
||||
ApprovedRoutes: []netip.Prefix{netip.MustParsePrefix("10.44.0.0/16")},
|
||||
}
|
||||
|
||||
nodeRouterB := &types.Node{
|
||||
ID: 14,
|
||||
GivenName: "router-b",
|
||||
IPv4: ptrAddr("100.65.172.123"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::5a37:ac7c"),
|
||||
Tags: []string{"tag:router-b"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("10.55.0.0/16")},
|
||||
},
|
||||
ApprovedRoutes: []netip.Prefix{netip.MustParsePrefix("10.55.0.0/16")},
|
||||
}
|
||||
|
||||
nodeMultiExitRouter := &types.Node{
|
||||
ID: 15,
|
||||
GivenName: "multi-exit-router",
|
||||
IPv4: ptrAddr("100.105.127.107"),
|
||||
IPv6: ptrAddr("fd7a:115c:a1e0::9537:7f6b"),
|
||||
Tags: []string{"tag:exit", "tag:router"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("10.33.0.0/16"),
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
},
|
||||
ApprovedRoutes: []netip.Prefix{
|
||||
netip.MustParsePrefix("10.33.0.0/16"),
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
}
|
||||
|
||||
return types.Nodes{
|
||||
nodeUser1,
|
||||
nodeUserKris,
|
||||
|
|
@ -161,6 +270,13 @@ func setupGrantsCompatNodes(users types.Users) types.Nodes {
|
|||
nodeTaggedClient,
|
||||
nodeSubnetRouter,
|
||||
nodeExitNode,
|
||||
nodeExitA,
|
||||
nodeExitB,
|
||||
nodeGroupA,
|
||||
nodeGroupB,
|
||||
nodeRouterA,
|
||||
nodeRouterB,
|
||||
nodeMultiExitRouter,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +380,7 @@ func TestGrantsCompat(t *testing.T) {
|
|||
t.Logf("Loaded %d grant test files", len(files))
|
||||
|
||||
users := setupGrantsCompatUsers()
|
||||
nodes := setupGrantsCompatNodes(users)
|
||||
allNodes := setupGrantsCompatNodes(users)
|
||||
|
||||
for _, file := range files {
|
||||
tf := loadGrantTestFile(t, file)
|
||||
|
|
@ -278,6 +394,16 @@ func TestGrantsCompat(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
// Determine which node set to use based on the test's topology.
|
||||
// Tests captured with the expanded 15-node topology (V26+) have
|
||||
// nodes like exit-a, group-a-client, etc. Tests from the original
|
||||
// 8-node topology should only use the first 8 nodes to avoid
|
||||
// resolving extra IPs from nodes that weren't present during capture.
|
||||
nodes := allNodes
|
||||
if _, hasNewNodes := tf.Captures["exit-a"]; !hasNewNodes {
|
||||
nodes = allNodes[:8]
|
||||
}
|
||||
|
||||
// Convert Tailscale user emails to headscale @example.com format
|
||||
policyJSON := convertPolicyUserEmails(tf.Input.FullPolicy)
|
||||
|
||||
|
|
|
|||
16828
hscontrol/policy/v2/testdata/grant_results/GRANT-V14.json
vendored
16828
hscontrol/policy/v2/testdata/grant_results/GRANT-V14.json
vendored
File diff suppressed because it is too large
Load diff
16826
hscontrol/policy/v2/testdata/grant_results/GRANT-V15.json
vendored
16826
hscontrol/policy/v2/testdata/grant_results/GRANT-V15.json
vendored
File diff suppressed because it is too large
Load diff
16830
hscontrol/policy/v2/testdata/grant_results/GRANT-V16.json
vendored
16830
hscontrol/policy/v2/testdata/grant_results/GRANT-V16.json
vendored
File diff suppressed because it is too large
Load diff
16853
hscontrol/policy/v2/testdata/grant_results/GRANT-V26.json
vendored
Normal file
16853
hscontrol/policy/v2/testdata/grant_results/GRANT-V26.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
16855
hscontrol/policy/v2/testdata/grant_results/GRANT-V27.json
vendored
Normal file
16855
hscontrol/policy/v2/testdata/grant_results/GRANT-V27.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
16862
hscontrol/policy/v2/testdata/grant_results/GRANT-V28.json
vendored
Normal file
16862
hscontrol/policy/v2/testdata/grant_results/GRANT-V28.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
17158
hscontrol/policy/v2/testdata/grant_results/GRANT-V29.json
vendored
Normal file
17158
hscontrol/policy/v2/testdata/grant_results/GRANT-V29.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
17172
hscontrol/policy/v2/testdata/grant_results/GRANT-V30.json
vendored
Normal file
17172
hscontrol/policy/v2/testdata/grant_results/GRANT-V30.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
17848
hscontrol/policy/v2/testdata/grant_results/GRANT-V31.json
vendored
Normal file
17848
hscontrol/policy/v2/testdata/grant_results/GRANT-V31.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
17295
hscontrol/policy/v2/testdata/grant_results/GRANT-V32.json
vendored
Normal file
17295
hscontrol/policy/v2/testdata/grant_results/GRANT-V32.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
17188
hscontrol/policy/v2/testdata/grant_results/GRANT-V33.json
vendored
Normal file
17188
hscontrol/policy/v2/testdata/grant_results/GRANT-V33.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
16855
hscontrol/policy/v2/testdata/grant_results/GRANT-V34.json
vendored
Normal file
16855
hscontrol/policy/v2/testdata/grant_results/GRANT-V34.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
16893
hscontrol/policy/v2/testdata/grant_results/GRANT-V35.json
vendored
Normal file
16893
hscontrol/policy/v2/testdata/grant_results/GRANT-V35.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
19156
hscontrol/policy/v2/testdata/grant_results/GRANT-V36.json
vendored
Normal file
19156
hscontrol/policy/v2/testdata/grant_results/GRANT-V36.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue