diff --git a/hscontrol/policy/v2/tailscale_routes_compat_test.go b/hscontrol/policy/v2/tailscale_routes_compat_test.go deleted file mode 100644 index d7cfbb52..00000000 --- a/hscontrol/policy/v2/tailscale_routes_compat_test.go +++ /dev/null @@ -1,8286 +0,0 @@ -package v2 - -// This file contains compatibility tests for subnet routes and exit nodes. -// It validates Headscale's ACL engine behavior against documented Tailscale -// SaaS behavior. Tests document behavioral differences with TODO comments. -// -// Source findings: /home/kradalby/acl-explore/findings/{10,11,12,13,14,15}-*.md - -import ( - "net/netip" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/juanfont/headscale/hscontrol/policy/policyutil" - "github.com/juanfont/headscale/hscontrol/types" - "github.com/stretchr/testify/require" - "gorm.io/gorm" - "tailscale.com/net/tsaddr" - "tailscale.com/tailcfg" -) - -// wildcardSrcIPs represents the SrcIPs used when wildcard (*) source is specified. -// In Tailscale, this includes the CGNAT range and IPv6 Tailscale range, plus any -// advertised subnet routes. -var wildcardSrcIPs = []string{ - "100.64.0.0/10", // CGNAT range for Tailscale IPs - "fd7a:115c:a1e0::/48", // Tailscale IPv6 range -} - -// memberSrcIPs represents the SrcIPs for autogroup:member (user-owned nodes). -// This includes client1, client2, and user1. -var memberSrcIPs = []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", -} - -// wildcardDstPorts represents wildcard destination ports using {IP: "*"}. -var wildcardDstPorts = []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, -} - -// setupRouteCompatUsers returns the test users for route compatibility tests. -func setupRouteCompatUsers() types.Users { - return types.Users{ - {Model: gorm.Model{ID: 1}, Name: "kratail2tid"}, - } -} - -// setupRouteCompatNodes returns the test nodes for route compatibility tests. -// The node configuration includes: -// - 2 client nodes (user-owned, no routes) -// - 1 subnet router (tag:router, 10.33.0.0/16) -// - 1 exit node (tag:exit, 0.0.0.0/0, ::/0) -// - 1 multi-router (tag:router + tag:exit, 172.16.0.0/24 + exit routes) -// - 2 HA routers (tag:ha, both advertise 192.168.1.0/24) -// - 1 big router (tag:router, 10.0.0.0/8) -// - 1 user-owned node (user1). -func setupRouteCompatNodes(users types.Users) types.Nodes { - // Node: client1 - User-owned client (no routes) - nodeClient1 := &types.Node{ - ID: 1, - GivenName: "client1", - User: &users[0], - UserID: &users[0].ID, - IPv4: ptrAddr("100.116.73.38"), - IPv6: ptrAddr("fd7a:115c:a1e0::a801:4949"), - Hostinfo: &tailcfg.Hostinfo{}, - ApprovedRoutes: []netip.Prefix{}, - } - - // Node: client2 - User-owned client (no routes) - nodeClient2 := &types.Node{ - ID: 2, - GivenName: "client2", - User: &users[0], - UserID: &users[0].ID, - IPv4: ptrAddr("100.89.42.23"), - IPv6: ptrAddr("fd7a:115c:a1e0::d01:2a2e"), - Hostinfo: &tailcfg.Hostinfo{}, - ApprovedRoutes: []netip.Prefix{}, - } - - // Node: subnet-router - Tagged with tag:router, advertises 10.33.0.0/16 - nodeSubnetRouter := &types.Node{ - ID: 3, - GivenName: "subnet-router", - IPv4: ptrAddr("100.119.139.79"), - IPv6: ptrAddr("fd7a:115c:a1e0::4001:8ba0"), - Tags: []string{"tag:router"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("10.33.0.0/16"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("10.33.0.0/16"), - }, - } - - // Node: exit-node - Tagged with tag:exit, advertises exit routes - nodeExitNode := &types.Node{ - ID: 4, - GivenName: "exit-node", - IPv4: ptrAddr("100.121.32.1"), - IPv6: ptrAddr("fd7a:115c:a1e0::7f01:2004"), - Tags: []string{"tag:exit"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: tsaddr.ExitRoutes(), - }, - ApprovedRoutes: tsaddr.ExitRoutes(), - } - - // Node: multi-router - Tagged with tag:router AND tag:exit - // Advertises both subnet (172.16.0.0/24) and exit routes - multiRouterRoutes := append([]netip.Prefix{ - netip.MustParsePrefix("172.16.0.0/24"), - }, tsaddr.ExitRoutes()...) - nodeMultiRouter := &types.Node{ - ID: 5, - GivenName: "multi-router", - IPv4: ptrAddr("100.74.117.7"), - IPv6: ptrAddr("fd7a:115c:a1e0::c401:7508"), - Tags: []string{"tag:router", "tag:exit"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: multiRouterRoutes, - }, - ApprovedRoutes: multiRouterRoutes, - } - - // Node: ha-router1 - Tagged with tag:ha, advertises 192.168.1.0/24 - nodeHARouter1 := &types.Node{ - ID: 6, - GivenName: "ha-router1", - IPv4: ptrAddr("100.85.37.108"), - IPv6: ptrAddr("fd7a:115c:a1e0::f101:2597"), - Tags: []string{"tag:ha"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("192.168.1.0/24"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("192.168.1.0/24"), - }, - } - - // Node: ha-router2 - Tagged with tag:ha, advertises same 192.168.1.0/24 - nodeHARouter2 := &types.Node{ - ID: 7, - GivenName: "ha-router2", - IPv4: ptrAddr("100.119.130.32"), - IPv6: ptrAddr("fd7a:115c:a1e0::4501:82a9"), - Tags: []string{"tag:ha"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("192.168.1.0/24"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("192.168.1.0/24"), - }, - } - - // Node: big-router - Tagged with tag:router, advertises 10.0.0.0/8 - nodeBigRouter := &types.Node{ - ID: 8, - GivenName: "big-router", - IPv4: ptrAddr("100.100.100.1"), - IPv6: ptrAddr("fd7a:115c:a1e0::6401:6401"), - Tags: []string{"tag:router"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("10.0.0.0/8"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("10.0.0.0/8"), - }, - } - - // Node: user1 - User-owned node (no routes) - nodeUser1 := &types.Node{ - ID: 9, - GivenName: "user1", - User: &users[0], - UserID: &users[0].ID, - IPv4: ptrAddr("100.90.199.68"), - IPv6: ptrAddr("fd7a:115c:a1e0::2d01:c747"), - Hostinfo: &tailcfg.Hostinfo{}, - ApprovedRoutes: []netip.Prefix{}, - } - - return types.Nodes{ - nodeClient1, - nodeClient2, - nodeSubnetRouter, - nodeExitNode, - nodeMultiRouter, - nodeHARouter1, - nodeHARouter2, - nodeBigRouter, - nodeUser1, - } -} - -// routesPolicyPrefix provides the standard groups, tagOwners, and hosts -// for route compatibility tests. -const routesPolicyPrefix = `{ - "groups": { - "group:admins": ["kratail2tid@"], - "group:empty": [] - }, - "tagOwners": { - "tag:router": ["kratail2tid@"], - "tag:exit": ["kratail2tid@"], - "tag:ha": ["kratail2tid@"] - }, - "hosts": { - "internal": "10.0.0.0/8", - "subnet24": "192.168.1.0/24" - }, - "acls": [` - -const routesPolicySuffix = ` - ] -}` - -// makeRoutesPolicy creates a full policy from just the ACL rules portion. -func makeRoutesPolicy(aclRules string) string { - return routesPolicyPrefix + aclRules + routesPolicySuffix -} - -// routesCompatTest defines a test case for routes compatibility testing. -type routesCompatTest struct { - name string // Test name - policy string // HuJSON policy as multiline raw string - wantFilters map[string][]tailcfg.FilterRule // node GivenName -> expected filters -} - -// TestTailscaleRoutesCompatSubnetBasics tests basic subnet route behavior (Category A). -// These tests verify that subnet routes are correctly included in SrcIPs for wildcard rules, -// that tag-based ACLs resolve to node IPs (not routes), and that explicit subnet filters -// are placed on the correct destination nodes. -func TestTailscaleRoutesCompatSubnetBasics(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // A1: Wildcard ACL includes subnet routes in SrcIPs - { - name: "A1_wildcard_acl_includes_routes_in_srcips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - // When using * -> *:*, SrcIPs should include advertised subnet routes - // (but NOT exit routes 0.0.0.0/0, ::/0). - // TODO: Verify Tailscale includes subnet routes 10.33.0.0/16, 172.16.0.0/24, - // 192.168.1.0/24, 10.0.0.0/8 in SrcIPs but NOT 0.0.0.0/0, ::/0 - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - // TODO: Tailscale also includes these subnet routes: - // "10.0.0.0/8", - // "10.33.0.0/16", - // "172.16.0.0/24", - // "192.168.1.0/24", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix DstPorts expansion and exit node coverage for tag-based ACLs - // - // A2: Tag-based ACL resolves to node IPs only, NOT routes - // - // TAILSCALE BEHAVIOR: - // - tag:router includes: subnet-router, multi-router, big-router - // - Each tag:router node receives filter with ALL tag:router IPs in DstPorts - // - exit-node (tag:exit only) does NOT receive any filter - // - DstPorts contains ONLY node IPs, NOT advertised routes - // - // HEADSCALE BEHAVIOR: - // - INCORRECT: Each node only gets ITS OWN IPs in DstPorts (should be ALL tag IPs) - // - INCORRECT: exit-node receives a filter because multi-router has exit routes - // and Headscale treats 0.0.0.0/0 as covering node IPs for filter distribution - // - // ROOT CAUSE: - // 1. Filter compilation only adds destinations for the current node being filtered, - // not all nodes matching the tag - // 2. Exit routes (0.0.0.0/0, ::/0) are treated as covering all destinations - // - // FIX REQUIRED: - // 1. When dst is a tag, include ALL IPs of nodes with that tag in DstPorts - // 2. Exclude exit routes from filter coverage calculations - { - name: "A2_tag_based_acl_excludes_routes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:router"], "dst": ["tag:router:*"]} - `), - // EXPECTED (Tailscale) - commented out: - // wantFilters: map[string][]tailcfg.FilterRule{ - // "client1": nil, - // "client2": nil, - // "exit-node": nil, // tag:exit only, not tag:router - // "ha-router1": nil, - // "ha-router2": nil, - // "user1": nil, - // "subnet-router": { - // { - // SrcIPs: []string{ - // "100.100.100.1/32", "100.119.139.79/32", "100.74.117.7/32", - // "fd7a:115c:a1e0::4001:8ba0/128", "fd7a:115c:a1e0::6401:6401/128", - // "fd7a:115c:a1e0::c401:7508/128", - // }, - // DstPorts: []tailcfg.NetPortRange{ - // // ALL tag:router IPs, not just this node's IP - // {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - // {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - // {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - // {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - // {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - // {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - // }, - // IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - // }, - // }, - // // Same for multi-router and big-router... - // }, - // - // ACTUAL (Headscale) - current behavior: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - // INCORRECT: subnet-router only gets its OWN IPs in DstPorts - // Tailscale includes ALL tag:router IPs - "subnet-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - // INCORRECT: Only this node's IPs, should be ALL tag:router IPs - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // multi-router has BOTH tag:router AND tag:exit - // Because of exit routes, filter merging includes all tag:router destinations - // This is actually the CORRECT Tailscale behavior for DstPorts (but wrong filter distribution) - "multi-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - // All tag:router IPs due to exit route coverage + filter merging - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: big-router only gets its OWN IPs in DstPorts - // Tailscale includes ALL tag:router IPs - "big-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - // INCORRECT: Only this node's IPs, should be ALL tag:router IPs - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: exit-node receives filter due to multi-router having exit routes - // and Headscale treating 0.0.0.0/0 as covering node IPs - // Tailscale would return nil here - "exit-node": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // A3: Explicit subnet destination - filter goes to router only - // - // TAILSCALE BEHAVIOR: - // - Filter placed ONLY on nodes whose routes cover the destination - // - subnet-router (10.33.0.0/16) gets filter - exact match - // - big-router (10.0.0.0/8) gets filter - parent covers child - // - exit-node and multi-router get NO filter - exit routes (0.0.0.0/0) - // do NOT count as "covering" subnet destinations for filter placement - // - // HEADSCALE BEHAVIOR: - // - Exit nodes (0.0.0.0/0) ARE treated as covering all destinations - // - exit-node and multi-router incorrectly receive the filter - // - // ROOT CAUSE: - // hscontrol/policy/v2/filter.go treats exit routes (0.0.0.0/0, ::/0) as - // covering all destinations, but Tailscale only uses exit routes for - // actual traffic routing, not for filter distribution. - // - // FIX REQUIRED: - // When determining which nodes receive a filter based on route coverage, - // exclude exit routes (0.0.0.0/0 and ::/0) from the coverage check. - // Exit nodes should only receive filters when explicitly targeted. - { - name: "A3_explicit_subnet_filter_to_router", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*"]} - `), - // EXPECTED (Tailscale) - commented out: - // wantFilters: map[string][]tailcfg.FilterRule{ - // "client1": nil, - // "client2": nil, - // "exit-node": nil, // Exit route does NOT cover for filter placement - // "ha-router1": nil, - // "ha-router2": nil, - // "user1": nil, - // "multi-router": nil, // Exit route does NOT cover for filter placement - // "subnet-router": { - // { - // SrcIPs: []string{"100.64.0.0/10", "fd7a:115c:a1e0::/48"}, - // DstPorts: []tailcfg.NetPortRange{{IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}}, - // IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - // }, - // }, - // "big-router": { - // { - // SrcIPs: []string{"100.64.0.0/10", "fd7a:115c:a1e0::/48"}, - // DstPorts: []tailcfg.NetPortRange{{IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}}, - // IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - // }, - // }, - // }, - // - // ACTUAL (Headscale) - current behavior: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - // subnet-router owns 10.33.0.0/16 - exact match (CORRECT) - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // big-router owns 10.0.0.0/8 which covers 10.33.0.0/16 (CORRECT) - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: exit-node gets filter because 0.0.0.0/0 "covers" destination - // Tailscale would return nil here - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: multi-router gets filter because 0.0.0.0/0 "covers" destination - // Tailscale would return nil here - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // A3b: autogroup:member to subnet - SrcIPs = member IPs only - // - // TAILSCALE BEHAVIOR: - // - autogroup:member = user-owned nodes only (client1, client2, user1) - // - Filter goes to subnet-router (exact match) and big-router (parent route) - // - exit-node and multi-router get NO filter (exit routes don't cover) - // - // HEADSCALE BEHAVIOR: - // - exit-node and multi-router incorrectly receive filters because - // exit routes (0.0.0.0/0) are treated as covering all destinations - // - // ROOT CAUSE: - // Same as A3 - exit routes should not count for filter distribution - // - // FIX REQUIRED: - // Exclude exit routes (0.0.0.0/0 and ::/0) from coverage checks - { - name: "A3b_autogroup_member_to_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:*"]} - `), - // EXPECTED (Tailscale) - commented out: - // wantFilters: map[string][]tailcfg.FilterRule{ - // "client1": nil, - // "client2": nil, - // "exit-node": nil, // Exit route does NOT cover - // "ha-router1": nil, - // "ha-router2": nil, - // "user1": nil, - // "multi-router": nil, // Exit route does NOT cover - // "subnet-router": { ... }, // Exact match - // "big-router": { ... }, // Parent route covers - // }, - // - // ACTUAL (Headscale) - current behavior: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - // CORRECT: subnet-router gets filter (exact match) - "subnet-router": { - { - SrcIPs: []string{ - "100.89.42.23/32", // client2 - "100.90.199.68/32", // user1 - "100.116.73.38/32", // client1 - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // CORRECT: big-router gets filter (parent route 10.0.0.0/8 covers) - "big-router": { - { - SrcIPs: []string{ - "100.89.42.23/32", - "100.90.199.68/32", - "100.116.73.38/32", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: exit-node receives filter due to 0.0.0.0/0 coverage - // Tailscale would return nil here - "exit-node": { - { - SrcIPs: []string{ - "100.89.42.23/32", - "100.90.199.68/32", - "100.116.73.38/32", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: multi-router receives filter due to 0.0.0.0/0 coverage - // Tailscale would return nil here - "multi-router": { - { - SrcIPs: []string{ - "100.89.42.23/32", - "100.90.199.68/32", - "100.116.73.38/32", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // A4: Multiple routes on same router (172.16.0.0/24 destination) - // - // TAILSCALE BEHAVIOR: - // - multi-router has 172.16.0.0/24, should get filter (exact match) - // - exit-node has 0.0.0.0/0 but does NOT cover 172.16.0.0/24 for filter placement - // - // HEADSCALE BEHAVIOR: - // - multi-router correctly gets filter - // - exit-node incorrectly gets filter because 0.0.0.0/0 is treated as covering - // - // ROOT CAUSE: - // Same as A3 - exit routes should not count for filter distribution - // - // FIX REQUIRED: - // Exclude exit routes from coverage checks - { - name: "A4_multiple_routes_same_router", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["172.16.0.0/24:*"]} - `), - // EXPECTED (Tailscale) - commented out: - // wantFilters: map[string][]tailcfg.FilterRule{ - // "client1": nil, - // "client2": nil, - // "subnet-router": nil, - // "exit-node": nil, // 0.0.0.0/0 does NOT cover for filter placement - // "ha-router1": nil, - // "ha-router2": nil, - // "big-router": nil, - // "user1": nil, - // "multi-router": { ... }, // Exact match - // }, - // - // ACTUAL (Headscale) - current behavior: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - "user1": nil, - // CORRECT: multi-router gets filter (exact match for 172.16.0.0/24) - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "172.16.0.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: exit-node receives filter due to 0.0.0.0/0 coverage - // Tailscale would return nil here - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "172.16.0.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // A5: Host alias to subnet (uses "internal" = "10.0.0.0/8") - // - // TAILSCALE BEHAVIOR: - // - "internal" resolves to 10.0.0.0/8 via hosts alias - // - big-router (10.0.0.0/8) gets filter - exact match - // - subnet-router (10.33.0.0/16) gets filter - child route - // - exit-node and multi-router get NO filter (exit routes don't cover) - // - // HEADSCALE BEHAVIOR: - // - big-router and subnet-router correctly get filters - // - exit-node and multi-router incorrectly get filters (exit route coverage) - // - // ROOT CAUSE: - // Same as A3 - exit routes should not count for filter distribution - // - // FIX REQUIRED: - // Exclude exit routes from coverage checks - { - name: "A5_host_alias_to_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["internal:22"]} - `), - // EXPECTED (Tailscale) - commented out: - // wantFilters: map[string][]tailcfg.FilterRule{ - // "client1": nil, - // "client2": nil, - // "exit-node": nil, // Exit route does NOT cover - // "ha-router1": nil, - // "ha-router2": nil, - // "user1": nil, - // "multi-router": nil, // Exit route does NOT cover - // "subnet-router": { ... }, // Child route - // "big-router": { ... }, // Exact match - // }, - // - // ACTUAL (Headscale) - current behavior: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - // CORRECT: subnet-router gets filter (child route 10.33.0.0/16 within 10.0.0.0/8) - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // CORRECT: big-router gets filter (exact match for 10.0.0.0/8) - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: exit-node receives filter due to 0.0.0.0/0 coverage - // Tailscale would return nil here - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: multi-router receives filter due to 0.0.0.0/0 coverage - // Tailscale would return nil here - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - pol, err := unmarshalPolicy([]byte(tt.policy)) - require.NoError(t, err, "failed to parse policy") - - err = pol.validate() - require.NoError(t, err, "policy validation failed") - - for nodeName, wantFilters := range tt.wantFilters { - node := findNodeByGivenName(nodes, nodeName) - require.NotNil(t, node, "node %s not found", nodeName) - - compiledFilters, err := pol.compileFilterRulesForNode(users, node.View(), nodes.ViewSlice()) - require.NoError(t, err, "failed to compile filters for node %s", nodeName) - - gotFilters := policyutil.ReduceFilterRules(node.View(), compiledFilters) - - if len(wantFilters) == 0 && len(gotFilters) == 0 { - continue - } - - if diff := cmp.Diff(wantFilters, gotFilters, cmpOptions()...); diff != "" { - t.Errorf("node %s filters mismatch (-want +got):\n%s", nodeName, diff) - } - } - }) - } -} - -// TestTailscaleRoutesCompatExitNodes tests exit node behavior (Category B). -// These tests verify that exit routes (0.0.0.0/0, ::/0) are NOT included in SrcIPs, -// that exit nodes can cover external destinations, and autogroup:internet behavior. -func TestTailscaleRoutesCompatExitNodes(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - // Standard wildcard filter that all nodes receive for * -> *:* ACL - wildcardFilter := []tailcfg.FilterRule{ - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - } - - tests := []routesCompatTest{ - // TODO: Verify Tailscale includes subnet routes in SrcIPs for wildcard ACLs - // - // B1: Exit routes NOT in SrcIPs with wildcard ACL - // - // TAILSCALE BEHAVIOR: - // - SrcIPs includes CGNAT + IPv6 Tailscale ranges - // - SrcIPs also includes advertised subnet routes (10.0.0.0/8, etc.) - // - Exit routes (0.0.0.0/0, ::/0) are NOT included in SrcIPs - // - // HEADSCALE BEHAVIOR: - // - SrcIPs only includes CGNAT + IPv6 Tailscale ranges - // - Subnet routes are NOT included in SrcIPs (might be a difference) - // - Exit routes correctly NOT included - // - // ROOT CAUSE: - // Headscale doesn't expand wildcard source to include subnet routes - // - // FIX REQUIRED (if needed): - // Add subnet routes to SrcIPs when source is wildcard - { - name: "B1_exit_routes_not_in_srcips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - // All nodes receive the same wildcard filter - // Key verification: exit routes NOT in SrcIPs (they're not - correct!) - wantFilters: map[string][]tailcfg.FilterRule{ - "exit-node": wildcardFilter, - "client1": wildcardFilter, - "client2": wildcardFilter, - "multi-router": wildcardFilter, - "subnet-router": wildcardFilter, - "ha-router1": wildcardFilter, - "ha-router2": wildcardFilter, - "big-router": wildcardFilter, - "user1": wildcardFilter, - }, - }, - // B2: tag:exit excludes exit routes from DstPorts - { - name: "B2_tag_exit_excludes_exit_routes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:exit"], "dst": ["tag:exit:*"]} - `), - // tag:exit includes: exit-node, multi-router - // DstPorts should contain ONLY their node IPs, NOT exit routes - 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": { - { - SrcIPs: []string{ - "100.121.32.1/32", // exit-node IPv4 - "100.74.117.7/32", // multi-router IPv4 - "fd7a:115c:a1e0::7f01:2004/128", // exit-node IPv6 - "fd7a:115c:a1e0::c401:7508/128", // multi-router IPv6 - }, - DstPorts: []tailcfg.NetPortRange{ - // Node IPs only, NOT exit routes (0.0.0.0/0, ::/0) - {IP: "100.121.32.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::7f01:2004/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.121.32.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::7f01:2004/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Verify Tailscale includes subnet routes in SrcIPs - // - // B4: Multi-router has both subnet and exit routes - // - // TAILSCALE BEHAVIOR: - // - multi-router has 172.16.0.0/24 (subnet) + 0.0.0.0/0,::/0 (exit) - // - SrcIPs may include 172.16.0.0/24 but NOT 0.0.0.0/0 or ::/0 - // - Only multi-router node may receive the filter (needs verification) - // - // HEADSCALE BEHAVIOR: - // - All nodes receive the same wildcard filter - // - SrcIPs is just CGNAT + IPv6 range, no subnet routes - // - // ROOT CAUSE: - // Headscale distributes wildcard filters to all nodes - { - name: "B4_multi_router_has_both_route_types", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - // EXPECTED (Tailscale) - commented out: - // wantFilters: map[string][]tailcfg.FilterRule{ - // "multi-router": { - // { - // SrcIPs: []string{ - // "100.64.0.0/10", - // "fd7a:115c:a1e0::/48", - // // Tailscale may include 172.16.0.0/24 here - // // but definitely NOT 0.0.0.0/0 or ::/0 - // }, - // DstPorts: []tailcfg.NetPortRange{ - // {IP: "100.64.0.0/10", Ports: tailcfg.PortRangeAny}, - // {IP: "fd7a:115c:a1e0::/48", Ports: tailcfg.PortRangeAny}, - // }, - // IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - // }, - // }, - // "client1": nil, - // "client2": nil, - // "subnet-router": nil, - // "exit-node": nil, - // "ha-router1": nil, - // "ha-router2": nil, - // "big-router": nil, - // "user1": nil, - // }, - // - // ACTUAL (Headscale) - all nodes get wildcard filter: - wantFilters: map[string][]tailcfg.FilterRule{ - "multi-router": wildcardFilter, - "client1": wildcardFilter, - "client2": wildcardFilter, - "subnet-router": wildcardFilter, - "exit-node": wildcardFilter, - "ha-router1": wildcardFilter, - "ha-router2": wildcardFilter, - "big-router": wildcardFilter, - "user1": wildcardFilter, - }, - }, - // B8: autogroup:internet generates no 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:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - "user1": nil, - }, - }, - // B3: Exit node advertises exit routes (verify RoutableIPs) - // - // This test verifies that exit-node has 0.0.0.0/0 and ::/0 in RoutableIPs. - // All nodes get wildcard filters with {IP: "*"} format matching Tailscale. - { - name: "B3_exit_node_advertises_routes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": wildcardFilter, - "client2": wildcardFilter, - "subnet-router": wildcardFilter, - "exit-node": wildcardFilter, - "multi-router": wildcardFilter, - "ha-router1": wildcardFilter, - "ha-router2": wildcardFilter, - "big-router": wildcardFilter, - "user1": wildcardFilter, - }, - }, - // B5: Exit node with wildcard destination has ExitNodeOption - // - // Exit nodes should have ExitNodeOption=true in MapResponse. - // All nodes get wildcard filters with {IP: "*"} format matching Tailscale. - { - name: "B5_exit_with_wildcard_dst", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": wildcardFilter, - "client2": wildcardFilter, - "subnet-router": wildcardFilter, - "exit-node": wildcardFilter, - "multi-router": wildcardFilter, - "ha-router1": wildcardFilter, - "ha-router2": wildcardFilter, - "big-router": wildcardFilter, - "user1": wildcardFilter, - }, - }, - // TODO: Verify Tailscale filter distribution for tag source with wildcard destination - // - // B6: ExitNodeOption field verification - // - // ACL: tag:exit -> *:* - // Nodes with approved exit routes should have ExitNodeOption=true. - // - // TAILSCALE BEHAVIOR: - // - Need to verify if only exit-tagged nodes receive filters - // - Or if ALL nodes (destinations) receive filters - // - // HEADSCALE BEHAVIOR: - // - ALL nodes receive filters (they're all destinations) - // - SrcIPs = tag:exit node IPs - // - DstPorts = explicit CIDR ranges (not "*") - // - // ROOT CAUSE: - // The test expected only exit-tagged nodes to get filters, but with - // `tag:exit -> *:*`, all nodes are destinations and should get filters. - { - name: "B6_exit_node_option_field", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:exit"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale) - need verification: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, // Or filter? - "client2": nil, // Or filter? - "subnet-router": nil, // Or filter? - "ha-router1": nil, // Or filter? - "ha-router2": nil, // Or filter? - "big-router": nil, // Or filter? - "user1": nil, // Or filter? - "exit-node": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { ... }, - }, - */ - // ACTUAL (Headscale): - // All nodes receive filters (they're all destinations) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Same as B6 - verify Tailscale filter distribution - // - // B7: Multiple exit nodes verification - // - // ACL: tag:exit -> *:* - // Both exit-node and multi-router have tag:exit. - // Same pattern as B6 - all nodes are destinations and receive filters. - // - // TAILSCALE BEHAVIOR: - // - Need to verify actual filter distribution - // - // HEADSCALE BEHAVIOR: - // - All nodes receive filters (same as B6) - { - name: "B7_multiple_exit_nodes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:exit"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale) - need verification: - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, // Or filter? - // ... same pattern as B6 - "exit-node": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { ... }, - }, - */ - // ACTUAL (Headscale): - // All nodes receive filters (same as B6) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.121.32.1/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // B9: Exit routes appear in peer AllowedIPs - // - // When viewing exit-node as a peer, AllowedIPs should include exit routes. - // All nodes get wildcard filters with {IP: "*"} format matching Tailscale. - { - name: "B9_exit_routes_in_allowedips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": wildcardFilter, - "client2": wildcardFilter, - "subnet-router": wildcardFilter, - "exit-node": wildcardFilter, - "multi-router": wildcardFilter, - "ha-router1": wildcardFilter, - "ha-router2": wildcardFilter, - "big-router": wildcardFilter, - "user1": wildcardFilter, - }, - }, - // B10: Exit routes NOT in PrimaryRoutes field - // - // PrimaryRoutes is for subnet routes only, not exit routes. - // Exit routes (0.0.0.0/0, ::/0) should NOT appear in PrimaryRoutes. - // All nodes get wildcard filters with {IP: "*"} format matching Tailscale. - { - name: "B10_exit_routes_not_in_primaryroutes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": wildcardFilter, - "client2": wildcardFilter, - "subnet-router": wildcardFilter, - "exit-node": wildcardFilter, - "multi-router": wildcardFilter, - "ha-router1": wildcardFilter, - "ha-router2": wildcardFilter, - "big-router": wildcardFilter, - "user1": wildcardFilter, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - pol, err := unmarshalPolicy([]byte(tt.policy)) - require.NoError(t, err, "failed to parse policy") - - err = pol.validate() - require.NoError(t, err, "policy validation failed") - - for nodeName, wantFilters := range tt.wantFilters { - node := findNodeByGivenName(nodes, nodeName) - require.NotNil(t, node, "node %s not found", nodeName) - - compiledFilters, err := pol.compileFilterRulesForNode(users, node.View(), nodes.ViewSlice()) - require.NoError(t, err, "failed to compile filters for node %s", nodeName) - - gotFilters := policyutil.ReduceFilterRules(node.View(), compiledFilters) - - if len(wantFilters) == 0 && len(gotFilters) == 0 { - continue - } - - if diff := cmp.Diff(wantFilters, gotFilters, cmpOptions()...); diff != "" { - t.Errorf("node %s filters mismatch (-want +got):\n%s", nodeName, diff) - } - } - }) - } -} - -// TestTailscaleRoutesCompatHARouters tests HA router behavior (Category E). -// These tests verify that multiple routers can advertise the same subnet, -// and that both receive filters even though only one is primary. -func TestTailscaleRoutesCompatHARouters(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // E1: Two HA routers advertise same subnet - both enabled - // - // ACL: * -> 192.168.1.0/24:* - // Both ha-router1 and ha-router2 advertise 192.168.1.0/24. - // Both should receive the filter (both are approved, one is primary). - // - // TAILSCALE BEHAVIOR: - // - Only HA routers get filters (exact route match) - // - Exit nodes do NOT get filters (exit routes don't cover for placement) - // - // HEADSCALE BEHAVIOR: - // - HA routers correctly get filters - // - Exit nodes also get filters because 0.0.0.0/0 "covers" destination - // - // ROOT CAUSE: - // Same as A3 - exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "E1_ha_two_routers_same_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): - // HA routers correctly get filters, but exit nodes also incorrectly get them - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - // CORRECT: Both HA routers get the filter - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters due to 0.0.0.0/0 coverage - // Tailscale would return nil here - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // E4: HA routers with host alias - // - // ACL: * -> subnet24:22 (subnet24 = 192.168.1.0/24) - // Same as E1 but uses host alias. Exit route coverage issue applies. - { - name: "E4_ha_both_get_filters_host_alias", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["subnet24:22"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { ... }, - "ha-router2": { ... }, - }, - */ - // ACTUAL (Headscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters due to 0.0.0.0/0 coverage - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // E2: HA primary node appears in peer AllowedIPs - // Same exit route coverage issue as E1. - { - name: "E2_ha_primary_in_allowedips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "exit-node": nil, - "multi-router": nil, - // ... only HA routers get filters - }, - */ - // ACTUAL (Headscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // E3: HA secondary does NOT have route in AllowedIPs - // Same exit route coverage issue as E1. - { - name: "E3_ha_secondary_no_route_in_allowedips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "exit-node": nil, - "multi-router": nil, - // ... only HA routers get filters - }, - */ - // ACTUAL (Headscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // E5: First advertiser becomes primary, both HA routers get filters - // - // TAILSCALE BEHAVIOR: - // - Only HA routers get filters (they own 192.168.1.0/24) - // - Exit nodes do NOT get filters (exit routes don't count for coverage) - // - // HEADSCALE BEHAVIOR: - // - HA routers correctly get filters - // - Exit nodes also get filters because 0.0.0.0/0 "covers" destination - // - // ROOT CAUSE: - // Same as E1-E4 - exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "E5_first_advertiser_is_primary", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes incorrectly get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatFilterPlacement tests filter placement rules (Category F). -// These tests verify that filters go to DESTINATION nodes (route owners), -// not to source nodes, and that route coverage rules are applied correctly. -func TestTailscaleRoutesCompatFilterPlacement(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // F1: Filter goes to destination node (route owner), not source - // - // TAILSCALE BEHAVIOR: - // - Filter placed on subnet-router (owns 10.33.0.0/16) and big-router (owns 10.0.0.0/8) - // - Source nodes (clients, user1) get null filters - // - Exit nodes do NOT get filters (exit routes don't count for coverage) - // - // HEADSCALE BEHAVIOR: - // - Correct for subnet-router and big-router - // - Exit nodes also get filters because 0.0.0.0/0 "covers" destination - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "F1_filter_on_destination_not_source", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:22"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes incorrectly get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix DstPorts expansion for autogroup:member to match Tailscale behavior - // - // F2: Subnet as ACL source, autogroup:member as destination - // - // TAILSCALE BEHAVIOR: - // - Each member receives a filter with DstPorts containing ALL member IPs - // - client1's filter has DstPorts with client1, client2, user1 IPs - // - // HEADSCALE BEHAVIOR: - // - Each member receives filter with DstPorts containing ONLY its own IP - // - client1's filter has DstPorts with only client1's IP - // - // ROOT CAUSE: - // DstPorts is not expanded to include all autogroup:member IPs - // - // FIX REQUIRED: - // Expand autogroup:member in DstPorts to include all member IPs, not just self - { - name: "F2_subnet_as_acl_source", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["10.33.0.0/16"], "dst": ["autogroup:member:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - }, - */ - // ACTUAL (Headscale): DstPorts only contains self IP, not all member IPs - // Additionally, tagged nodes also incorrectly receive filters - wantFilters: map[string][]tailcfg.FilterRule{ - // Members receive filters with ONLY self IP in DstPorts - "client1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - // INCORRECT: Only client1's IPs, should include all members - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - // INCORRECT: Only client2's IPs - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - // INCORRECT: Only user1's IPs - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Tagged nodes should not get filters but do in Headscale - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - // INCORRECT: Exit nodes get filters with all member IPs in DstPorts - "exit-node": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // F3: Wildcard source, specific subnet destination - // - // TAILSCALE BEHAVIOR: - // - Filter on subnet-router (owns 10.33.0.0/16) and big-router (owns 10.0.0.0/8) - // - Exit nodes do NOT get filters (exit routes don't count for coverage) - // - // HEADSCALE BEHAVIOR: - // - Correct for subnet-router and big-router - // - Exit nodes also get filters because 0.0.0.0/0 "covers" destination - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "F3_wildcard_src_specific_dst", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:22"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes incorrectly get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // F7: Filter DstPorts shows ACL CIDR, not route CIDR - // - // TAILSCALE BEHAVIOR: - // - DstPorts.IP = ACL CIDR (10.33.1.0/24), not route CIDR - // - Only subnet-router and big-router get filters - // - Exit nodes do NOT get filters - // - // HEADSCALE BEHAVIOR: - // - DstPorts.IP correctly uses ACL CIDR (this part works) - // - Exit nodes also get filters because 0.0.0.0/0 "covers" destination - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "F7_filter_dstports_shows_acl_cidr", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:22"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes incorrectly get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix wildcard destination filter distribution to match Tailscale behavior - // - // F4: Specific source (tag:router), wildcard destination - // - // TAILSCALE BEHAVIOR: - // - Filter sent to all non-source nodes (all nodes except tag:router nodes) - // - Non-router nodes get filter, router nodes don't receive filter for their own traffic - // - // HEADSCALE BEHAVIOR: - // - All nodes get the filter, including the source nodes themselves - // - DstPorts uses expanded CGNAT ranges instead of "*" - // - // ROOT CAUSE: - // Wildcard destination distribution differs - Headscale sends to all nodes - // DstPorts format differs - Headscale expands "*" to CGNAT ranges - // - // FIX REQUIRED: - // Review wildcard destination distribution logic - { - name: "F4_specific_src_wildcard_dst", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:router"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.100.100.1/32", // big-router - "100.119.139.79/32", // subnet-router - "100.74.117.7/32", // multi-router - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - }, - */ - // ACTUAL (Headscale): All nodes get filters with expanded CGNAT ranges - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.74.117.7/32", // multi-router - "100.100.100.1/32", // big-router - "100.119.139.79/32", // subnet-router - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.74.117.7/32", - "100.100.100.1/32", - "100.119.139.79/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix bidirectional subnet access and DstPorts expansion to match Tailscale - // - // F5: Bidirectional subnet access - // - // TAILSCALE BEHAVIOR: - // - Rule 1 (member -> subnet): Filters on subnet-router and big-router only - // - Rule 2 (subnet -> member): All members get filter with all member IPs in DstPorts - // - Exit nodes do NOT get filters - // - // HEADSCALE BEHAVIOR: - // - All members get filters (rule 2 distribution to all) - // - DstPorts only contains self IP, not all member IPs - // - Exit nodes also get filters (exit route coverage issue) - // - // ROOT CAUSE: - // 1. autogroup:member DstPorts expansion only includes self - // 2. Exit routes treated as covering subnet destinations - // - // FIX REQUIRED: - // 1. Expand autogroup:member in DstPorts to all member IPs - // 2. Exclude exit routes from filter distribution coverage - { - name: "F5_bidirectional_subnet_access", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:*"]}, - {"action": "accept", "src": ["10.33.0.0/16"], "dst": ["autogroup:member:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": nil, - "user1": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Multiple issues - wantFilters: map[string][]tailcfg.FilterRule{ - // All members get filters with self-only DstPorts - "client1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - // INCORRECT: Only client1's IPs - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters from BOTH rules - "exit-node": { - // First filter: from rule 1 (member -> subnet) - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - // Second filter: from rule 2 (subnet -> member) - // Exit node gets this because 0.0.0.0/0 "covers" member IPs - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - // First filter: from rule 1 (member -> subnet) - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - // Second filter: from rule 2 (subnet -> member) - // Multi-router gets this because 0.0.0.0/0 "covers" member IPs - { - SrcIPs: []string{"10.33.0.0/16"}, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.89.42.23/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.90.199.68/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.116.73.38/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::d01:2a2e/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::2d01:c747/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::a801:4949/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // F6: Filter SrcIPs expansion with autogroup:member - // - // TAILSCALE BEHAVIOR: - // - Only subnet-router and big-router get filters - // - Exit nodes do NOT get filters - // - // HEADSCALE BEHAVIOR: - // - Correct for subnet-router and big-router - // - Exit nodes also get filters because 0.0.0.0/0 "covers" destination - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "F6_filter_srcips_expansion", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes incorrectly get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix policy validation to allow undefined tags (matching Tailscale behavior) - // - // F8: Route enabled but ACL source doesn't match any nodes - // - // TAILSCALE BEHAVIOR: - // - Policy is accepted even if tag doesn't exist (no nodes have that tag) - // - All nodes get null filters - // - // HEADSCALE BEHAVIOR: - // - Policy parsing fails with "Tag is not defined in the Policy" - // - Headscale requires all tags to be defined in tagOwners - // - // ROOT CAUSE: - // Headscale validates that all tags in ACLs are defined in tagOwners - // Tailscale allows undefined tags (they just match nothing) - // - // FIX REQUIRED: - // Either relax tag validation or accept that this is a stricter policy mode - // Using group:empty instead (defined but has no members) - { - name: "F8_route_enabled_acl_denies", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["group:empty"], "dst": ["10.33.0.0/16:*"]} - `), - // group:empty has no members, so no source IPs match - // All nodes should get null filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // F9: ACL allows traffic to subnet but no node has that route - // - // TAILSCALE BEHAVIOR: - // - No node has 10.99.0.0/16 route - // - No filters should be generated for any node - // - // HEADSCALE BEHAVIOR: - // - Exit nodes get filters because 0.0.0.0/0 "covers" 10.99.0.0/16 - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "F9_route_disabled_acl_allows", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.99.0.0/16:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - }, - */ - // ACTUAL (Headscale): Routers with covering routes get filters - // NOTE: big-router (10.0.0.0/8) covers 10.99.0.0/16, so it correctly gets filter - // Exit nodes also incorrectly get filters due to 0.0.0.0/0 coverage - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - // big-router (10.0.0.0/8) correctly covers 10.99.0.0/16 - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.99.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.99.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.99.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// runRoutesCompatTests is a helper to run route compatibility tests. -func runRoutesCompatTests(t *testing.T, users types.Users, nodes types.Nodes, tests []routesCompatTest) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - pol, err := unmarshalPolicy([]byte(tt.policy)) - require.NoError(t, err, "failed to parse policy") - - err = pol.validate() - require.NoError(t, err, "policy validation failed") - - for nodeName, wantFilters := range tt.wantFilters { - node := findNodeByGivenName(nodes, nodeName) - require.NotNil(t, node, "node %s not found", nodeName) - - compiledFilters, err := pol.compileFilterRulesForNode(users, node.View(), nodes.ViewSlice()) - require.NoError(t, err, "failed to compile filters for node %s", nodeName) - - gotFilters := policyutil.ReduceFilterRules(node.View(), compiledFilters) - - if len(wantFilters) == 0 && len(gotFilters) == 0 { - continue - } - - if diff := cmp.Diff(wantFilters, gotFilters, cmpOptions()...); diff != "" { - t.Errorf("node %s filters mismatch (-want +got):\n%s", nodeName, diff) - } - } - }) - } -} - -// TestTailscaleRoutesCompatRouteCoverage tests route coverage rules (Category R). -// These tests verify that: -// - Route coverage: R.Bits() <= D.Bits() && R.Contains(D.Addr()) -// - Exit nodes (0.0.0.0/0) receive filters for ANY destination -// - Parent routes cover child destinations. -func TestTailscaleRoutesCompatRouteCoverage(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // R1: Exit route covers external destination - { - name: "R1_exit_covers_external_dest", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["8.8.8.0/24:53"]} - `), - // 8.8.8.0/24 is external (Google DNS range) - // Exit nodes (0.0.0.0/0) should receive the filter because they cover it - // TODO: Verify this is Tailscale behavior - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, // 10.33.0.0/16 doesn't cover 8.8.8.0/24 - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, // 10.0.0.0/8 doesn't cover 8.8.8.0/24 - "user1": nil, - // Exit nodes cover 8.8.8.0/24 - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "8.8.8.0/24", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "8.8.8.0/24", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // R2: Parent route covers child destination - // TODO: Exit route coverage issue - exit nodes get filters when they shouldn't. - // TAILSCALE BEHAVIOR: Exit nodes (0.0.0.0/0) do NOT receive filters for internal - // subnet destinations like 10.33.1.0/24. Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters because Headscale treats exit routes - // (0.0.0.0/0) as covering all IPv4 destinations, including internal ranges. - // ROOT CAUSE: routeCoversDestination() returns true for exit routes covering internal IPs. - { - name: "R2_parent_route_covers_child_dest", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:22"]} - `), - // big-router has 10.0.0.0/8 - covers 10.33.1.0/24 - // subnet-router has 10.33.0.0/16 - also covers 10.33.1.0/24 - // Both should receive the filter - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { ... }, - "big-router": { ... }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters due to exit route coverage - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route 0.0.0.0/0 covers 10.33.1.0/24) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // R3: Sibling routes don't cover each other - // TODO: Exit route coverage issue - exit nodes get filters when they shouldn't. - // TAILSCALE BEHAVIOR: Exit nodes do NOT receive filters for internal subnet destinations. - // subnet-router (10.33.0.0/16) correctly does NOT get filter (sibling doesn't cover sibling). - // HEADSCALE BEHAVIOR: Exit nodes get filters because 0.0.0.0/0 covers 10.34.0.0/16. - // ROOT CAUSE: routeCoversDestination() returns true for exit routes covering internal IPs. - { - name: "R3_sibling_routes_no_coverage", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.34.0.0/16:22"]} - `), - // 10.34.0.0/16 is a sibling to 10.33.0.0/16 (different /16 in 10.0.0.0/8) - // subnet-router (10.33.0.0/16) should NOT get filter - // big-router (10.0.0.0/8) SHOULD get filter (parent covers both) - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "big-router": { ... }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters due to exit route coverage - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, // 10.33.0.0/16 doesn't cover 10.34.0.0/16 (correct) - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - // Only big-router covers 10.34.0.0/16 - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.34.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route 0.0.0.0/0 covers 10.34.0.0/16) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.34.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.34.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // R4: Exact match route - // TODO: Exit route coverage issue - exit nodes get filters when they shouldn't. - // TAILSCALE BEHAVIOR: Exit nodes do NOT receive filters for internal subnet destinations. - // HEADSCALE BEHAVIOR: Exit nodes get filters because 0.0.0.0/0 covers 10.33.0.0/16. - // ROOT CAUSE: routeCoversDestination() returns true for exit routes covering internal IPs. - { - name: "R4_exact_match_route", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:22"]} - `), - // Exact match: subnet-router has exactly 10.33.0.0/16 - // big-router (10.0.0.0/8) also covers it - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { ... }, - "big-router": { ... }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters due to exit route coverage - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route 0.0.0.0/0 covers 10.33.0.0/16) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatOverlapping tests overlapping route behavior (Category O). -// These tests verify that multiple routers with overlapping routes all receive filters. -func TestTailscaleRoutesCompatOverlapping(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // O2: HA routers both get filter - // TODO: Fix exit route coverage for HA route destinations - // TAILSCALE BEHAVIOR: Only ha-router1 and ha-router2 get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 192.168.1.0/24). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "O2_ha_routers_both_get_filter", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { filter with 192.168.1.0/24:* }, - "ha-router2": { filter with 192.168.1.0/24:* }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O3: Parent-child routes on different nodes - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.1.0/24). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "O3_parent_child_different_nodes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:22"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { filter with 10.33.1.0/24:22 }, - "big-router": { filter with 10.33.1.0/24:22 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O6: Exit route expands filter distribution - { - name: "O6_exit_route_expands_filter_dist", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["8.8.8.0/24:53"]} - `), - // Only exit nodes cover 8.8.8.0/24 - 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": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "8.8.8.0/24", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "8.8.8.0/24", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O12: Filter dest is ACL CIDR, not route CIDR - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.1.0/24). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "O12_filter_dest_is_acl_cidr", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:22"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { filter with 10.33.1.0/24:22 }, - "big-router": { filter with 10.33.1.0/24:22 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // Must be ACL CIDR "10.33.1.0/24", NOT route "10.33.0.0/16" - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // Must be ACL CIDR "10.33.1.0/24", NOT route "10.0.0.0/8" - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatTagResolution tests tag resolution behavior (Category T). -// These tests verify that tags resolve to node IPs only, NOT to routes. -func TestTailscaleRoutesCompatTagResolution(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix per-node DstPorts visibility and exit route coverage - // - // T1: Tags resolve to IPs, not routes - // - // TAILSCALE BEHAVIOR: - // - Only tag:router nodes (subnet-router, multi-router, big-router) get filters - // - DstPorts shows ALL tag:router node IPs to each node - // - exit-node does NOT get filter (not in tag:router) - // - // HEADSCALE BEHAVIOR: - // - Exit node also gets filter (0.0.0.0/0 route "covers" tag:router IPs) - // - Per-node DstPorts visibility: each node only sees its OWN IP in DstPorts - // (subnet-router sees only subnet-router IPs, big-router sees only big-router IPs) - // - // ROOT CAUSE: - // 1. Exit routes (0.0.0.0/0) are treated as covering all destinations - // 2. Filter reduction logic scopes DstPorts to per-node visibility - // - // FIX REQUIRED: - // 1. Exclude exit routes from tag-based filter distribution - // 2. Show full destination set to all destination nodes (not per-node scoped) - { - name: "T1_tags_resolve_to_ips_not_routes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:router"], "dst": ["tag:router:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "exit-node": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - "subnet-router": { SrcIPs: all tag:router, DstPorts: ALL tag:router IPs }, - "multi-router": { SrcIPs: all tag:router, DstPorts: ALL tag:router IPs }, - "big-router": { SrcIPs: all tag:router, DstPorts: ALL tag:router IPs }, - }, - */ - // ACTUAL (Headscale): Exit gets filter, per-node DstPorts scoped to own IPs - // tag:router = subnet-router (100.119.139.79), multi-router (100.74.117.7), big-router (100.100.100.1) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "ha-router1": nil, - "ha-router2": nil, - "user1": nil, - // INCORRECT: exit-node gets filter due to 0.0.0.0/0 coverage - "exit-node": { - { - SrcIPs: []string{ - "100.100.100.1/32", // big-router - "100.119.139.79/32", // subnet-router - "100.74.117.7/32", // multi-router - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // exit-node sees all tag:router IPs (via 0.0.0.0/0 coverage) - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: subnet-router only sees its own IPs in DstPorts - "subnet-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // Per-node scoped: only subnet-router's own IPs - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: multi-router sees ALL IPs (it has tag:router AND tag:exit with 0.0.0.0/0) - "multi-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // multi-router sees ALL tag:router IPs (it has 0.0.0.0/0 exit route coverage) - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: big-router only sees its own IPs in DstPorts - "big-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // Per-node scoped: only big-router's own IPs - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // T2: tag:exit to tag:exit - { - name: "T2_tag_to_tag_with_exit", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:exit"], "dst": ["tag:exit:*"]} - `), - // tag:exit = exit-node, multi-router - // DstPorts = node IPs only, NOT exit routes - 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": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // Node IPs only - no exit routes 0.0.0.0/0, ::/0 - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.121.32.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::7f01:2004/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.121.32.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::7f01:2004/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // T5: Multi-tag node appears in both src and dst - { - name: "T5_multi_tag_node_in_both", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:router"], "dst": ["tag:exit:*"]} - `), - // multi-router has BOTH tag:router and tag:exit - // It should appear in BOTH SrcIPs (as router) and DstPorts (as exit) - 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": { - { - // Source: tag:router nodes - SrcIPs: []string{ - "100.100.100.1/32", // big-router - "100.119.139.79/32", // subnet-router - "100.74.117.7/32", // multi-router (has both tags) - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // Dest: tag:exit nodes (exit-node + multi-router) - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.121.32.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::7f01:2004/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.121.32.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::7f01:2004/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatProtocolPort tests protocol and port restrictions on subnet routes. -// Category G: Tests from 13-route-acl-interactions.md focusing on protocol/port handling. -func TestTailscaleRoutesCompatProtocolPort(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // G1: Port restriction on subnet (22 only) - // - // TAILSCALE BEHAVIOR: - // - Only subnet-router and big-router get filters - // - Exit nodes do NOT get filters for subnet destinations - // - // HEADSCALE BEHAVIOR: - // - Exit nodes also get filters because 0.0.0.0/0 "covers" everything - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "G1_port_restriction_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:22"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { filter with port 22 }, - "big-router": { filter with port 22 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters due to 0.0.0.0/0 coverage - "exit-node": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // G2: Port range on subnet (80-443) - // - // TAILSCALE BEHAVIOR: - // - Only subnet-router and big-router get filters - // - Exit nodes do NOT get filters for subnet destinations - // - // HEADSCALE BEHAVIOR: - // - Exit nodes also get filters because 0.0.0.0/0 "covers" everything - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "G2_port_range_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:80-443"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "exit-node": nil, - "multi-router": nil, - "subnet-router": { filter with port 80-443 }, - "big-router": { filter with port 80-443 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters due to 0.0.0.0/0 coverage - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit node route coverage to match Tailscale behavior - // - // G7: All ports wildcard - // - // TAILSCALE BEHAVIOR: - // - Only subnet-router and big-router get filters - // - Exit nodes do NOT get filters for subnet destinations - // - // HEADSCALE BEHAVIOR: - // - Exit nodes also get filters because 0.0.0.0/0 "covers" everything - // - // ROOT CAUSE: - // Exit routes (0.0.0.0/0) are treated as covering all destinations - // - // FIX REQUIRED: - // Exclude exit routes from filter distribution coverage checks - { - name: "G7_all_ports_wildcard", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "exit-node": nil, - "multi-router": nil, - "subnet-router": { filter with all ports }, - "big-router": { filter with all ports }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit nodes get filters due to 0.0.0.0/0 coverage - "exit-node": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::2d01:c747/128", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatIPv6 tests IPv6-specific route behavior. -// Category I: Tests from 15-overlapping-subnets.md focusing on IPv6 handling. -func TestTailscaleRoutesCompatIPv6(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - - // Create nodes with IPv6 subnet routes - nodeClient1 := &types.Node{ - ID: 1, - GivenName: "client1", - User: &users[0], - UserID: &users[0].ID, - IPv4: ptrAddr("100.116.73.38"), - IPv6: ptrAddr("fd7a:115c:a1e0::a801:4949"), - Hostinfo: &tailcfg.Hostinfo{}, - ApprovedRoutes: []netip.Prefix{}, - } - - // IPv6 subnet router - nodeIPv6Router := &types.Node{ - ID: 2, - GivenName: "ipv6-router", - IPv4: ptrAddr("100.119.139.80"), - IPv6: ptrAddr("fd7a:115c:a1e0::4001:8ba1"), - Tags: []string{"tag:router"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("fd00::/48"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("fd00::/48"), - }, - } - - // IPv6 child route (more specific) - nodeIPv6ChildRouter := &types.Node{ - ID: 3, - GivenName: "ipv6-child-router", - IPv4: ptrAddr("100.119.139.81"), - IPv6: ptrAddr("fd7a:115c:a1e0::4001:8ba2"), - Tags: []string{"tag:router"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("fd00:1::/64"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("fd00:1::/64"), - }, - } - - // IPv6 exit node (with ::/0) - nodeIPv6Exit := &types.Node{ - ID: 4, - GivenName: "ipv6-exit", - IPv4: ptrAddr("100.121.32.2"), - IPv6: ptrAddr("fd7a:115c:a1e0::7f01:2005"), - Tags: []string{"tag:exit"}, - Hostinfo: &tailcfg.Hostinfo{ - RoutableIPs: []netip.Prefix{ - netip.MustParsePrefix("::/0"), - }, - }, - ApprovedRoutes: []netip.Prefix{ - netip.MustParsePrefix("::/0"), - }, - } - - nodes := types.Nodes{nodeClient1, nodeIPv6Router, nodeIPv6ChildRouter, nodeIPv6Exit} - - tests := []routesCompatTest{ - // TODO: Fix wildcard DstPorts format, SrcIPs to include subnet routes, and filter distribution - // - // I1: IPv6 subnet route with wildcard ACL - // - // TAILSCALE BEHAVIOR: - // - SrcIPs includes IPv6 subnet route (fd00::/48) in wildcard expansion - // - DstPorts uses {IP: "*"} for wildcard destinations - // - Only client1 receives a filter (filter placed on destination node) - // - Other nodes (routers) do NOT receive filters for wildcard dst - // - // HEADSCALE BEHAVIOR: - // - SrcIPs doesn't include subnet routes, only CGNAT ranges - // - DstPorts expands to CGNAT ranges instead of "*" - // - ALL nodes receive filters (incorrect filter distribution) - // - // ROOT CAUSE: - // 1. Headscale doesn't include subnet routes in wildcard SrcIPs - // 2. Headscale expands "*" to CGNAT ranges instead of using "*" - // 3. Headscale distributes filters to all nodes instead of only the destination - // - // FIX REQUIRED: - // 1. Include advertised subnet routes in wildcard SrcIPs - // 2. Use {IP: "*"} for wildcard destinations - // 3. Fix filter distribution to only send to destination nodes - { - name: "I1_ipv6_subnet_route", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - // Wildcard ACL - SrcIPs should include IPv6 route (fd00::/48) - SrcIPs: []string{ - "100.64.0.0/10", - "fd00::/48", // IPv6 subnet route - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ipv6-router": nil, - "ipv6-child-router": nil, - "ipv6-exit": nil, - }, - */ - // ACTUAL (Headscale): All nodes get filters with CGNAT DstPorts - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: All routers get filters (should be nil) - "ipv6-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ipv6-child-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ipv6-exit": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix IPv6 parent route coverage - // - // I4: IPv6 specific ACL targeting fd00:1::/64 - // - // TAILSCALE BEHAVIOR: - // - ipv6-router (fd00::/48) covers fd00:1::/64 - should get filter - // - ipv6-child-router (fd00:1::/64) exact match - should get filter - // - ipv6-exit (::/0) covers everything - should get filter - // - // HEADSCALE BEHAVIOR: - // - ipv6-router (fd00::/48) does NOT get filter - Headscale doesn't recognize - // that fd00::/48 covers fd00:1::/64 (parent route coverage not working) - // - ipv6-child-router (fd00:1::/64) gets filter (exact match works) - // - ipv6-exit (::/0) gets filter (IPv6 exit route coverage works) - // - // ROOT CAUSE: - // Headscale's route coverage logic doesn't properly handle IPv6 parent routes. - // fd00::/48 should cover fd00:1::/64 but Headscale doesn't recognize this. - // - // FIX REQUIRED: - // Fix IPv6 parent route coverage in filter distribution logic. - { - name: "I4_ipv6_specific_acl", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["fd00:1::/64:443"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - // ipv6-router should get filter (fd00::/48 covers fd00:1::/64) - "ipv6-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1::/64", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ipv6-child-router should also get filter (exact match) - "ipv6-child-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1::/64", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ipv6-exit should get filter (::/0 covers everything) - "ipv6-exit": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1::/64", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): ipv6-router doesn't get filter (parent route coverage broken) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - // INCORRECT: ipv6-router doesn't get filter (should based on parent coverage) - "ipv6-router": nil, - // ipv6-child-router gets filter (exact match works) - "ipv6-child-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1::/64", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ipv6-exit gets filter (::/0 covers everything) - "ipv6-exit": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1::/64", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix IPv6 parent route coverage - // - // I5: IPv6 parent/child route coverage - // - // TAILSCALE BEHAVIOR: - // - ipv6-router (fd00::/48) covers fd00:1:2::/80 - should get filter - // - ipv6-child-router (fd00:1::/64) does NOT cover fd00:1:2::/80 - // (fd00:1::/64 = fd00:0001:0000::/64, fd00:1:2::/80 = fd00:0001:0002::/80 - different) - // - ipv6-exit (::/0) covers everything - should get filter - // - // HEADSCALE BEHAVIOR: - // - ipv6-router (fd00::/48) does NOT get filter - Headscale doesn't recognize - // that fd00::/48 covers fd00:1:2::/80 (parent route coverage not working) - // - ipv6-child-router correctly gets nil (fd00:1::/64 doesn't cover fd00:1:2::/80) - // - ipv6-exit gets filter (::/0 covers everything) - // - // ROOT CAUSE: - // Headscale's route coverage logic doesn't properly handle IPv6 parent routes. - // fd00::/48 should cover fd00:1:2::/80 but Headscale doesn't recognize this. - // - // FIX REQUIRED: - // Fix IPv6 parent route coverage in filter distribution logic. - { - name: "I5_ipv6_parent_child_routes", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["fd00:1:2::/80:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - // ipv6-router (fd00::/48) covers fd00:1:2::/80 - should get filter - "ipv6-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1:2::/80", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ipv6-child-router (fd00:1::/64) does NOT cover fd00:1:2::/80 - "ipv6-child-router": nil, - // ipv6-exit (::/0) covers everything - "ipv6-exit": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1:2::/80", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): ipv6-router doesn't get filter (parent route coverage broken) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - // INCORRECT: ipv6-router doesn't get filter (should based on parent coverage) - "ipv6-router": nil, - // ipv6-child-router correctly doesn't get filter - // (fd00:1::/64 doesn't cover fd00:1:2::/80) - "ipv6-child-router": nil, - // ipv6-exit gets filter (::/0 covers everything) - "ipv6-exit": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "fd7a:115c:a1e0::a801:4949/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00:1:2::/80", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // I7: IPv6 exit route coverage (external IPv6 destination) - { - name: "I7_ipv6_exit_coverage", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["2001:db8::/32:443"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "ipv6-router": nil, // fd00::/48 doesn't cover 2001:db8::/32 - "ipv6-child-router": nil, // fd00:1::/64 doesn't cover 2001:db8::/32 - // Only ipv6-exit (::/0) should get filter - "ipv6-exit": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "2001:db8::/32", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatEdgeCases tests edge cases and unusual configurations. -// Category H: Edge cases from various findings documents. -func TestTailscaleRoutesCompatEdgeCases(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix wildcard SrcIPs to include subnet routes like Tailscale - // - // H1: Verify wildcard SrcIPs format - // - // TAILSCALE BEHAVIOR: - // - SrcIPs includes CGNAT range + all advertised subnet routes - // - Exit nodes do NOT get filters for tag:router destination - // - // HEADSCALE BEHAVIOR: - // - SrcIPs only includes CGNAT range (no subnet routes) - // - Exit nodes also get filters due to exit route coverage - // - // ROOT CAUSE: - // 1. Headscale doesn't include subnet routes in wildcard SrcIPs - // 2. Exit routes (0.0.0.0/0) treated as covering all destinations - // - // FIX REQUIRED: - // 1. Include advertised subnet routes in wildcard SrcIPs - // 2. Exclude exit routes from filter distribution - { - name: "H1_wildcard_srcips_format", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["tag:router:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", "fd7a:115c:a1e0::/48", - "10.0.0.0/8", "10.33.0.0/16", "172.16.0.0/24", "192.168.1.0/24", // routes! - }, - DstPorts: ... tag:router IPs, - }, - }, - // ... multi-router and big-router with same SrcIPs pattern - }, - */ - // ACTUAL (Headscale): - // - SrcIPs missing routes - // - DstPorts only contains node's own IPs (not all tag:router IPs) - // - exit-node gets filter - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - // INCORRECT: DstPorts only contains self IPs, not all tag:router IPs - "subnet-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - // Only subnet-router's own IPs - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // multi-router has tag:router AND tag:exit, gets all tag:router IPs - "multi-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - // All tag:router IPs - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - // Only big-router's own IPs - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit node gets filter (should be nil) - // Exit-node has tag:exit, NOT tag:router, so shouldn't get filter - // But due to exit route coverage, it gets ALL tag:router IPs - "exit-node": { - { - SrcIPs: []string{ - "100.64.0.0/10", - "fd7a:115c:a1e0::/48", - }, - DstPorts: []tailcfg.NetPortRange{ - // All tag:router IPs (exit-node sees all because of route coverage) - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H9: Large prefix (/8) subnet route - // TODO: Fix exit route coverage and child route coverage - // TAILSCALE BEHAVIOR: Only big-router (has 10.0.0.0/8) gets the filter. - // subnet-router (10.33.0.0/16) is a CHILD of 10.0.0.0/8 - doesn't cover parent. - // Exit nodes do NOT get filters for specific subnet destinations. - // HEADSCALE BEHAVIOR: Exit nodes get filters (0.0.0.0/0 covers). subnet-router also - // gets filter (Headscale incorrectly treats child routes as covering). - // ROOT CAUSE: Two issues: (1) Exit route coverage, (2) Child route coverage. - // FIX REQUIRED: Exclude exit nodes and fix route coverage to only include parents. - { - name: "H9_large_prefix_works", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.0.0.0/8:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - }, - */ - // ACTUAL (Headscale): Exit nodes and child routes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // subnet-router incorrectly gets filter (child route coverage) - "subnet-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: memberSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H2: Wildcard DstPorts format - // TODO: Fix wildcard DstPorts format and filter distribution - // TAILSCALE BEHAVIOR: DstPorts uses {IP: "*"} for wildcard destinations. - // Only client1 receives a filter (filter placed on destination node). - // HEADSCALE BEHAVIOR: DstPorts expands to CGNAT ranges (100.64.0.0/10, fd7a:115c:a1e0::/48). - // ALL nodes receive filters. - // ROOT CAUSE: Two issues: (1) Headscale expands "*" to CGNAT ranges instead of using "*", - // (2) Headscale distributes filters to all nodes instead of only the destination. - // FIX REQUIRED: Use {IP: "*"} for wildcard destinations and fix filter distribution. - { - name: "H2_wildcard_dstports_format", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["*:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: memberSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - "user1": nil, - }, - */ - // ACTUAL (Headscale): DstPorts expanded to CGNAT, all nodes get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: memberSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H3: CGNAT range expansion in wildcard - // TODO: Fix filter distribution and exit route coverage for tag destinations - // TAILSCALE BEHAVIOR: Only tag:router nodes (subnet-router, multi-router, big-router) - // receive filters. Each receives DstPorts containing ALL tag:router node IPs. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (exit route covers tag:router IPs). - // Each node only sees its OWN IPs in DstPorts, not all tag:router IPs. - // ROOT CAUSE: Two issues: (1) Exit route coverage gives filters to exit-node, - // (2) Per-node DstPorts filtering shows only self IPs instead of all tag:router IPs. - // FIX REQUIRED: Exclude exit nodes from tag-based destinations, fix DstPorts to include all. - { - name: "H3_cgnat_range_expansion", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["tag:router:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // All tag:router node IPs - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, // big-router - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, // subnet-router - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, // multi-router - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": nil, // Also tag:router but expected to get filter - "big-router": nil, // Also tag:router but expected to get filter - }, - */ - // ACTUAL (Headscale): Each node only sees its own IPs in DstPorts, - // multi-router and big-router also get filters, exit-node incorrectly gets filter - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // Only self IPs - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // All tag:router IPs (multi-router sees all) - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // Only self IPs - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // exit-node incorrectly gets filter (exit route covers tag:router IPs) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.100.1/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H4: IPv6 range in SrcIPs - // TODO: Fix wildcard DstPorts format and filter distribution - // TAILSCALE BEHAVIOR: DstPorts uses {IP: "*"} for wildcard destinations. - // SrcIPs includes fd7a:115c:a1e0::/48 (IPv6 Tailscale range). Only client1 receives filter. - // HEADSCALE BEHAVIOR: DstPorts expands to CGNAT ranges. ALL nodes receive filters. - // ROOT CAUSE: Same as H2 - Headscale expands "*" to CGNAT and distributes to all nodes. - // FIX REQUIRED: Use {IP: "*"} for wildcard destinations and fix filter distribution. - { - name: "H4_ipv6_range_in_srcips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - "user1": nil, - }, - */ - // ACTUAL (Headscale): All nodes get filters with CGNAT DstPorts - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H7: Two nodes claiming same subnet - first is primary - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only ha-router1 and ha-router2 (which have 192.168.1.0/24) get filters. - // Exit nodes do NOT get filters for specific subnet destinations. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 192.168.1.0/24). - // ROOT CAUSE: Exit route coverage gives filters to exit-node and multi-router. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "H7_two_nodes_same_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers 192.168.1.0/24) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H10: Very small prefix (/32) - // TODO: Fix exit route coverage for /32 destinations - // TAILSCALE BEHAVIOR: Only subnet-router (10.33.0.0/16) and big-router (10.0.0.0/8) get filters. - // These routes cover 10.33.0.100/32. Exit nodes do NOT get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.0.100/32). - // ROOT CAUSE: Exit route coverage gives filters to exit-node and multi-router. - // FIX REQUIRED: Exclude exit nodes from specific IP destinations. - { - name: "H10_very_small_prefix", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.100/32:80"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalR tests additional route coverage scenarios (Category R). -func TestTailscaleRoutesCompatAdditionalR(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // R5: Route coverage check logic verification - // TODO: Exit route coverage issue - exit nodes get filters when they shouldn't. - // TAILSCALE BEHAVIOR: Exit nodes do NOT receive filters for internal subnet destinations. - // HEADSCALE BEHAVIOR: Exit nodes get filters because 0.0.0.0/0 covers 10.33.1.0/24. - // ROOT CAUSE: routeCoversDestination() returns true for exit routes covering internal IPs. - { - name: "R5_route_coverage_check_logic", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:22"]} - `), - // Route coverage: R.Bits() <= D.Bits() && R.Contains(D.Addr()) - // 10.0.0.0/8 (bits=8) <= 24 && contains 10.33.1.0 -> YES - // 10.33.0.0/16 (bits=16) <= 24 && contains 10.33.1.0 -> YES - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { ... }, - "big-router": { ... }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters due to exit route coverage - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route 0.0.0.0/0 covers 10.33.1.0/24) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // R6: IPv6 route coverage - // TODO: Exit route coverage issue - exit nodes get filters for IPv6 Tailscale range. - // TAILSCALE BEHAVIOR: No nodes get filters for IPv6 addresses in the Tailscale range - // (fd7a:115c:a1e0::/48) as these are node IPs, not routed destinations. - // HEADSCALE BEHAVIOR: Exit nodes get filters because ::/0 covers all IPv6 addresses. - // ROOT CAUSE: routeCoversDestination() returns true for exit routes covering all IPs. - { - name: "R6_ipv6_route_coverage", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["fd7a:115c:a1e0::1/128:443"]} - `), - // Targeting a specific IPv6 in the Tailscale range - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - }, - */ - // ACTUAL (Headscale): Exit nodes get filters (exit route ::/0 covers all IPv6) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - // Exit nodes incorrectly get filters (exit route ::/0 covers fd7a:115c:a1e0::1) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::1/128", Ports: tailcfg.PortRange{First: 443, Last: 443}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::1/128", Ports: tailcfg.PortRange{First: 443, Last: 443}}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // R7: Exit node IPv6 coverage - { - name: "R7_exit_ipv6_coverage", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["2001:db8::1/128:443"]} - `), - // External IPv6 address - only exit nodes cover - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - // Exit nodes cover all destinations - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "2001:db8::1/128", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "2001:db8::1/128", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // R8: Mixed IPv4/IPv6 coverage - // TODO: Multiple coverage issues in this test: - // 1. Exit route coverage: exit nodes get IPv4 filters (0.0.0.0/0 covers 10.33.0.0/16) - // 2. Node IP coverage: all nodes get IPv6 filters because their IPv6 addresses are in - // fd7a:115c:a1e0::/48 which overlaps with the destination fd7a:115c:a1e0::/64 - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters (IPv4 only). - // HEADSCALE BEHAVIOR: - // - All nodes get filters for IPv6 (node IPs are in fd7a:115c:a1e0::/48) - // - Exit nodes get filters for IPv4 (exit route covers 10.33.0.0/16) - // - subnet-router and big-router get both IPv4 and IPv6 - // ROOT CAUSE: Node IP prefixes incorrectly treated as routes covering destinations. - { - name: "R8_mixed_ipv4_ipv6_coverage", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*", "fd7a:115c:a1e0::/64:*"]} - `), - // Both IPv4 and IPv6 destinations - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { IPv4 only: 10.33.0.0/16 }, - "big-router": { IPv4 only: 10.33.0.0/16 }, - }, - */ - // ACTUAL (Headscale): Multiple issues - node IPs treated as routes, exit coverage - wantFilters: map[string][]tailcfg.FilterRule{ - // All nodes get IPv6 filters because their IPs are in fd7a:115c:a1e0::/48 - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{{IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}}, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // subnet-router and big-router get both IPv4 (from routes) and IPv6 (from node IPs) - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes get both IPv4 (exit route) and IPv6 (exit route + node IPs) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::/64", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalO tests additional overlapping route scenarios (Category O). -func TestTailscaleRoutesCompatAdditionalO(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // O1: Overlapping routes not merged - // TODO: Fix wildcard destination handling for nodes with routes - // TAILSCALE BEHAVIOR: Only client1 gets filters (dst *:* only goes to primary node). - // HEADSCALE BEHAVIOR: All nodes get filters (dst *:* expands to Headscale IP ranges for all nodes). - // ROOT CAUSE: Wildcard destination expands to Headscale IP ranges, not literal "*". - // FIX REQUIRED: Limit *:* distribution to match Tailscale behavior. - { - name: "O1_overlapping_routes_not_merged", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { filter with *:* }, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - "user1": nil, - }, - */ - // ACTUAL (Headscale): All nodes get filters with expanded IP ranges - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // All routers get filters because *:* expands to all Headscale IP ranges - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O4: Three-way hierarchy - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.1.128/25). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "O4_three_way_hierarchy", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.128/25:22"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { filter with 10.33.1.128/25:22 }, - "big-router": { filter with 10.33.1.128/25:22 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.128/25", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.128/25", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.128/25", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.128/25", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O5: Sibling routes with parent ACL - // TODO: Fix exit route coverage and subnet-router getting parent ACL filter - // TAILSCALE BEHAVIOR: Only big-router gets filters (exact /8 route match). - // HEADSCALE BEHAVIOR: Exit nodes get filters (0.0.0.0/0 covers 10.0.0.0/8), - // and subnet-router gets filters (10.33.0.0/16 is within /8). - // ROOT CAUSE: Exit route coverage + child routes get parent ACL filters. - // FIX REQUIRED: Exclude exit nodes and child routes from parent ACL. - { - name: "O5_sibling_routes_with_parent_acl", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.0.0.0/8:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": { filter with 10.0.0.0/8:* }, - }, - */ - // ACTUAL (Headscale): Exit nodes and subnet-router also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // subnet-router incorrectly gets filter (child route within parent ACL) - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O7: Specific IP targeting with multiple covering routes - // TODO: Fix exit route coverage for specific IP destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.0.100/32). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "O7_specific_ip_targeting", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.100/32:80"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { filter with 10.33.0.100/32:80 }, - "big-router": { filter with 10.33.0.100/32:80 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.100/32", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O10: ACL dest covered by multiple routes - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.1.0/24). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "O10_acl_dest_covered_by_multiple", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:22"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { filter with 10.33.1.0/24:22 }, - "big-router": { filter with 10.33.1.0/24:22 }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O11: ACL dest not covered by any route - // TODO: Fix exit route coverage for uncovered destinations - // TAILSCALE BEHAVIOR: No nodes get filters (no route covers 192.168.99.0/24). - // HEADSCALE BEHAVIOR: Exit nodes get filters (0.0.0.0/0 covers 192.168.99.0/24). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from uncovered destinations. - { - name: "O11_acl_dest_not_covered", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.99.0/24:22"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - }, - */ - // ACTUAL (Headscale): Exit nodes get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.99.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.99.0/24", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalG tests additional protocol and port scenarios (Category G). -func TestTailscaleRoutesCompatAdditionalG(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // G3: Multiple ports on subnet - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.0.0/16). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "G3_multiple_ports_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:22,80,443"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, - "multi-router": nil, - "subnet-router": { ... }, - "big-router": { ... }, - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // G8: Default IPProto (all protocols) - // TODO: Fix exit route coverage for subnet destinations - // TAILSCALE BEHAVIOR: Only subnet-router and big-router get filters. - // HEADSCALE BEHAVIOR: Exit nodes also get filters (0.0.0.0/0 covers 10.33.0.0/16). - // ROOT CAUSE: Exit route coverage. - // FIX REQUIRED: Exclude exit nodes from subnet-specific destinations. - { - name: "G8_default_ipproto", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:22"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "exit-node": nil, - "multi-router": nil, - ... - }, - */ - // ACTUAL (Headscale): Exit nodes also get filters - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - // TCP=6, UDP=17, ICMP=1, ICMPv6=58 - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes incorrectly get filters (exit route covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalT tests additional tag resolution scenarios (Category T). -func TestTailscaleRoutesCompatAdditionalT(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix wildcard destination expansion and filter distribution - // - // T3: Tag source includes all tagged nodes - // - // TAILSCALE BEHAVIOR: - // - Only client1 gets filter (user-owned, thus a valid destination) - // - DstPorts uses literal "*" for wildcard destination - // - // HEADSCALE BEHAVIOR: - // - ALL nodes get filters (wildcard destination distributed to everyone) - // - DstPorts expands to CGNAT ranges instead of "*" - // - // ROOT CAUSE: - // 1. Wildcard destination distributed to all nodes instead of only non-source nodes - // 2. DstPorts expands wildcards to explicit CGNAT ranges - // - // FIX REQUIRED: - // 1. Limit filter distribution for wildcard destinations - // 2. Use literal "*" in DstPorts for wildcard destinations - { - name: "T3_tag_src_includes_all_tagged", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:router"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ tag:router IPs }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - }, - }, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "ha-router1": nil, - "ha-router2": nil, - "big-router": nil, - "user1": nil, - }, - */ - // ACTUAL (Headscale): ALL nodes get filters, DstPorts expanded to CGNAT ranges - // tag:router = subnet-router, multi-router, big-router - wantFilters: map[string][]tailcfg.FilterRule{ - // INCORRECT: All nodes get filters, not just destination nodes - "client1": { - { - SrcIPs: []string{ - "100.100.100.1/32", // big-router - "100.119.139.79/32", // subnet-router - "100.74.117.7/32", // multi-router - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - // INCORRECT: DstPorts uses CGNAT ranges instead of "*" - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix per-node DstPorts visibility and exit route coverage - // - // T4: Tag destination includes all tagged nodes - // - // TAILSCALE BEHAVIOR: - // - Only ha-router1 and ha-router2 get filters (tag:ha nodes) - // - DstPorts shows ALL tag:ha node IPs to each node - // - exit-node and multi-router do NOT get filters - // - // HEADSCALE BEHAVIOR: - // - Exit nodes also get filter (0.0.0.0/0 route "covers" tag:ha IPs) - // - Per-node DstPorts visibility: each node only sees its OWN IP in DstPorts - // - // ROOT CAUSE: - // 1. Exit routes (0.0.0.0/0) are treated as covering all destinations - // 2. Filter reduction logic scopes DstPorts to per-node visibility - // - // FIX REQUIRED: - // 1. Exclude exit routes from tag-based filter distribution - // 2. Show full destination set to all destination nodes (not per-node scoped) - { - name: "T4_tag_dst_includes_all_tagged", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["tag:ha:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "exit-node": nil, - "multi-router": nil, - "big-router": nil, - "user1": nil, - "ha-router1": { DstPorts: ALL tag:ha IPs }, - "ha-router2": { DstPorts: ALL tag:ha IPs }, - }, - */ - // ACTUAL (Headscale): Exit nodes get filters, per-node DstPorts scoped - // tag:ha = ha-router1 (100.85.37.108), ha-router2 (100.119.130.32) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "subnet-router": nil, - "big-router": nil, - "user1": nil, - // INCORRECT: exit-node gets filter due to 0.0.0.0/0 coverage - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - // exit-node sees ALL tag:ha IPs via exit route coverage - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.119.130.32/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.85.37.108/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4501:82a9/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::f101:2597/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: multi-router gets filter due to 0.0.0.0/0 coverage - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - // multi-router sees ALL tag:ha IPs via exit route coverage - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.119.130.32/32", Ports: tailcfg.PortRangeAny}, - {IP: "100.85.37.108/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4501:82a9/128", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::f101:2597/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: ha-router1 only sees its own IPs in DstPorts - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - // Per-node scoped: only ha-router1's own IPs - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.85.37.108/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::f101:2597/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: ha-router2 only sees its own IPs in DstPorts - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - // Per-node scoped: only ha-router2's own IPs - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.119.130.32/32", Ports: tailcfg.PortRangeAny}, - {IP: "fd7a:115c:a1e0::4501:82a9/128", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAutoApprover tests autoApprover behavior (Category D). -// These tests validate automatic route approval based on tags and prefixes. -// NOTE: AutoApprover affects route ENABLING, not filter distribution. -// The filter tests here verify filters ASSUMING routes are enabled. -func TestTailscaleRoutesCompatAutoApprover(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // D1: Basic route auto-approval with autoApprover - // 10.0.0.0/8 -> tag:router means routes within 10.0.0.0/8 - // advertised by nodes with tag:router are auto-approved - { - name: "D1_basic_route_auto_approval", - // This test validates that with autoApprover configured, - // routes matching the prefix/tag combination are enabled. - // Filter distribution follows standard rules. - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*"]} - `), - // Assuming route is auto-approved and enabled: - // Filter goes to subnet-router (route owner) + big-router (parent route) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - // subnet-router owns 10.33.0.0/16 - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // big-router owns 10.0.0.0/8 (covers 10.33.0.0/16) - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // exit-node and multi-router also get filter (0.0.0.0/0 covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D2: Nested prefix approval - autoApprover for parent covers child - { - name: "D2_nested_prefix_approval", - // autoApprover 10.0.0.0/8 covers advertised 10.33.0.0/16 - // This test verifies subset prefixes are approved - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:22"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D3: Exact prefix approval - { - name: "D3_exact_prefix_approval", - // autoApprover for exactly 10.33.0.0/16 matches advertised 10.33.0.0/16 - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D4: Prefix not covered by autoApprover - // 192.168.0.0/16, but node advertises 10.0.0.0/8 - NOT approved - // Without approval, route not enabled, no filters distributed - { - name: "D4_prefix_not_covered", - // If autoApprover is 192.168.0.0/16 but we target 10.0.0.0/8 - // the route would NOT be auto-approved - // This tests that only matching prefixes get filters - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["192.168.1.0/24:*"]} - `), - // Only HA routers own 192.168.1.0/24 - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, - "big-router": nil, - // exit-node and multi-router get filter (0.0.0.0/0 covers) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D5: Wrong tag not approved - // autoApprover 10.0.0.0/8 -> tag:router, but node is tag:ha - { - name: "D5_wrong_tag_not_approved", - // HA routers have tag:ha, not tag:router - // Their 192.168.1.0/24 route would not be auto-approved - // by an autoApprover for tag:router - // But we can still target the route in ACL if manually enabled - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:router"], "dst": ["192.168.1.0/24:*"]} - `), - // tag:router sources: subnet-router, multi-router, big-router - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "subnet-router": nil, // Source, not destination - "big-router": nil, // Source, not destination - "exit-node": { - { - SrcIPs: []string{ - "100.100.100.1/32", // big-router - "100.119.139.79/32", // subnet-router - "100.74.117.7/32", // multi-router - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.100.100.1/32", - "100.119.139.79/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::4001:8ba0/128", - "fd7a:115c:a1e0::6401:6401/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix wildcard DstPorts expansion to use "*" instead of CGNAT ranges - // - // D6: Exit node auto-approval - wildcard ACL with routes - // - // TAILSCALE BEHAVIOR: - // - DstPorts uses literal "*" for wildcard destination - // - All nodes get filter with DstPorts: [{IP: "*", Ports: 0-65535}] - // - // HEADSCALE BEHAVIOR: - // - DstPorts expands to CGNAT ranges instead of using "*" - // - Uses {IP: "100.64.0.0/10"} and {IP: "fd7a:115c:a1e0::/48"} - // - // ROOT CAUSE: - // Headscale expands wildcard destinations to explicit IP ranges - // instead of using the "*" shorthand that Tailscale uses - // - // FIX REQUIRED: - // Use literal "*" in DstPorts for wildcard destinations - { - name: "D6_exit_node_auto_approval", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ... all other nodes same pattern with DstPorts: [{IP: "*"}] - }, - */ - // ACTUAL (Headscale): DstPorts expanded to CGNAT ranges - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix wildcard DstPorts expansion to use "*" instead of CGNAT ranges - // - // D7: Exit auto-approval wrong tag - tag:exit to wildcard destination - // - // TAILSCALE BEHAVIOR: - // - DstPorts uses literal "*" for wildcard destination - // - tag:exit (exit-node, multi-router) can access anywhere - // - // HEADSCALE BEHAVIOR: - // - DstPorts expands to CGNAT ranges instead of using "*" - // - Uses {IP: "100.64.0.0/10"} and {IP: "fd7a:115c:a1e0::/48"} - // - // ROOT CAUSE: - // Headscale expands wildcard destinations to explicit IP ranges - // - // FIX REQUIRED: - // Use literal "*" in DstPorts for wildcard destinations - { - name: "D7_exit_auto_approval_wrong_tag", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["tag:exit"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.121.32.1/32", "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ... all other nodes same pattern with DstPorts: [{IP: "*"}] - }, - */ - // ACTUAL (Headscale): DstPorts expanded to CGNAT ranges - // tag:exit = exit-node, multi-router - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: []string{ - "100.121.32.1/32", // exit-node - "100.74.117.7/32", // multi-router - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.121.32.1/32", - "100.74.117.7/32", - "fd7a:115c:a1e0::7f01:2004/128", - "fd7a:115c:a1e0::c401:7508/128", - }, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D8: Auto-approval enables route, but ACL still enforced - // Route is enabled via autoApprover, but restrictive ACL limits access - { - name: "D8_auto_approval_acl_interaction", - // Route auto-approved, but ACL only allows specific source - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["10.33.0.0/16:22"]} - `), - // Only autogroup:member sources (user-owned nodes) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, // Source, not destination - "client2": nil, // Source, not destination - "user1": nil, // Source, not destination - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", // client1 - "100.89.42.23/32", // client2 - "100.90.199.68/32", // user1 - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D9: Auto-approval triggers on advertise - // Policy exists first, then node advertises - triggers approval - // This is a state/timing test - filter distribution is the same - { - name: "D9_auto_approval_triggers_on_advertise", - // Same as D1 - validates consistent behavior - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D10: Auto-approval retroactive - // Node advertised first, policy added later - requires re-advertisement - // Same filter distribution as D1 when route is enabled - { - name: "D10_auto_approval_retroactive", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:443"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // D11: Overlapping auto-approvers - // 10.0.0.0/8 -> tag:router, 10.33.0.0/16 -> tag:special - // Both are valid for their respective tags - { - name: "D11_overlapping_auto_approvers", - // Both big-router (10.0.0.0/8) and subnet-router (10.33.0.0/16) - // can be approved by different autoApprover rules - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.0.0.0/8:80"]} - `), - // Targeting 10.0.0.0/8 - only big-router exact match + exit nodes - // subnet-router's 10.33.0.0/16 is WITHIN 10.0.0.0/8 so also gets filter - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.0.0.0/8", Ports: tailcfg.PortRange{First: 80, Last: 80}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalProtocol tests additional protocol restrictions (G4-G6). -func TestTailscaleRoutesCompatAdditionalProtocol(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // G4: Protocol ICMP only - // proto:icmp results in IPProto=[1] (ICMP only) - // NOTE: Exit nodes still get filters due to exit route coverage issue (separate TODO) - { - name: "G4_protocol_icmp_subnet", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*"], "proto": "icmp"} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolICMP}, - }, - }, - // Exit nodes also get filters (exit route coverage issue) - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolICMP}, - }, - }, - }, - }, - // G5: Protocol TCP only - { - name: "G5_protocol_tcp_only", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:22"], "proto": "tcp"} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - // TCP only - IPProto: []int{ProtocolTCP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP}, - }, - }, - }, - }, - // G6: Protocol UDP only - { - name: "G6_protocol_udp_only", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:53"], "proto": "udp"} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - // UDP only - IPProto: []int{ProtocolUDP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolUDP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolUDP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRange{First: 53, Last: 53}}, - }, - IPProto: []int{ProtocolUDP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalEdgeCases tests additional edge cases (H5, H6, H8, H11). -func TestTailscaleRoutesCompatAdditionalEdgeCases(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // H5: Subnet overlaps CGNAT - cannot be enabled - // Route 100.64.0.0/24 overlaps with Tailscale CGNAT range - { - name: "H5_subnet_overlaps_cgnat", - // A route overlapping CGNAT cannot be enabled - // This test verifies no filters are distributed for such routes - // Using a normal subnet route as baseline - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["100.64.0.0/24:*"]} - `), - // TODO: Tailscale blocks routes overlapping CGNAT - // Headscale behavior may differ - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": nil, - "big-router": nil, - // Exit nodes might still get filter since 0.0.0.0/0 covers everything - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.64.0.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.64.0.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H6: Loopback routes not distributed - // Route 127.0.0.1/32 can be advertised but NOT in peer AllowedIPs - { - name: "H6_loopback_routes_not_distributed", - // Loopback routes are not practical but test edge case handling - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["127.0.0.1/32:*"]} - `), - // TODO: Tailscale allows advertising loopback but doesn't distribute - // Verify Headscale behavior - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": nil, - "big-router": nil, - // Exit nodes might get filter - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "127.0.0.1/32", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "127.0.0.1/32", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H8: CGNAT overlap blocked - // TODO: Fix CGNAT overlap route handling - // TAILSCALE BEHAVIOR: Routes overlapping CGNAT (100.64.0.0/10) are blocked. - // Only exit nodes get filters for destinations in the blocked range. - // HEADSCALE BEHAVIOR: big-router gets filter because its IP (100.100.100.1) - // is within the destination range 100.100.0.0/16. - // ROOT CAUSE: Headscale checks if node IPs are in destination range, - // not just if advertised routes cover the destination. - // FIX REQUIRED: May need to exclude nodes whose IPs are in destination range. - { - name: "H8_cgnat_overlap_blocked", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["100.100.0.0/16:*"]} - `), - /* - EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": nil, - "big-router": nil, // No filter expected - "exit-node": { ... }, - "multi-router": { ... }, - }, - */ - // ACTUAL (Headscale): big-router gets filter (its IP 100.100.100.1 is in 100.100.0.0/16) - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": nil, - // big-router gets filter because its IP (100.100.100.1) is in destination range - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // H11: IPv6 small prefix /128 - { - name: "H11_ipv6_small_prefix", - // /128 is a single IPv6 address - smallest possible prefix - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["fd00::1/128:443"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": nil, - "big-router": nil, - // Exit nodes with ::/0 cover all IPv6 - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00::1/128", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "fd00::1/128", Ports: tailcfg.PortRange{First: 443, Last: 443}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalIPv6 tests additional IPv6 scenarios (I2, I3, I6). -func TestTailscaleRoutesCompatAdditionalIPv6(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // TODO: Fix wildcard DstPorts format - // - // I2: IPv6 exit route ::/0 - verifies ::/0 NOT in SrcIPs - // - // TAILSCALE BEHAVIOR: - // - DstPorts uses {IP: "*"} for wildcard destinations - // - ::/0 does NOT appear in SrcIPs (exit routes excluded) - // - Filter distributed to all nodes - // - // HEADSCALE BEHAVIOR: - // - DstPorts expands to CGNAT ranges instead of "*" - // - ::/0 correctly excluded from SrcIPs - // - Filter distributed to all nodes (same as Tailscale) - // - // ROOT CAUSE: - // Headscale expands "*" to CGNAT ranges instead of using "*". - // - // FIX REQUIRED: - // Use {IP: "*"} for wildcard destinations. - { - name: "I2_ipv6_exit_route", - // ::/0 is the IPv6 exit route (like 0.0.0.0/0 for IPv4) - // Should NOT appear in SrcIPs - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["*:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "*", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ... same for all nodes with {IP: "*"} - }, - */ - // ACTUAL (Headscale): DstPorts expanded to CGNAT ranges - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "client2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "user1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router1": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: wildcardDstPorts, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit route coverage and per-node DstPorts filtering - // - // I3: IPv6 in wildcard SrcIPs - // - // TAILSCALE BEHAVIOR: - // - SrcIPs includes fd7a:115c:a1e0::/48 (IPv6 Tailscale range) - CORRECT - // - Only tag:router nodes receive filters (subnet-router, multi-router, big-router) - // - Exit-node (tag:exit only) does NOT get filter - // - Each tag:router node sees ALL tag:router IPs in DstPorts - // - // HEADSCALE BEHAVIOR: - // - SrcIPs correctly includes fd7a:115c:a1e0::/48 (IPv6 range works) - // - Exit-node incorrectly gets filter (exit route covers all tag:router IPs) - // - Each node only sees its OWN IPs in DstPorts (not all tag:router IPs) - // - // ROOT CAUSE: - // 1. Exit route coverage: exit-node's 0.0.0.0/0 + ::/0 covers tag:router IPs - // 2. Per-node DstPorts: Headscale only includes self IPs in DstPorts - // - // FIX REQUIRED: - // 1. Exclude exit nodes from tag-based destinations - // 2. Include all matching tag IPs in DstPorts for each destination node - { - name: "I3_ipv6_in_wildcard_srcips", - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["tag:router:22"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, // tag:exit, NOT tag:router - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // ALL tag:router IPs - {IP: "100.100.100.1/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // ... multi-router and big-router with same ALL tag:router IPs - }, - */ - // ACTUAL (Headscale): exit-node gets filter, each node sees only self IPs - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - // INCORRECT: subnet-router only sees its own IPs - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // Only self IPs (missing big-router and multi-router IPs) - {IP: "100.119.139.79/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // multi-router has tag:router AND tag:exit, gets all tag:router IPs - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // All tag:router IPs (multi-router sees all) - {IP: "100.100.100.1/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: big-router only sees its own IPs - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - // Only self IPs (missing subnet-router and multi-router IPs) - {IP: "100.100.100.1/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit-node gets filter (should be nil - tag:exit not tag:router) - // Due to exit route coverage, it sees all tag:router IPs - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "100.100.100.1/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "100.119.139.79/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "100.74.117.7/32", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::4001:8ba0/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::6401:6401/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - {IP: "fd7a:115c:a1e0::c401:7508/128", Ports: tailcfg.PortRange{First: 22, Last: 22}}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // TODO: Fix exit route coverage for subnet destinations - // - // I6: Dual-stack node - targeting both IPv4 and IPv6 subnets - // - // TAILSCALE BEHAVIOR: - // - Only subnet-router (10.33.0.0/16) and big-router (10.0.0.0/8) get IPv4 filter - // - No node has fd00:1::/64 route, so no node gets IPv6 filter - // - Exit nodes do NOT get filters for specific subnet destinations - // - Multiple rules with same SrcIPs kept as separate rules - // - // HEADSCALE BEHAVIOR: - // - Exit nodes get filters (exit route covers both subnets) - // - Rules with same SrcIPs and IPProto are MERGED into single rule - // with combined DstPorts - // - No node owns fd00:1::/64, but exit nodes cover it via ::/0 - // - // ROOT CAUSE: - // 1. Exit route coverage: 0.0.0.0/0 and ::/0 cover all subnets - // 2. Filter rule merging: Headscale merges rules with identical SrcIPs/IPProto - // - // FIX REQUIRED: - // Exclude exit nodes from specific subnet destinations. - { - name: "I6_dual_stack_node", - // Target both IPv4 and IPv6 subnets - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.0.0/16:*"]}, - {"action": "accept", "src": ["*"], "dst": ["fd00:1::/64:*"]} - `), - /* EXPECTED (Tailscale): - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "exit-node": nil, // Exit nodes shouldn't get subnet filters - "multi-router": nil, // Only has 172.16.0.0/24 + exit routes - // subnet-router owns 10.33.0.0/16 - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // big-router covers 10.33.0.0/16 via 10.0.0.0/8 - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - */ - // ACTUAL (Headscale): Exit nodes cover both, rules merged - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - // subnet-router owns 10.33.0.0/16 - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // big-router covers 10.33.0.0/16 via 10.0.0.0/8 - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: Exit-node gets MERGED filter covering both subnets - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - // Both destinations merged into single rule - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - {IP: "fd00:1::/64", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // INCORRECT: multi-router gets MERGED filter covering both subnets - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - // Both destinations merged into single rule - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.0.0/16", Ports: tailcfg.PortRangeAny}, - {IP: "fd00:1::/64", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} - -// TestTailscaleRoutesCompatAdditionalOverlapping tests additional overlapping route scenarios (O8, O9). -func TestTailscaleRoutesCompatAdditionalOverlapping(t *testing.T) { - t.Parallel() - - users := setupRouteCompatUsers() - nodes := setupRouteCompatNodes(users) - - tests := []routesCompatTest{ - // O8: Same node overlapping routes - // Node with 10.0.0.0/8, 10.33.0.0/16, 10.33.1.0/24 - NOT merged - { - name: "O8_same_node_overlapping_routes", - // If a single node advertises multiple overlapping routes, - // they should all appear separately, not merged - // big-router has 10.0.0.0/8 - // Let's target a specific child prefix - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["*"], "dst": ["10.33.1.0/24:*"]} - `), - // big-router (10.0.0.0/8) and subnet-router (10.33.0.0/16) both cover - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, - "client2": nil, - "user1": nil, - "ha-router1": nil, - "ha-router2": nil, - "subnet-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "big-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "exit-node": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: wildcardSrcIPs, - DstPorts: []tailcfg.NetPortRange{ - {IP: "10.33.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - // O9: Different nodes same route - // Two nodes with 192.168.1.0/24 - only first is primary - { - name: "O9_different_nodes_same_route", - // ha-router1 and ha-router2 both have 192.168.1.0/24 - // Both should receive filters, but only one is primary - policy: makeRoutesPolicy(` - {"action": "accept", "src": ["autogroup:member"], "dst": ["192.168.1.0/24:*"]} - `), - wantFilters: map[string][]tailcfg.FilterRule{ - "client1": nil, // Source - "client2": nil, // Source - "user1": nil, // Source - "subnet-router": nil, - "big-router": nil, - // Both HA routers get filter despite sharing route - "ha-router1": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "ha-router2": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - // Exit nodes also cover - "exit-node": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - "multi-router": { - { - SrcIPs: []string{ - "100.116.73.38/32", - "100.89.42.23/32", - "100.90.199.68/32", - "fd7a:115c:a1e0::a801:4949/128", - "fd7a:115c:a1e0::d01:2a2e/128", - "fd7a:115c:a1e0::2d01:c747/128", - }, - DstPorts: []tailcfg.NetPortRange{ - {IP: "192.168.1.0/24", Ports: tailcfg.PortRangeAny}, - }, - IPProto: []int{ProtocolTCP, ProtocolUDP, ProtocolICMP, ProtocolIPv6ICMP}, - }, - }, - }, - }, - } - - runRoutesCompatTests(t, users, nodes, tests) -} diff --git a/hscontrol/policy/v2/tailscale_routes_data_compat_test.go b/hscontrol/policy/v2/tailscale_routes_data_compat_test.go new file mode 100644 index 00000000..2cd6e07d --- /dev/null +++ b/hscontrol/policy/v2/tailscale_routes_data_compat_test.go @@ -0,0 +1,294 @@ +// This file implements a data-driven test runner for routes compatibility tests. +// It loads JSON golden files from testdata/routes_results/ROUTES-*.json and +// compares headscale's route-aware ACL engine output against the expected +// packet filter rules. +// +// Each JSON file contains: +// - A full policy (groups, tagOwners, hosts, acls) +// - A topology section with nodes, including routable_ips and approved_routes +// - Expected packet_filter_rules per node +// +// Test data source: testdata/routes_results/ROUTES-*.json +// Original source: Tailscale SaaS captures + headscale-generated expansions + +package v2 + +import ( + "encoding/json" + "net/netip" + "os" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/juanfont/headscale/hscontrol/policy/policyutil" + "github.com/juanfont/headscale/hscontrol/types" + "github.com/stretchr/testify/require" + "gorm.io/gorm" + "tailscale.com/tailcfg" +) + +// routesTestFile represents the JSON structure of a captured routes test file. +type routesTestFile struct { + TestID string `json:"test_id"` + Source string `json:"source"` + ParentTest string `json:"parent_test"` + Input struct { + FullPolicy json.RawMessage `json:"full_policy"` + } `json:"input"` + Topology routesTopology `json:"topology"` + Captures map[string]struct { + PacketFilterRules json.RawMessage `json:"packet_filter_rules"` + } `json:"captures"` +} + +// routesTopology describes the node topology for a routes test. +type routesTopology struct { + Users []struct { + ID uint `json:"id"` + Name string `json:"name"` + } `json:"users"` + Nodes map[string]routesNodeDef `json:"nodes"` +} + +// routesNodeDef describes a single node in the routes test topology. +type routesNodeDef struct { + ID int `json:"id"` + Hostname string `json:"hostname"` + IPv4 string `json:"ipv4"` + IPv6 string `json:"ipv6"` + Tags []string `json:"tags"` + User string `json:"user,omitempty"` + RoutableIPs []string `json:"routable_ips"` + ApprovedRoutes []string `json:"approved_routes"` +} + +// loadRoutesTestFile loads and parses a single routes test JSON file. +func loadRoutesTestFile(t *testing.T, path string) routesTestFile { + t.Helper() + + content, err := os.ReadFile(path) + require.NoError(t, err, "failed to read test file %s", path) + + var tf routesTestFile + + err = json.Unmarshal(content, &tf) + require.NoError(t, err, "failed to parse test file %s", path) + + return tf +} + +// buildRoutesUsersAndNodes constructs types.Users and types.Nodes from the +// JSON topology definition. This allows each test file to define its own +// topology (e.g., the IPv6 tests use different nodes than the standard tests). +func buildRoutesUsersAndNodes( + t *testing.T, + topo routesTopology, +) (types.Users, types.Nodes) { + t.Helper() + + // Build users + users := make(types.Users, 0, len(topo.Users)) + for _, u := range topo.Users { + users = append(users, types.User{ + Model: gorm.Model{ID: u.ID}, + Name: u.Name, + }) + } + + // Build nodes + nodes := make(types.Nodes, 0, len(topo.Nodes)) + + for _, nodeDef := range topo.Nodes { + node := &types.Node{ + ID: types.NodeID(nodeDef.ID), //nolint:gosec + GivenName: nodeDef.Hostname, + IPv4: ptrAddr(nodeDef.IPv4), + IPv6: ptrAddr(nodeDef.IPv6), + Tags: nodeDef.Tags, + } + + // Set up Hostinfo with RoutableIPs + hostinfo := &tailcfg.Hostinfo{} + + if len(nodeDef.RoutableIPs) > 0 { + routableIPs := make( + []netip.Prefix, + 0, + len(nodeDef.RoutableIPs), + ) + for _, r := range nodeDef.RoutableIPs { + routableIPs = append( + routableIPs, + netip.MustParsePrefix(r), + ) + } + + hostinfo.RoutableIPs = routableIPs + } + + node.Hostinfo = hostinfo + + // Set ApprovedRoutes + if len(nodeDef.ApprovedRoutes) > 0 { + approvedRoutes := make( + []netip.Prefix, + 0, + len(nodeDef.ApprovedRoutes), + ) + for _, r := range nodeDef.ApprovedRoutes { + approvedRoutes = append( + approvedRoutes, + netip.MustParsePrefix(r), + ) + } + + node.ApprovedRoutes = approvedRoutes + } else { + node.ApprovedRoutes = []netip.Prefix{} + } + + // Assign user if specified + if nodeDef.User != "" { + for i := range users { + if users[i].Name == nodeDef.User { + node.User = &users[i] + node.UserID = &users[i].ID + + break + } + } + } + + nodes = append(nodes, node) + } + + return users, nodes +} + +// routesSkipReasons documents WHY tests are expected to fail. +var routesSkipReasons = map[string]string{} + +// TestRoutesCompat is a data-driven test that loads all ROUTES-*.json test +// files and compares headscale's route-aware ACL engine output against the +// expected behavior. +func TestRoutesCompat(t *testing.T) { + t.Parallel() + + files, err := filepath.Glob( + filepath.Join("testdata", "routes_results", "ROUTES-*.json"), + ) + require.NoError(t, err, "failed to glob test files") + require.NotEmpty( + t, + files, + "no ROUTES-*.json test files found in testdata/routes_results/", + ) + + t.Logf("Loaded %d routes test files", len(files)) + + for _, file := range files { + tf := loadRoutesTestFile(t, file) + + t.Run(tf.TestID, func(t *testing.T) { + t.Parallel() + + if reason, ok := routesSkipReasons[tf.TestID]; ok { + t.Skipf( + "TODO: %s — see routesSkipReasons for details", + reason, + ) + + return + } + + // Build topology from JSON + users, nodes := buildRoutesUsersAndNodes(t, tf.Topology) + + // Parse and validate policy + pol, err := unmarshalPolicy(tf.Input.FullPolicy) + require.NoError( + t, + err, + "%s: policy should parse successfully", + tf.TestID, + ) + + err = pol.validate() + require.NoError( + t, + err, + "%s: policy should validate successfully", + tf.TestID, + ) + + for nodeName, capture := range tf.Captures { + t.Run(nodeName, func(t *testing.T) { + captureIsNull := len(capture.PacketFilterRules) == 0 || + string(capture.PacketFilterRules) == "null" //nolint:goconst + + node := findNodeByGivenName(nodes, nodeName) + if node == nil { + t.Skipf( + "node %s not found in topology", + nodeName, + ) + + return + } + + compiledRules, err := pol.compileFilterRulesForNode( + users, + node.View(), + nodes.ViewSlice(), + ) + require.NoError( + t, + err, + "%s/%s: failed to compile filter rules", + tf.TestID, + nodeName, + ) + + gotRules := policyutil.ReduceFilterRules( + node.View(), + compiledRules, + ) + + var wantRules []tailcfg.FilterRule + if !captureIsNull { + err = json.Unmarshal( + capture.PacketFilterRules, + &wantRules, + ) + require.NoError( + t, + err, + "%s/%s: failed to unmarshal expected rules", + tf.TestID, + nodeName, + ) + } + + opts := append( + cmpOptions(), + cmpopts.EquateEmpty(), + ) + if diff := cmp.Diff( + wantRules, + gotRules, + opts..., + ); diff != "" { + t.Errorf( + "%s/%s: filter rules mismatch (-want +got):\n%s", + tf.TestID, + nodeName, + diff, + ) + } + }) + } + }) + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-A1_wildcard_acl_includes_routes_in_srcips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A1_wildcard_acl_includes_routes_in_srcips.json new file mode 100644 index 00000000..c11add20 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A1_wildcard_acl_includes_routes_in_srcips.json @@ -0,0 +1,285 @@ +{ + "test_id": "ROUTES-A1_wildcard_acl_includes_routes_in_srcips", + "source": "headscale_adapted", + "parent_test": "SubnetBasics", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.64.0.0/10", + "fd7a:115c:a1e0::/48", + "10.0.0.0/8", + "10.33.0.0/16", + "172.16.0.0/24", + "192.168.1.0/24" + ], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "client2": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "user1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-A2_tag_based_acl_excludes_routes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A2_tag_based_acl_excludes_routes.json new file mode 100644 index 00000000..7d76427d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A2_tag_based_acl_excludes_routes.json @@ -0,0 +1,202 @@ +{ + "test_id": "ROUTES-A2_tag_based_acl_excludes_routes", + "source": "headscale_adapted", + "parent_test": "SubnetBasics", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:router"], + "dst": ["tag:router:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.100.100.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.119.139.79/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::4001:8ba0/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::6401:6401/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-A3_explicit_subnet_filter_to_router.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A3_explicit_subnet_filter_to_router.json new file mode 100644 index 00000000..24a85a90 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A3_explicit_subnet_filter_to_router.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-A3_explicit_subnet_filter_to_router", + "source": "headscale_adapted", + "parent_test": "SubnetBasics", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-A3b_autogroup_member_to_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A3b_autogroup_member_to_subnet.json new file mode 100644 index 00000000..73d48c33 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A3b_autogroup_member_to_subnet.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-A3b_autogroup_member_to_subnet", + "source": "headscale_adapted", + "parent_test": "SubnetBasics", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-A4_multiple_routes_same_router.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A4_multiple_routes_same_router.json new file mode 100644 index 00000000..909b21e2 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A4_multiple_routes_same_router.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-A4_multiple_routes_same_router", + "source": "headscale_adapted", + "parent_test": "SubnetBasics", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["172.16.0.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-A5_host_alias_to_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A5_host_alias_to_subnet.json new file mode 100644 index 00000000..800748f8 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-A5_host_alias_to_subnet.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-A5_host_alias_to_subnet", + "source": "headscale_adapted", + "parent_test": "SubnetBasics", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["internal:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B10_exit_routes_not_in_primaryroutes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B10_exit_routes_not_in_primaryroutes.json new file mode 100644 index 00000000..0c8a5c1d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B10_exit_routes_not_in_primaryroutes.json @@ -0,0 +1,124 @@ +{ + "test_id": "ROUTES-B10_exit_routes_not_in_primaryroutes", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": {} +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B1_exit_routes_not_in_srcips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B1_exit_routes_not_in_srcips.json new file mode 100644 index 00000000..2ee117ae --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B1_exit_routes_not_in_srcips.json @@ -0,0 +1,124 @@ +{ + "test_id": "ROUTES-B1_exit_routes_not_in_srcips", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": {} +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B2_tag_exit_excludes_exit_routes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B2_tag_exit_excludes_exit_routes.json new file mode 100644 index 00000000..1d2c6db2 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B2_tag_exit_excludes_exit_routes.json @@ -0,0 +1,232 @@ +{ + "test_id": "ROUTES-B2_tag_exit_excludes_exit_routes", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:exit"], + "dst": ["tag:exit:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.121.32.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::7f01:2004/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.121.32.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::7f01:2004/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B3_exit_node_advertises_routes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B3_exit_node_advertises_routes.json new file mode 100644 index 00000000..828962ed --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B3_exit_node_advertises_routes.json @@ -0,0 +1,124 @@ +{ + "test_id": "ROUTES-B3_exit_node_advertises_routes", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": {} +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B4_multi_router_has_both_route_types.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B4_multi_router_has_both_route_types.json new file mode 100644 index 00000000..bd8da9af --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B4_multi_router_has_both_route_types.json @@ -0,0 +1,173 @@ +{ + "test_id": "ROUTES-B4_multi_router_has_both_route_types", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "100.64.0.0/10", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::/48", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B5_exit_with_wildcard_dst.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B5_exit_with_wildcard_dst.json new file mode 100644 index 00000000..3fe3ddd4 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B5_exit_with_wildcard_dst.json @@ -0,0 +1,124 @@ +{ + "test_id": "ROUTES-B5_exit_with_wildcard_dst", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": {} +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B6_exit_node_option_field.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B6_exit_node_option_field.json new file mode 100644 index 00000000..075d11d8 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B6_exit_node_option_field.json @@ -0,0 +1,171 @@ +{ + "test_id": "ROUTES-B6_exit_node_option_field", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:exit"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B7_multiple_exit_nodes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B7_multiple_exit_nodes.json new file mode 100644 index 00000000..e46800bc --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B7_multiple_exit_nodes.json @@ -0,0 +1,153 @@ +{ + "test_id": "ROUTES-B7_multiple_exit_nodes", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:exit"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B8_autogroup_internet_no_filters.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B8_autogroup_internet_no_filters.json new file mode 100644 index 00000000..10f18973 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B8_autogroup_internet_no_filters.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-B8_autogroup_internet_no_filters", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["autogroup:internet:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-B9_exit_routes_in_allowedips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B9_exit_routes_in_allowedips.json new file mode 100644 index 00000000..43408a16 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-B9_exit_routes_in_allowedips.json @@ -0,0 +1,124 @@ +{ + "test_id": "ROUTES-B9_exit_routes_in_allowedips", + "source": "headscale_adapted", + "parent_test": "ExitNodes", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": {} +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D10_auto_approval_retroactive.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D10_auto_approval_retroactive.json new file mode 100644 index 00000000..d08b52d0 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D10_auto_approval_retroactive.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D10_auto_approval_retroactive", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D11_overlapping_auto_approvers.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D11_overlapping_auto_approvers.json new file mode 100644 index 00000000..5cb9b49b --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D11_overlapping_auto_approvers.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D11_overlapping_auto_approvers", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.0.0.0/8:80"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.0.0.0/8", + "Ports": { + "First": 80, + "Last": 80 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.0.0.0/8", + "Ports": { + "First": 80, + "Last": 80 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.0.0.0/8", + "Ports": { + "First": 80, + "Last": 80 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.0.0.0/8", + "Ports": { + "First": 80, + "Last": 80 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D1_basic_route_auto_approval.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D1_basic_route_auto_approval.json new file mode 100644 index 00000000..eed0cc03 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D1_basic_route_auto_approval.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D1_basic_route_auto_approval", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D2_nested_prefix_approval.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D2_nested_prefix_approval.json new file mode 100644 index 00000000..a61b2c60 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D2_nested_prefix_approval.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D2_nested_prefix_approval", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D3_exact_prefix_approval.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D3_exact_prefix_approval.json new file mode 100644 index 00000000..02d8ae8e --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D3_exact_prefix_approval.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D3_exact_prefix_approval", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D4_prefix_not_covered.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D4_prefix_not_covered.json new file mode 100644 index 00000000..f78b75d8 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D4_prefix_not_covered.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D4_prefix_not_covered", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D5_wrong_tag_not_approved.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D5_wrong_tag_not_approved.json new file mode 100644 index 00000000..0b49beb2 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D5_wrong_tag_not_approved.json @@ -0,0 +1,236 @@ +{ + "test_id": "ROUTES-D5_wrong_tag_not_approved", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:router"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D6_exit_node_auto_approval.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D6_exit_node_auto_approval.json new file mode 100644 index 00000000..20bff5b2 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D6_exit_node_auto_approval.json @@ -0,0 +1,142 @@ +{ + "test_id": "ROUTES-D6_exit_node_auto_approval", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D7_exit_auto_approval_wrong_tag.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D7_exit_auto_approval_wrong_tag.json new file mode 100644 index 00000000..fb6367c9 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D7_exit_auto_approval_wrong_tag.json @@ -0,0 +1,147 @@ +{ + "test_id": "ROUTES-D7_exit_auto_approval_wrong_tag", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:exit"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D8_auto_approval_acl_interaction.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D8_auto_approval_acl_interaction.json new file mode 100644 index 00000000..71e18870 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D8_auto_approval_acl_interaction.json @@ -0,0 +1,236 @@ +{ + "test_id": "ROUTES-D8_auto_approval_acl_interaction", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-D9_auto_approval_triggers_on_advertise.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D9_auto_approval_triggers_on_advertise.json new file mode 100644 index 00000000..d1bce5a6 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-D9_auto_approval_triggers_on_advertise.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-D9_auto_approval_triggers_on_advertise", + "source": "headscale_adapted", + "parent_test": "AutoApprover", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-E1_ha_two_routers_same_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E1_ha_two_routers_same_subnet.json new file mode 100644 index 00000000..7dd08c80 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E1_ha_two_routers_same_subnet.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-E1_ha_two_routers_same_subnet", + "source": "headscale_adapted", + "parent_test": "HARouters", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-E2_ha_primary_in_allowedips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E2_ha_primary_in_allowedips.json new file mode 100644 index 00000000..a1079b1a --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E2_ha_primary_in_allowedips.json @@ -0,0 +1,131 @@ +{ + "test_id": "ROUTES-E2_ha_primary_in_allowedips", + "source": "headscale_adapted", + "parent_test": "HARouters", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-E3_ha_secondary_no_route_in_allowedips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E3_ha_secondary_no_route_in_allowedips.json new file mode 100644 index 00000000..d726b217 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E3_ha_secondary_no_route_in_allowedips.json @@ -0,0 +1,131 @@ +{ + "test_id": "ROUTES-E3_ha_secondary_no_route_in_allowedips", + "source": "headscale_adapted", + "parent_test": "HARouters", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-E4_ha_both_get_filters_host_alias.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E4_ha_both_get_filters_host_alias.json new file mode 100644 index 00000000..abd25a5c --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E4_ha_both_get_filters_host_alias.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-E4_ha_both_get_filters_host_alias", + "source": "headscale_adapted", + "parent_test": "HARouters", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["subnet24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-E5_first_advertiser_is_primary.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E5_first_advertiser_is_primary.json new file mode 100644 index 00000000..3ffe8c8e --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-E5_first_advertiser_is_primary.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-E5_first_advertiser_is_primary", + "source": "headscale_adapted", + "parent_test": "HARouters", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F1_filter_on_destination_not_source.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F1_filter_on_destination_not_source.json new file mode 100644 index 00000000..56687483 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F1_filter_on_destination_not_source.json @@ -0,0 +1,194 @@ +{ + "test_id": "ROUTES-F1_filter_on_destination_not_source", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F2_subnet_as_acl_source.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F2_subnet_as_acl_source.json new file mode 100644 index 00000000..3ebe60a4 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F2_subnet_as_acl_source.json @@ -0,0 +1,299 @@ +{ + "test_id": "ROUTES-F2_subnet_as_acl_source", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["10.33.0.0/16"], + "dst": ["autogroup:member:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": ["10.33.0.0/16"], + "DstPorts": [ + { + "IP": "100.116.73.38/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.89.42.23/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.90.199.68/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::a801:4949/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::d01:2a2e/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::2d01:c747/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "client2": { + "packet_filter_rules": [ + { + "SrcIPs": ["10.33.0.0/16"], + "DstPorts": [ + { + "IP": "100.116.73.38/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.89.42.23/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.90.199.68/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::a801:4949/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::d01:2a2e/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::2d01:c747/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "user1": { + "packet_filter_rules": [ + { + "SrcIPs": ["10.33.0.0/16"], + "DstPorts": [ + { + "IP": "100.116.73.38/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.89.42.23/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.90.199.68/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::a801:4949/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::d01:2a2e/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::2d01:c747/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F3_wildcard_src_specific_dst.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F3_wildcard_src_specific_dst.json new file mode 100644 index 00000000..b43c9124 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F3_wildcard_src_specific_dst.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-F3_wildcard_src_specific_dst", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F4_specific_src_wildcard_dst.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F4_specific_src_wildcard_dst.json new file mode 100644 index 00000000..28650fb0 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F4_specific_src_wildcard_dst.json @@ -0,0 +1,173 @@ +{ + "test_id": "ROUTES-F4_specific_src_wildcard_dst", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:router"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F5_bidirectional_subnet_access.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F5_bidirectional_subnet_access.json new file mode 100644 index 00000000..5ddd050d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F5_bidirectional_subnet_access.json @@ -0,0 +1,248 @@ +{ + "test_id": "ROUTES-F5_bidirectional_subnet_access", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:*"] + }, + { + "action": "accept", + "src": ["10.33.0.0/16"], + "dst": ["autogroup:member:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": ["10.33.0.0/16"], + "DstPorts": [ + { + "IP": "100.116.73.38/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.89.42.23/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.90.199.68/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::2d01:c747/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::a801:4949/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::d01:2a2e/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::2d01:c747/128", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::2d01:c747/128", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F6_filter_srcips_expansion.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F6_filter_srcips_expansion.json new file mode 100644 index 00000000..a7496834 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F6_filter_srcips_expansion.json @@ -0,0 +1,194 @@ +{ + "test_id": "ROUTES-F6_filter_srcips_expansion", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::2d01:c747/128", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::2d01:c747/128", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128" + ], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F7_filter_dstports_shows_acl_cidr.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F7_filter_dstports_shows_acl_cidr.json new file mode 100644 index 00000000..bc70e0d1 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F7_filter_dstports_shows_acl_cidr.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-F7_filter_dstports_shows_acl_cidr", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.1.0/24", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.1.0/24", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F8_route_enabled_acl_denies.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F8_route_enabled_acl_denies.json new file mode 100644 index 00000000..7dc74a38 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F8_route_enabled_acl_denies.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-F8_route_enabled_acl_denies", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["group:empty"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-F9_route_disabled_acl_allows.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F9_route_disabled_acl_allows.json new file mode 100644 index 00000000..346ed379 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-F9_route_disabled_acl_allows.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-F9_route_disabled_acl_allows", + "source": "headscale_adapted", + "parent_test": "FilterPlacement", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.99.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G1_port_restriction_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G1_port_restriction_subnet.json new file mode 100644 index 00000000..cf98b525 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G1_port_restriction_subnet.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-G1_port_restriction_subnet", + "source": "headscale_adapted", + "parent_test": "ProtocolPort", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G2_port_range_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G2_port_range_subnet.json new file mode 100644 index 00000000..8ba71ffa --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G2_port_range_subnet.json @@ -0,0 +1,137 @@ +{ + "test_id": "ROUTES-G2_port_range_subnet", + "source": "headscale_adapted", + "parent_test": "ProtocolPort", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:80-443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G3_multiple_ports_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G3_multiple_ports_subnet.json new file mode 100644 index 00000000..bf4ac5dd --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G3_multiple_ports_subnet.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-G3_multiple_ports_subnet", + "source": "headscale_adapted", + "parent_test": "AdditionalG", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:22,80,443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G4_protocol_icmp_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G4_protocol_icmp_subnet.json new file mode 100644 index 00000000..4cd3d20d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G4_protocol_icmp_subnet.json @@ -0,0 +1,209 @@ +{ + "test_id": "ROUTES-G4_protocol_icmp_subnet", + "source": "headscale_adapted", + "parent_test": "AdditionalProtocol", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*"], + "proto": "icmp" + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [1] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [1] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [1] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [1] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G5_protocol_tcp_only.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G5_protocol_tcp_only.json new file mode 100644 index 00000000..e3ec7225 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G5_protocol_tcp_only.json @@ -0,0 +1,209 @@ +{ + "test_id": "ROUTES-G5_protocol_tcp_only", + "source": "headscale_adapted", + "parent_test": "AdditionalProtocol", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:22"], + "proto": "tcp" + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G6_protocol_udp_only.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G6_protocol_udp_only.json new file mode 100644 index 00000000..d2ca9172 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G6_protocol_udp_only.json @@ -0,0 +1,209 @@ +{ + "test_id": "ROUTES-G6_protocol_udp_only", + "source": "headscale_adapted", + "parent_test": "AdditionalProtocol", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:53"], + "proto": "udp" + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [17] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [17] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [17] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [17] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G7_all_ports_wildcard.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G7_all_ports_wildcard.json new file mode 100644 index 00000000..74a8cce4 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G7_all_ports_wildcard.json @@ -0,0 +1,137 @@ +{ + "test_id": "ROUTES-G7_all_ports_wildcard", + "source": "headscale_adapted", + "parent_test": "ProtocolPort", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.33.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-G8_default_ipproto.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G8_default_ipproto.json new file mode 100644 index 00000000..5d95947e --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-G8_default_ipproto.json @@ -0,0 +1,131 @@ +{ + "test_id": "ROUTES-G8_default_ipproto", + "source": "headscale_adapted", + "parent_test": "AdditionalG", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H10_very_small_prefix.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H10_very_small_prefix.json new file mode 100644 index 00000000..d996d48c --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H10_very_small_prefix.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-H10_very_small_prefix", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.100/32:80"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.100/32", + "Ports": { + "First": 80, + "Last": 80 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.100/32", + "Ports": { + "First": 80, + "Last": 80 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H11_ipv6_small_prefix.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H11_ipv6_small_prefix.json new file mode 100644 index 00000000..bc91ab8e --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H11_ipv6_small_prefix.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-H11_ipv6_small_prefix", + "source": "headscale_adapted", + "parent_test": "AdditionalEdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["fd00::1/128:443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "fd00::1/128", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "fd00::1/128", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H1_wildcard_srcips_format.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H1_wildcard_srcips_format.json new file mode 100644 index 00000000..98290253 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H1_wildcard_srcips_format.json @@ -0,0 +1,157 @@ +{ + "test_id": "ROUTES-H1_wildcard_srcips_format", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["tag:router:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.64.0.0/10", + "fd7a:115c:a1e0::/48", + "10.0.0.0/8", + "10.33.0.0/16", + "172.16.0.0/24", + "192.168.1.0/24" + ] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H2_wildcard_dstports_format.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H2_wildcard_dstports_format.json new file mode 100644 index 00000000..8b36eaa9 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H2_wildcard_dstports_format.json @@ -0,0 +1,173 @@ +{ + "test_id": "ROUTES-H2_wildcard_dstports_format", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::2d01:c747/128", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128" + ], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H3_cgnat_range_expansion.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H3_cgnat_range_expansion.json new file mode 100644 index 00000000..df2dd12c --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H3_cgnat_range_expansion.json @@ -0,0 +1,201 @@ +{ + "test_id": "ROUTES-H3_cgnat_range_expansion", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["tag:router:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "100.100.100.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.119.139.79/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::4001:8ba0/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::6401:6401/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H4_ipv6_range_in_srcips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H4_ipv6_range_in_srcips.json new file mode 100644 index 00000000..dd24116d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H4_ipv6_range_in_srcips.json @@ -0,0 +1,166 @@ +{ + "test_id": "ROUTES-H4_ipv6_range_in_srcips", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H5_subnet_overlaps_cgnat.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H5_subnet_overlaps_cgnat.json new file mode 100644 index 00000000..8c2107c4 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H5_subnet_overlaps_cgnat.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-H5_subnet_overlaps_cgnat", + "source": "headscale_adapted", + "parent_test": "AdditionalEdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["100.64.0.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "100.64.0.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "100.64.0.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H6_loopback_routes_not_distributed.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H6_loopback_routes_not_distributed.json new file mode 100644 index 00000000..c81dca2b --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H6_loopback_routes_not_distributed.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-H6_loopback_routes_not_distributed", + "source": "headscale_adapted", + "parent_test": "AdditionalEdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["127.0.0.1/32:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "127.0.0.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "127.0.0.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H7_two_nodes_same_subnet.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H7_two_nodes_same_subnet.json new file mode 100644 index 00000000..ff07bb06 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H7_two_nodes_same_subnet.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-H7_two_nodes_same_subnet", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H8_cgnat_overlap_blocked.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H8_cgnat_overlap_blocked.json new file mode 100644 index 00000000..2e8a34c7 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H8_cgnat_overlap_blocked.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-H8_cgnat_overlap_blocked", + "source": "headscale_adapted", + "parent_test": "AdditionalEdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["100.100.0.0/16:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-H9_large_prefix_works.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H9_large_prefix_works.json new file mode 100644 index 00000000..5e7cf160 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-H9_large_prefix_works.json @@ -0,0 +1,173 @@ +{ + "test_id": "ROUTES-H9_large_prefix_works", + "source": "headscale_adapted", + "parent_test": "EdgeCases", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["10.0.0.0/8:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::2d01:c747/128", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128" + ], + "DstPorts": [ + { + "IP": "10.0.0.0/8", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I1_ipv6_subnet_route.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I1_ipv6_subnet_route.json new file mode 100644 index 00000000..c1bd8d72 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I1_ipv6_subnet_route.json @@ -0,0 +1,104 @@ +{ + "test_id": "ROUTES-I1_ipv6_subnet_route", + "source": "headscale_adapted", + "parent_test": "IPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "ipv6-router": { + "id": 2, + "hostname": "ipv6-router", + "ipv4": "100.119.139.80", + "ipv6": "fd7a:115c:a1e0::4001:8ba1", + "tags": ["tag:router"], + "routable_ips": ["fd00::/48"], + "approved_routes": ["fd00::/48"] + }, + "ipv6-child-router": { + "id": 3, + "hostname": "ipv6-child-router", + "ipv4": "100.119.139.81", + "ipv6": "fd7a:115c:a1e0::4001:8ba2", + "tags": ["tag:router"], + "routable_ips": ["fd00:1::/64"], + "approved_routes": ["fd00:1::/64"] + }, + "ipv6-exit": { + "id": 4, + "hostname": "ipv6-exit", + "ipv4": "100.121.32.2", + "ipv6": "fd7a:115c:a1e0::7f01:2005", + "tags": ["tag:exit"], + "routable_ips": ["::/0"], + "approved_routes": ["::/0"] + } + } + }, + "captures": { + "ipv6-router": { + "packet_filter_rules": null + }, + "ipv6-child-router": { + "packet_filter_rules": null + }, + "ipv6-exit": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd00::/48", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I2_ipv6_exit_route.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I2_ipv6_exit_route.json new file mode 100644 index 00000000..e5c1c8f0 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I2_ipv6_exit_route.json @@ -0,0 +1,142 @@ +{ + "test_id": "ROUTES-I2_ipv6_exit_route", + "source": "headscale_adapted", + "parent_test": "AdditionalIPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I3_ipv6_in_wildcard_srcips.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I3_ipv6_in_wildcard_srcips.json new file mode 100644 index 00000000..58406cda --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I3_ipv6_in_wildcard_srcips.json @@ -0,0 +1,195 @@ +{ + "test_id": "ROUTES-I3_ipv6_in_wildcard_srcips", + "source": "headscale_adapted", + "parent_test": "AdditionalIPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["tag:router:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "100.100.100.1/32", + "Ports": { + "First": 22, + "Last": 22 + } + }, + { + "IP": "100.119.139.79/32", + "Ports": { + "First": 22, + "Last": 22 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 22, + "Last": 22 + } + }, + { + "IP": "fd7a:115c:a1e0::4001:8ba0/128", + "Ports": { + "First": 22, + "Last": 22 + } + }, + { + "IP": "fd7a:115c:a1e0::6401:6401/128", + "Ports": { + "First": 22, + "Last": 22 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 22, + "Last": 22 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I4_ipv6_specific_acl.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I4_ipv6_specific_acl.json new file mode 100644 index 00000000..4c0d1b0f --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I4_ipv6_specific_acl.json @@ -0,0 +1,132 @@ +{ + "test_id": "ROUTES-I4_ipv6_specific_acl", + "source": "headscale_adapted", + "parent_test": "IPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["fd00:1::/64:443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "ipv6-router": { + "id": 2, + "hostname": "ipv6-router", + "ipv4": "100.119.139.80", + "ipv6": "fd7a:115c:a1e0::4001:8ba1", + "tags": ["tag:router"], + "routable_ips": ["fd00::/48"], + "approved_routes": ["fd00::/48"] + }, + "ipv6-child-router": { + "id": 3, + "hostname": "ipv6-child-router", + "ipv4": "100.119.139.81", + "ipv6": "fd7a:115c:a1e0::4001:8ba2", + "tags": ["tag:router"], + "routable_ips": ["fd00:1::/64"], + "approved_routes": ["fd00:1::/64"] + }, + "ipv6-exit": { + "id": 4, + "hostname": "ipv6-exit", + "ipv4": "100.121.32.2", + "ipv6": "fd7a:115c:a1e0::7f01:2005", + "tags": ["tag:exit"], + "routable_ips": ["::/0"], + "approved_routes": ["::/0"] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "ipv6-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "fd00:1::/64", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ipv6-child-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "fd00:1::/64", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ipv6-exit": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "fd00:1::/64", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I5_ipv6_parent_child_routes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I5_ipv6_parent_child_routes.json new file mode 100644 index 00000000..b58bae9a --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I5_ipv6_parent_child_routes.json @@ -0,0 +1,118 @@ +{ + "test_id": "ROUTES-I5_ipv6_parent_child_routes", + "source": "headscale_adapted", + "parent_test": "IPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["fd00:1:2::/80:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "ipv6-router": { + "id": 2, + "hostname": "ipv6-router", + "ipv4": "100.119.139.80", + "ipv6": "fd7a:115c:a1e0::4001:8ba1", + "tags": ["tag:router"], + "routable_ips": ["fd00::/48"], + "approved_routes": ["fd00::/48"] + }, + "ipv6-child-router": { + "id": 3, + "hostname": "ipv6-child-router", + "ipv4": "100.119.139.81", + "ipv6": "fd7a:115c:a1e0::4001:8ba2", + "tags": ["tag:router"], + "routable_ips": ["fd00:1::/64"], + "approved_routes": ["fd00:1::/64"] + }, + "ipv6-exit": { + "id": 4, + "hostname": "ipv6-exit", + "ipv4": "100.121.32.2", + "ipv6": "fd7a:115c:a1e0::7f01:2005", + "tags": ["tag:exit"], + "routable_ips": ["::/0"], + "approved_routes": ["::/0"] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "ipv6-child-router": { + "packet_filter_rules": null + }, + "ipv6-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.116.73.38/32", "fd7a:115c:a1e0::a801:4949/128"], + "DstPorts": [ + { + "IP": "fd00:1:2::/80", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ipv6-exit": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.116.73.38/32", "fd7a:115c:a1e0::a801:4949/128"], + "DstPorts": [ + { + "IP": "fd00:1:2::/80", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I6_dual_stack_node.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I6_dual_stack_node.json new file mode 100644 index 00000000..a1af97e6 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I6_dual_stack_node.json @@ -0,0 +1,185 @@ +{ + "test_id": "ROUTES-I6_dual_stack_node", + "source": "headscale_adapted", + "parent_test": "AdditionalIPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*"] + }, + { + "action": "accept", + "src": ["*"], + "dst": ["fd00:1::/64:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.0.0/16", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-I7_ipv6_exit_coverage.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I7_ipv6_exit_coverage.json new file mode 100644 index 00000000..83088efe --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-I7_ipv6_exit_coverage.json @@ -0,0 +1,104 @@ +{ + "test_id": "ROUTES-I7_ipv6_exit_coverage", + "source": "headscale_adapted", + "parent_test": "IPv6", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["2001:db8::/32:443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "ipv6-router": { + "id": 2, + "hostname": "ipv6-router", + "ipv4": "100.119.139.80", + "ipv6": "fd7a:115c:a1e0::4001:8ba1", + "tags": ["tag:router"], + "routable_ips": ["fd00::/48"], + "approved_routes": ["fd00::/48"] + }, + "ipv6-child-router": { + "id": 3, + "hostname": "ipv6-child-router", + "ipv4": "100.119.139.81", + "ipv6": "fd7a:115c:a1e0::4001:8ba2", + "tags": ["tag:router"], + "routable_ips": ["fd00:1::/64"], + "approved_routes": ["fd00:1::/64"] + }, + "ipv6-exit": { + "id": 4, + "hostname": "ipv6-exit", + "ipv4": "100.121.32.2", + "ipv6": "fd7a:115c:a1e0::7f01:2005", + "tags": ["tag:exit"], + "routable_ips": ["::/0"], + "approved_routes": ["::/0"] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "ipv6-router": { + "packet_filter_rules": null + }, + "ipv6-child-router": { + "packet_filter_rules": null + }, + "ipv6-exit": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "2001:db8::/32", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O10_acl_dest_covered_by_multiple.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O10_acl_dest_covered_by_multiple.json new file mode 100644 index 00000000..21757d22 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O10_acl_dest_covered_by_multiple.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O10_acl_dest_covered_by_multiple", + "source": "headscale_adapted", + "parent_test": "AdditionalO", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O11_acl_dest_not_covered.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O11_acl_dest_not_covered.json new file mode 100644 index 00000000..033c873d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O11_acl_dest_not_covered.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O11_acl_dest_not_covered", + "source": "headscale_adapted", + "parent_test": "AdditionalO", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.99.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O12_filter_dest_is_acl_cidr.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O12_filter_dest_is_acl_cidr.json new file mode 100644 index 00000000..75ef51b3 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O12_filter_dest_is_acl_cidr.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O12_filter_dest_is_acl_cidr", + "source": "headscale_adapted", + "parent_test": "Overlapping", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O1_overlapping_routes_not_merged.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O1_overlapping_routes_not_merged.json new file mode 100644 index 00000000..cd69ac8d --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O1_overlapping_routes_not_merged.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O1_overlapping_routes_not_merged", + "source": "headscale_adapted", + "parent_test": "AdditionalO", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O2_ha_routers_both_get_filter.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O2_ha_routers_both_get_filter.json new file mode 100644 index 00000000..1720d849 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O2_ha_routers_both_get_filter.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O2_ha_routers_both_get_filter", + "source": "headscale_adapted", + "parent_test": "Overlapping", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O3_parent_child_different_nodes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O3_parent_child_different_nodes.json new file mode 100644 index 00000000..58630780 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O3_parent_child_different_nodes.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O3_parent_child_different_nodes", + "source": "headscale_adapted", + "parent_test": "Overlapping", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O4_three_way_hierarchy.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O4_three_way_hierarchy.json new file mode 100644 index 00000000..feb23215 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O4_three_way_hierarchy.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O4_three_way_hierarchy", + "source": "headscale_adapted", + "parent_test": "AdditionalO", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.128/25:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O5_sibling_routes_with_parent_acl.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O5_sibling_routes_with_parent_acl.json new file mode 100644 index 00000000..5e2d493c --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O5_sibling_routes_with_parent_acl.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O5_sibling_routes_with_parent_acl", + "source": "headscale_adapted", + "parent_test": "AdditionalO", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.0.0.0/8:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O6_exit_route_expands_filter_dist.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O6_exit_route_expands_filter_dist.json new file mode 100644 index 00000000..55b38e0a --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O6_exit_route_expands_filter_dist.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-O6_exit_route_expands_filter_dist", + "source": "headscale_adapted", + "parent_test": "Overlapping", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["8.8.8.0/24:53"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "8.8.8.0/24", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "8.8.8.0/24", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O7_specific_ip_targeting.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O7_specific_ip_targeting.json new file mode 100644 index 00000000..9cd5c63f --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O7_specific_ip_targeting.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-O7_specific_ip_targeting", + "source": "headscale_adapted", + "parent_test": "AdditionalO", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.100/32:80"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O8_same_node_overlapping_routes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O8_same_node_overlapping_routes.json new file mode 100644 index 00000000..9c4b2b8a --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O8_same_node_overlapping_routes.json @@ -0,0 +1,208 @@ +{ + "test_id": "ROUTES-O8_same_node_overlapping_routes", + "source": "headscale_adapted", + "parent_test": "AdditionalOverlapping", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "big-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "10.33.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-O9_different_nodes_same_route.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O9_different_nodes_same_route.json new file mode 100644 index 00000000..b9da38f4 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-O9_different_nodes_same_route.json @@ -0,0 +1,236 @@ +{ + "test_id": "ROUTES-O9_different_nodes_same_route", + "source": "headscale_adapted", + "parent_test": "AdditionalOverlapping", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["autogroup:member"], + "dst": ["192.168.1.0/24:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "ha-router2": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.116.73.38/32", + "100.89.42.23/32", + "100.90.199.68/32", + "fd7a:115c:a1e0::a801:4949/128", + "fd7a:115c:a1e0::d01:2a2e/128", + "fd7a:115c:a1e0::2d01:c747/128" + ], + "DstPorts": [ + { + "IP": "192.168.1.0/24", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R1_exit_covers_external_dest.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R1_exit_covers_external_dest.json new file mode 100644 index 00000000..d341c5bb --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R1_exit_covers_external_dest.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-R1_exit_covers_external_dest", + "source": "headscale_adapted", + "parent_test": "RouteCoverage", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["8.8.8.0/24:53"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "8.8.8.0/24", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "8.8.8.0/24", + "Ports": { + "First": 53, + "Last": 53 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R2_parent_route_covers_child_dest.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R2_parent_route_covers_child_dest.json new file mode 100644 index 00000000..24b19431 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R2_parent_route_covers_child_dest.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-R2_parent_route_covers_child_dest", + "source": "headscale_adapted", + "parent_test": "RouteCoverage", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R3_sibling_routes_no_coverage.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R3_sibling_routes_no_coverage.json new file mode 100644 index 00000000..8fc3887a --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R3_sibling_routes_no_coverage.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-R3_sibling_routes_no_coverage", + "source": "headscale_adapted", + "parent_test": "RouteCoverage", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.34.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R4_exact_match_route.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R4_exact_match_route.json new file mode 100644 index 00000000..683ecc13 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R4_exact_match_route.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-R4_exact_match_route", + "source": "headscale_adapted", + "parent_test": "RouteCoverage", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R5_route_coverage_check_logic.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R5_route_coverage_check_logic.json new file mode 100644 index 00000000..074e6628 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R5_route_coverage_check_logic.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-R5_route_coverage_check_logic", + "source": "headscale_adapted", + "parent_test": "AdditionalR", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.1.0/24:22"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R6_ipv6_route_coverage.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R6_ipv6_route_coverage.json new file mode 100644 index 00000000..bb14acfc --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R6_ipv6_route_coverage.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-R6_ipv6_route_coverage", + "source": "headscale_adapted", + "parent_test": "AdditionalR", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["fd7a:115c:a1e0::1/128:443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R7_exit_ipv6_coverage.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R7_exit_ipv6_coverage.json new file mode 100644 index 00000000..fd03a3cc --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R7_exit_ipv6_coverage.json @@ -0,0 +1,180 @@ +{ + "test_id": "ROUTES-R7_exit_ipv6_coverage", + "source": "headscale_adapted", + "parent_test": "AdditionalR", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["2001:db8::1/128:443"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "2001:db8::1/128", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": ["100.64.0.0/10", "fd7a:115c:a1e0::/48"], + "DstPorts": [ + { + "IP": "2001:db8::1/128", + "Ports": { + "First": 443, + "Last": 443 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-R8_mixed_ipv4_ipv6_coverage.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R8_mixed_ipv4_ipv6_coverage.json new file mode 100644 index 00000000..375069a0 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-R8_mixed_ipv4_ipv6_coverage.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-R8_mixed_ipv4_ipv6_coverage", + "source": "headscale_adapted", + "parent_test": "AdditionalR", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["10.33.0.0/16:*", "fd7a:115c:a1e0::/64:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-T1_tags_resolve_to_ips_not_routes.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T1_tags_resolve_to_ips_not_routes.json new file mode 100644 index 00000000..b31702b6 --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T1_tags_resolve_to_ips_not_routes.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-T1_tags_resolve_to_ips_not_routes", + "source": "headscale_adapted", + "parent_test": "TagResolution", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:router"], + "dst": ["tag:router:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-T2_tag_to_tag_with_exit.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T2_tag_to_tag_with_exit.json new file mode 100644 index 00000000..e1d68c8f --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T2_tag_to_tag_with_exit.json @@ -0,0 +1,232 @@ +{ + "test_id": "ROUTES-T2_tag_to_tag_with_exit", + "source": "headscale_adapted", + "parent_test": "TagResolution", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:exit"], + "dst": ["tag:exit:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.121.32.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::7f01:2004/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.121.32.1/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::7f01:2004/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.121.32.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::7f01:2004/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-T3_tag_src_includes_all_tagged.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T3_tag_src_includes_all_tagged.json new file mode 100644 index 00000000..dbce7c4b --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T3_tag_src_includes_all_tagged.json @@ -0,0 +1,165 @@ +{ + "test_id": "ROUTES-T3_tag_src_includes_all_tagged", + "source": "headscale_adapted", + "parent_test": "AdditionalT", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:router"], + "dst": ["*:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "client1": { + "packet_filter_rules": [ + { + "SrcIPs": [], + "DstPorts": [ + { + "IP": "*", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ] + } + ] + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-T4_tag_dst_includes_all_tagged.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T4_tag_dst_includes_all_tagged.json new file mode 100644 index 00000000..92257dda --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T4_tag_dst_includes_all_tagged.json @@ -0,0 +1,152 @@ +{ + "test_id": "ROUTES-T4_tag_dst_includes_all_tagged", + "source": "headscale_adapted", + "parent_test": "AdditionalT", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["*"], + "dst": ["tag:ha:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": null + }, + "multi-router": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + } + } +} diff --git a/hscontrol/policy/v2/testdata/routes_results/ROUTES-T5_multi_tag_node_in_both.json b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T5_multi_tag_node_in_both.json new file mode 100644 index 00000000..cb942e6c --- /dev/null +++ b/hscontrol/policy/v2/testdata/routes_results/ROUTES-T5_multi_tag_node_in_both.json @@ -0,0 +1,236 @@ +{ + "test_id": "ROUTES-T5_multi_tag_node_in_both", + "source": "headscale_adapted", + "parent_test": "TagResolution", + "input": { + "full_policy": { + "groups": { + "group:admins": ["kratail2tid@"], + "group:empty": [] + }, + "tagOwners": { + "tag:router": ["kratail2tid@"], + "tag:exit": ["kratail2tid@"], + "tag:ha": ["kratail2tid@"] + }, + "hosts": { + "internal": "10.0.0.0/8", + "subnet24": "192.168.1.0/24" + }, + "acls": [ + { + "action": "accept", + "src": ["tag:router"], + "dst": ["tag:exit:*"] + } + ] + } + }, + "topology": { + "users": [ + { + "id": 1, + "name": "kratail2tid" + } + ], + "nodes": { + "client1": { + "id": 1, + "hostname": "client1", + "ipv4": "100.116.73.38", + "ipv6": "fd7a:115c:a1e0::a801:4949", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "client2": { + "id": 2, + "hostname": "client2", + "ipv4": "100.89.42.23", + "ipv6": "fd7a:115c:a1e0::d01:2a2e", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + }, + "subnet-router": { + "id": 3, + "hostname": "subnet-router", + "ipv4": "100.119.139.79", + "ipv6": "fd7a:115c:a1e0::4001:8ba0", + "tags": ["tag:router"], + "routable_ips": ["10.33.0.0/16"], + "approved_routes": ["10.33.0.0/16"] + }, + "exit-node": { + "id": 4, + "hostname": "exit-node", + "ipv4": "100.121.32.1", + "ipv6": "fd7a:115c:a1e0::7f01:2004", + "tags": ["tag:exit"], + "routable_ips": ["0.0.0.0/0", "::/0"], + "approved_routes": ["0.0.0.0/0", "::/0"] + }, + "multi-router": { + "id": 5, + "hostname": "multi-router", + "ipv4": "100.74.117.7", + "ipv6": "fd7a:115c:a1e0::c401:7508", + "tags": ["tag:router", "tag:exit"], + "routable_ips": ["172.16.0.0/24", "0.0.0.0/0", "::/0"], + "approved_routes": ["172.16.0.0/24", "0.0.0.0/0", "::/0"] + }, + "ha-router1": { + "id": 6, + "hostname": "ha-router1", + "ipv4": "100.85.37.108", + "ipv6": "fd7a:115c:a1e0::f101:2597", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "ha-router2": { + "id": 7, + "hostname": "ha-router2", + "ipv4": "100.119.130.32", + "ipv6": "fd7a:115c:a1e0::4501:82a9", + "tags": ["tag:ha"], + "routable_ips": ["192.168.1.0/24"], + "approved_routes": ["192.168.1.0/24"] + }, + "big-router": { + "id": 8, + "hostname": "big-router", + "ipv4": "100.100.100.1", + "ipv6": "fd7a:115c:a1e0::6401:6401", + "tags": ["tag:router"], + "routable_ips": ["10.0.0.0/8"], + "approved_routes": ["10.0.0.0/8"] + }, + "user1": { + "id": 9, + "hostname": "user1", + "ipv4": "100.90.199.68", + "ipv6": "fd7a:115c:a1e0::2d01:c747", + "tags": [], + "user": "kratail2tid", + "routable_ips": [], + "approved_routes": [] + } + } + }, + "captures": { + "client1": { + "packet_filter_rules": null + }, + "client2": { + "packet_filter_rules": null + }, + "subnet-router": { + "packet_filter_rules": null + }, + "ha-router1": { + "packet_filter_rules": null + }, + "ha-router2": { + "packet_filter_rules": null + }, + "big-router": { + "packet_filter_rules": null + }, + "user1": { + "packet_filter_rules": null + }, + "exit-node": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.121.32.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::7f01:2004/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + }, + "multi-router": { + "packet_filter_rules": [ + { + "SrcIPs": [ + "100.100.100.1/32", + "100.119.139.79/32", + "100.74.117.7/32", + "fd7a:115c:a1e0::4001:8ba0/128", + "fd7a:115c:a1e0::6401:6401/128", + "fd7a:115c:a1e0::c401:7508/128" + ], + "DstPorts": [ + { + "IP": "100.121.32.1/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "100.74.117.7/32", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::7f01:2004/128", + "Ports": { + "First": 0, + "Last": 65535 + } + }, + { + "IP": "fd7a:115c:a1e0::c401:7508/128", + "Ports": { + "First": 0, + "Last": 65535 + } + } + ], + "IPProto": [6, 17, 1, 58] + } + ] + } + } +}