From 63123196cc21eeb301eec60bf05d5fe0a2bc4787 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 25 Aug 2026 15:23:46 +0000 Subject: [PATCH] all: inline deprecated tailcfg capability aliases go 1.27 vet reports the //go:fix inline directives tailscale added to the tailcfg cap aliases; applied with `go fix -inline ./...`. --- hscontrol/mapper/mapper.go | 5 +- hscontrol/mapper/tail_test.go | 55 ++++++++-------- hscontrol/policy/matcher/matcher_test.go | 7 ++- hscontrol/policy/v2/compiled.go | 8 ++- hscontrol/policy/v2/filter.go | 9 +-- hscontrol/policy/v2/filter_test.go | 27 ++++---- hscontrol/policy/v2/nodeattrs_test.go | 63 ++++++++++--------- hscontrol/policy/v2/tailnet_state_caps.go | 35 ++++++----- .../v2/tailscale_nodeattrs_compat_test.go | 3 +- hscontrol/policy/v2/types.go | 28 +++++---- hscontrol/servertest/grants_test.go | 13 ++-- hscontrol/servertest/nodeattrs_test.go | 47 +++++++------- hscontrol/types/node.go | 11 ++-- integration/grant_cap_test.go | 50 ++++++++------- 14 files changed, 189 insertions(+), 172 deletions(-) diff --git a/hscontrol/mapper/mapper.go b/hscontrol/mapper/mapper.go index bd04d2e7..b6cf1a7d 100644 --- a/hscontrol/mapper/mapper.go +++ b/hscontrol/mapper/mapper.go @@ -21,6 +21,7 @@ import ( "github.com/rs/zerolog/log" "tailscale.com/envknob" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" "tailscale.com/types/dnstype" "tailscale.com/types/views" ) @@ -106,8 +107,8 @@ func generateUserProfiles( // path, and "nextdns:no-device-info" suppresses the metadata-appending step. // See https://tailscale.com/docs/integrations/nextdns. const ( - nextDNSAttrPrefix = "nextdns:" - nextDNSAttrNoInfo tailcfg.NodeCapability = "nextdns:no-device-info" + nextDNSAttrPrefix = "nextdns:" + nextDNSAttrNoInfo nodecap.Cap = "nextdns:no-device-info" ) // nextDNSProfileRE bounds the characters accepted in a `nextdns:` diff --git a/hscontrol/mapper/tail_test.go b/hscontrol/mapper/tail_test.go index ee5da85c..991ae503 100644 --- a/hscontrol/mapper/tail_test.go +++ b/hscontrol/mapper/tail_test.go @@ -12,6 +12,7 @@ import ( "github.com/juanfont/headscale/hscontrol/types" "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" "tailscale.com/types/key" ) @@ -74,10 +75,10 @@ func TestTailNode(t *testing.T) { MachineAuthorized: true, CapMap: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: []tailcfg.RawMessage{tailcfg.RawMessage("false")}, + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.FileSharing: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: []tailcfg.RawMessage{tailcfg.RawMessage("false")}, }, }, wantErr: false, @@ -163,10 +164,10 @@ func TestTailNode(t *testing.T) { MachineAuthorized: true, CapMap: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: []tailcfg.RawMessage{tailcfg.RawMessage("false")}, + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.FileSharing: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: []tailcfg.RawMessage{tailcfg.RawMessage("false")}, }, }, wantErr: false, @@ -188,10 +189,10 @@ func TestTailNode(t *testing.T) { MachineAuthorized: true, CapMap: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: []tailcfg.RawMessage{tailcfg.RawMessage("false")}, + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.FileSharing: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: []tailcfg.RawMessage{tailcfg.RawMessage("false")}, }, }, wantErr: false, @@ -274,10 +275,10 @@ func TestTailNodeBaselineGates(t *testing.T) { AutoUpdate: types.AutoUpdateConfig{Enabled: false}, }, want: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: autoUpdate(false), + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.FileSharing: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: autoUpdate(false), }, }, { @@ -287,9 +288,9 @@ func TestTailNodeBaselineGates(t *testing.T) { AutoUpdate: types.AutoUpdateConfig{Enabled: false}, }, want: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: autoUpdate(false), + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: autoUpdate(false), }, }, { @@ -299,10 +300,10 @@ func TestTailNodeBaselineGates(t *testing.T) { AutoUpdate: types.AutoUpdateConfig{Enabled: true}, }, want: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: autoUpdate(true), + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.FileSharing: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: autoUpdate(true), }, }, { @@ -312,9 +313,9 @@ func TestTailNodeBaselineGates(t *testing.T) { AutoUpdate: types.AutoUpdateConfig{Enabled: true}, }, want: tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, - tailcfg.NodeAttrDefaultAutoUpdate: autoUpdate(true), + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, + nodecap.DefaultAutoUpdate: autoUpdate(true), }, }, } @@ -351,7 +352,7 @@ func TestTailNodeBaselineGates(t *testing.T) { func TestTailNodeDisableIPv4(t *testing.T) { t.Parallel() - const NodeAttrDisableIPv4 tailcfg.NodeCapability = "disable-ipv4" + const NodeAttrDisableIPv4 nodecap.Cap = "disable-ipv4" v4 := iap("100.64.0.1") v6Addr := netip.MustParseAddr("fd7a:115c:a1e0::1") diff --git a/hscontrol/policy/matcher/matcher_test.go b/hscontrol/policy/matcher/matcher_test.go index 69ecbfaf..bfd528cb 100644 --- a/hscontrol/policy/matcher/matcher_test.go +++ b/hscontrol/policy/matcher/matcher_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/peercap" ) func TestMatchFromStrings(t *testing.T) { @@ -194,7 +195,7 @@ func TestMatchFromFilterRule(t *testing.T) { netip.MustParsePrefix("100.64.0.3/32"), }, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: nil, + peercap.Relay: nil, }, }, }, @@ -218,7 +219,7 @@ func TestMatchFromFilterRule(t *testing.T) { netip.MustParsePrefix("100.64.0.2/32"), }, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelayTarget: nil, + peercap.RelayTarget: nil, }, }, }, @@ -242,7 +243,7 @@ func TestMatchFromFilterRule(t *testing.T) { netip.MustParsePrefix("100.64.0.3/32"), }, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: nil, + peercap.Relay: nil, }, }, }, diff --git a/hscontrol/policy/v2/compiled.go b/hscontrol/policy/v2/compiled.go index 642100c7..7deafa6c 100644 --- a/hscontrol/policy/v2/compiled.go +++ b/hscontrol/policy/v2/compiled.go @@ -10,6 +10,8 @@ import ( "github.com/rs/zerolog/log" "go4.org/netipx" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" + "tailscale.com/tailcfg/peercap" "tailscale.com/types/views" "tailscale.com/util/set" ) @@ -157,7 +159,7 @@ func (pol *Policy) compileNodeAttrs( } result := make(map[types.NodeID]tailcfg.NodeCapMap) - stamp := func(id types.NodeID, attr tailcfg.NodeCapability) { + stamp := func(id types.NodeID, attr nodecap.Cap) { capMap, ok := result[id] if !ok { capMap = tailcfg.NodeCapMap{} @@ -190,7 +192,7 @@ func (pol *Policy) compileNodeAttrs( if pol.RandomizeClientPort { for _, ni := range nodeList { - stamp(ni.id, tailcfg.NodeAttrRandomizeClientPort) + stamp(ni.id, nodecap.RandomizeClientPort) } } @@ -598,7 +600,7 @@ func collectRelayTargetIPs(grants []compiledGrant) (*netipx.IPSet, error) { for i := range grants { for _, rule := range grants[i].rules { for _, cg := range rule.CapGrant { - if _, ok := cg.CapMap[tailcfg.PeerCapabilityRelay]; !ok { + if _, ok := cg.CapMap[peercap.Relay]; !ok { continue } diff --git a/hscontrol/policy/v2/filter.go b/hscontrol/policy/v2/filter.go index cd4138b3..8ed85f97 100644 --- a/hscontrol/policy/v2/filter.go +++ b/hscontrol/policy/v2/filter.go @@ -13,6 +13,7 @@ import ( "github.com/rs/zerolog/log" "go4.org/netipx" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/peercap" "tailscale.com/types/views" ) @@ -24,8 +25,8 @@ var ( // companionCap pairs a well-known Tailscale capability with its // companion capability. type companionCap struct { - original tailcfg.PeerCapability - companion tailcfg.PeerCapability + original peercap.Cap + companion peercap.Cap } // companionCaps lists certain well-known Tailscale capabilities and @@ -35,8 +36,8 @@ type companionCap struct { // The slice is ordered by the original capability name so that // generated companion rules are emitted deterministically. var companionCaps = []companionCap{ - {tailcfg.PeerCapabilityTaildrive, tailcfg.PeerCapabilityTaildriveSharer}, - {tailcfg.PeerCapabilityRelay, tailcfg.PeerCapabilityRelayTarget}, + {peercap.Taildrive, peercap.TaildriveSharer}, + {peercap.Relay, peercap.RelayTarget}, } // companionCapGrantRules returns additional [tailcfg.FilterRule]s for any diff --git a/hscontrol/policy/v2/filter_test.go b/hscontrol/policy/v2/filter_test.go index 2d99331d..15a757f3 100644 --- a/hscontrol/policy/v2/filter_test.go +++ b/hscontrol/policy/v2/filter_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/require" "go4.org/netipx" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/peercap" ) // aliasWithPorts creates an AliasWithPorts structure from an alias and ports. @@ -3166,7 +3167,7 @@ func TestCompanionCapGrantRules(t *testing.T) { dstIPStrings: []string{"100.64.0.1"}, srcPrefixes: []netip.Prefix{mp("100.64.0.2/32")}, capMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildrive: {tailcfg.RawMessage(`{}`)}, + peercap.Taildrive: {tailcfg.RawMessage(`{}`)}, }, want: []tailcfg.FilterRule{ { @@ -3175,7 +3176,7 @@ func TestCompanionCapGrantRules(t *testing.T) { { Dsts: []netip.Prefix{mp("100.64.0.2/32")}, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildriveSharer: nil, + peercap.TaildriveSharer: nil, }, }, }, @@ -3187,7 +3188,7 @@ func TestCompanionCapGrantRules(t *testing.T) { dstIPStrings: []string{"100.64.0.10"}, srcPrefixes: []netip.Prefix{mp("100.64.0.20/32")}, capMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: {tailcfg.RawMessage(`{}`)}, + peercap.Relay: {tailcfg.RawMessage(`{}`)}, }, want: []tailcfg.FilterRule{ { @@ -3196,7 +3197,7 @@ func TestCompanionCapGrantRules(t *testing.T) { { Dsts: []netip.Prefix{mp("100.64.0.20/32")}, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelayTarget: nil, + peercap.RelayTarget: nil, }, }, }, @@ -3208,8 +3209,8 @@ func TestCompanionCapGrantRules(t *testing.T) { dstIPStrings: []string{"100.64.0.1"}, srcPrefixes: []netip.Prefix{mp("100.64.0.2/32")}, capMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: {tailcfg.RawMessage(`{}`)}, - tailcfg.PeerCapabilityTaildrive: {tailcfg.RawMessage(`{}`)}, + peercap.Relay: {tailcfg.RawMessage(`{}`)}, + peercap.Taildrive: {tailcfg.RawMessage(`{}`)}, }, want: []tailcfg.FilterRule{ { @@ -3219,7 +3220,7 @@ func TestCompanionCapGrantRules(t *testing.T) { { Dsts: []netip.Prefix{mp("100.64.0.2/32")}, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildriveSharer: nil, + peercap.TaildriveSharer: nil, }, }, }, @@ -3230,7 +3231,7 @@ func TestCompanionCapGrantRules(t *testing.T) { { Dsts: []netip.Prefix{mp("100.64.0.2/32")}, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelayTarget: nil, + peercap.RelayTarget: nil, }, }, }, @@ -3251,7 +3252,7 @@ func TestCompanionCapGrantRules(t *testing.T) { dstIPStrings: []string{"100.64.0.5"}, srcPrefixes: []netip.Prefix{mp("100.64.0.6/32")}, capMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildrive: { + peercap.Taildrive: { tailcfg.RawMessage(`{"access":"rw"}`), }, }, @@ -3262,7 +3263,7 @@ func TestCompanionCapGrantRules(t *testing.T) { { Dsts: []netip.Prefix{mp("100.64.0.6/32")}, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildriveSharer: nil, + peercap.TaildriveSharer: nil, }, }, }, @@ -3280,7 +3281,7 @@ func TestCompanionCapGrantRules(t *testing.T) { mp("100.64.0.21/32"), }, capMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: {tailcfg.RawMessage(`{}`)}, + peercap.Relay: {tailcfg.RawMessage(`{}`)}, }, want: []tailcfg.FilterRule{ { @@ -3292,7 +3293,7 @@ func TestCompanionCapGrantRules(t *testing.T) { mp("100.64.0.21/32"), }, CapMap: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelayTarget: nil, + peercap.RelayTarget: nil, }, }, }, @@ -3805,7 +3806,7 @@ func TestCompileViaGrant(t *testing.T) { Sources: Aliases{up("testuser@")}, Destinations: Aliases{pp("10.0.0.0/24")}, App: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: {tailcfg.RawMessage(`{}`)}, + peercap.Relay: {tailcfg.RawMessage(`{}`)}, }, Via: []Tag{"tag:relay"}, }, diff --git a/hscontrol/policy/v2/nodeattrs_test.go b/hscontrol/policy/v2/nodeattrs_test.go index a08c86b2..e6d3a50a 100644 --- a/hscontrol/policy/v2/nodeattrs_test.go +++ b/hscontrol/policy/v2/nodeattrs_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" ) // nodeAttrsTestUsers returns a minimal user set: two passkey-style users on @@ -81,7 +82,7 @@ const nodeAttrsTagOwners = `"tag:server": ["alice@example.com"], func TestNodeAttrsCompile(t *testing.T) { t.Parallel() - capMap := func(c tailcfg.NodeCapability) tailcfg.NodeCapMap { + capMap := func(c nodecap.Cap) tailcfg.NodeCapMap { return tailcfg.NodeCapMap{c: nil} } @@ -95,18 +96,18 @@ func TestNodeAttrsCompile(t *testing.T) { name: "wildcard target hits every node", extra: `"nodeAttrs": [{"target": ["*"], "attr": ["randomize-client-port"]}]`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 1: capMap(tailcfg.NodeAttrRandomizeClientPort), - 2: capMap(tailcfg.NodeAttrRandomizeClientPort), - 3: capMap(tailcfg.NodeAttrRandomizeClientPort), - 4: capMap(tailcfg.NodeAttrRandomizeClientPort), - 5: capMap(tailcfg.NodeAttrRandomizeClientPort), + 1: capMap(nodecap.RandomizeClientPort), + 2: capMap(nodecap.RandomizeClientPort), + 3: capMap(nodecap.RandomizeClientPort), + 4: capMap(nodecap.RandomizeClientPort), + 5: capMap(nodecap.RandomizeClientPort), }, }, { name: "user target hits only that user's untagged nodes", extra: `"nodeAttrs": [{"target": ["alice@example.com"], "attr": ["randomize-client-port"]}]`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 1: capMap(tailcfg.NodeAttrRandomizeClientPort), + 1: capMap(nodecap.RandomizeClientPort), }, }, { @@ -114,8 +115,8 @@ func TestNodeAttrsCompile(t *testing.T) { extra: `"nodeAttrs": [{"target": ["tag:server"], "attr": ["drive:share", "drive:access"]}]`, want: map[types.NodeID]tailcfg.NodeCapMap{ 3: { - tailcfg.NodeAttrsTaildriveShare: nil, - tailcfg.NodeAttrsTaildriveAccess: nil, + nodecap.TaildriveShare: nil, + nodecap.TaildriveAccess: nil, }, }, }, @@ -123,17 +124,17 @@ func TestNodeAttrsCompile(t *testing.T) { name: "autogroup:member hits untagged nodes only", extra: `"nodeAttrs": [{"target": ["autogroup:member"], "attr": ["randomize-client-port"]}]`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 1: capMap(tailcfg.NodeAttrRandomizeClientPort), - 2: capMap(tailcfg.NodeAttrRandomizeClientPort), + 1: capMap(nodecap.RandomizeClientPort), + 2: capMap(nodecap.RandomizeClientPort), }, }, { name: "autogroup:tagged hits tagged nodes only", extra: `"nodeAttrs": [{"target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"]}]`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 3: capMap(tailcfg.NodeAttrDisableCaptivePortalDetection), - 4: capMap(tailcfg.NodeAttrDisableCaptivePortalDetection), - 5: capMap(tailcfg.NodeAttrDisableCaptivePortalDetection), + 3: capMap(nodecap.DisableCaptivePortalDetection), + 4: capMap(nodecap.DisableCaptivePortalDetection), + 5: capMap(nodecap.DisableCaptivePortalDetection), }, }, { @@ -143,14 +144,14 @@ func TestNodeAttrsCompile(t *testing.T) { {"target": ["tag:server"], "attr": ["drive:share"]} ]`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 1: capMap(tailcfg.NodeAttrsTaildriveAccess), - 2: capMap(tailcfg.NodeAttrsTaildriveAccess), + 1: capMap(nodecap.TaildriveAccess), + 2: capMap(nodecap.TaildriveAccess), 3: { - tailcfg.NodeAttrsTaildriveAccess: nil, - tailcfg.NodeAttrsTaildriveShare: nil, + nodecap.TaildriveAccess: nil, + nodecap.TaildriveShare: nil, }, - 4: capMap(tailcfg.NodeAttrsTaildriveAccess), - 5: capMap(tailcfg.NodeAttrsTaildriveAccess), + 4: capMap(nodecap.TaildriveAccess), + 5: capMap(nodecap.TaildriveAccess), }, }, { @@ -162,11 +163,11 @@ func TestNodeAttrsCompile(t *testing.T) { name: "top-level randomizeClientPort stamps every node", extra: `"randomizeClientPort": true`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 1: capMap(tailcfg.NodeAttrRandomizeClientPort), - 2: capMap(tailcfg.NodeAttrRandomizeClientPort), - 3: capMap(tailcfg.NodeAttrRandomizeClientPort), - 4: capMap(tailcfg.NodeAttrRandomizeClientPort), - 5: capMap(tailcfg.NodeAttrRandomizeClientPort), + 1: capMap(nodecap.RandomizeClientPort), + 2: capMap(nodecap.RandomizeClientPort), + 3: capMap(nodecap.RandomizeClientPort), + 4: capMap(nodecap.RandomizeClientPort), + 5: capMap(nodecap.RandomizeClientPort), }, }, { @@ -174,14 +175,14 @@ func TestNodeAttrsCompile(t *testing.T) { extra: `"randomizeClientPort": true, "nodeAttrs": [{"target": ["tag:server"], "attr": ["disable-captive-portal-detection"]}]`, want: map[types.NodeID]tailcfg.NodeCapMap{ - 1: capMap(tailcfg.NodeAttrRandomizeClientPort), - 2: capMap(tailcfg.NodeAttrRandomizeClientPort), + 1: capMap(nodecap.RandomizeClientPort), + 2: capMap(nodecap.RandomizeClientPort), 3: { - tailcfg.NodeAttrRandomizeClientPort: nil, - tailcfg.NodeAttrDisableCaptivePortalDetection: nil, + nodecap.RandomizeClientPort: nil, + nodecap.DisableCaptivePortalDetection: nil, }, - 4: capMap(tailcfg.NodeAttrRandomizeClientPort), - 5: capMap(tailcfg.NodeAttrRandomizeClientPort), + 4: capMap(nodecap.RandomizeClientPort), + 5: capMap(nodecap.RandomizeClientPort), }, }, } diff --git a/hscontrol/policy/v2/tailnet_state_caps.go b/hscontrol/policy/v2/tailnet_state_caps.go index 9ee11bf6..575efb83 100644 --- a/hscontrol/policy/v2/tailnet_state_caps.go +++ b/hscontrol/policy/v2/tailnet_state_caps.go @@ -19,6 +19,7 @@ import ( "github.com/juanfont/headscale/hscontrol/types" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" ) // PeerCapMap returns the subset of peerSelfCaps the Tailscale client @@ -47,12 +48,12 @@ func PeerCapMap(peer types.NodeView, peerSelfCaps tailcfg.NodeCapMap) tailcfg.No // the suggestion from following an advertised-but-not-yet-trusted // node. if peer.IsExitNode() { - if v, ok := peerSelfCaps[tailcfg.NodeAttrSuggestExitNode]; ok { + if v, ok := peerSelfCaps[nodecap.SuggestExitNode]; ok { if out == nil { out = tailcfg.NodeCapMap{} } - out[tailcfg.NodeAttrSuggestExitNode] = v + out[nodecap.SuggestExitNode] = v } } @@ -70,7 +71,7 @@ func PeerCapMap(peer types.NodeView, peerSelfCaps tailcfg.NodeCapMap) tailcfg.No // anonymized capture. // 4. Caps that are internal magicsock or embedded-SSH tuning with no // headscale-side equivalent. -var unmodelledTailnetStateCaps = []tailcfg.NodeCapability{ +var unmodelledTailnetStateCaps = []nodecap.Cap{ // --- 1. User-role gated --- // [tailcfg.CapabilityAdmin]: the hosted control plane stamps this @@ -80,13 +81,13 @@ var unmodelledTailnetStateCaps = []tailcfg.NodeCapability{ // the always-on baseline. Stripping on both sides keeps the diff // from failing on every user-owned non-admin node in a capture. // Long-term fix is autogroup:admin support. - tailcfg.CapabilityAdmin, + nodecap.Admin, // [tailcfg.CapabilityOwner]: same shape as is-admin, conditional // on the "owner" role rather than admin. Headscale does not emit // this cap at all. autogroup:owner support is tracked under // NO_USER_ROLES — see the compat skip list. - tailcfg.CapabilityOwner, + nodecap.Owner, // --- 2. Feature not implemented --- @@ -95,23 +96,23 @@ var unmodelledTailnetStateCaps = []tailcfg.NodeCapability{ // re-keying by the control plane. Client reads at // ipn/ipnlocal/local.go:1752 (b.capTailnetLock). Headscale has no // tailnet-lock implementation. - tailcfg.CapabilityTailnetLock, + nodecap.TailnetLock, // [tailcfg.NodeAttrServiceHost]: marks a node as approved to host // VIP services (Tailscale Services). Client reads via // UnmarshalNodeCapViewJSON at ipn/ipnlocal/local.go:2704. // Headscale does not implement Tailscale Services. - tailcfg.NodeAttrServiceHost, + nodecap.ServiceHost, // [tailcfg.NodeAttrStoreAppCRoutes]: tells an app-connector node // to persist learned routes across restarts. Client reads via // controlknobs:148. Headscale does not implement app connectors. - tailcfg.NodeAttrStoreAppCRoutes, + nodecap.StoreAppCRoutes, // [tailcfg.CapabilityWarnFunnelNoHTTPS]: deprecated in Tailscale // 2023-08-09. Should not appear in fresh captures — listed // defensively in case a stale tailnet still emits it. - tailcfg.CapabilityWarnFunnelNoHTTPS, + nodecap.WarnFunnelNoHTTPS, // --- 3. Tailnet-state metadata not derivable from headscale config --- @@ -121,36 +122,36 @@ var unmodelledTailnetStateCaps = []tailcfg.NodeCapability{ // from cfg.Domain() that does not round-trip through the // anonymized capture string. Skip rather than diverge on a value // with no real-world equivalent. - tailcfg.NodeAttrTailnetDisplayName, + nodecap.TailnetDisplayName, // [tailcfg.NodeAttrMaxKeyDuration]: tailnet-wide max key duration // value. Headscale has cfg.Node.Expiry but does not surface it // as a cap today; the hosted control plane emits this only when // a non-default value is configured. - tailcfg.NodeAttrMaxKeyDuration, + nodecap.MaxKeyDuration, // [tailcfg.NodeAttrNativeIPV4]: peer-consumed cap conditional on // tailnet ipv4 reachability state. Out of scope for the current // peer-cap adoption (only suggest-exit-node is wired in this // PR). - tailcfg.NodeAttrNativeIPV4, + nodecap.NativeIPV4, // --- 4. Internal tuning, no headscale equivalent --- // [tailcfg.NodeAttrProbeUDPLifetime]: tunes magicsock's UDP // path-lifetime probe behavior. Internal performance knob; not // policy-driven. Client reads via controlknobs:147. - tailcfg.NodeAttrProbeUDPLifetime, + nodecap.ProbeUDPLifetime, // [tailcfg.NodeAttrSSHBehaviorV1]: configures the embedded SSH // server (no su, in-process SFTP). Internal tuning; the embedded // server picks Tailscale-vendored defaults without the cap. - tailcfg.NodeAttrSSHBehaviorV1, + nodecap.SSHBehaviorV1, // [tailcfg.NodeAttrSSHEnvironmentVariables]: gates SendEnv // forwarding in the embedded SSH server. Internal; default chosen // by the server. - tailcfg.NodeAttrSSHEnvironmentVariables, + nodecap.SSHEnvironmentVariables, } // strippedCapPrefixes lists URL/string prefixes for parameterized or @@ -172,7 +173,7 @@ func stripUnmodelledTailnetStateCaps(cm tailcfg.NodeCapMap) tailcfg.NodeCapMap { } out := maps.Clone(cm) - maps.DeleteFunc(out, func(k tailcfg.NodeCapability, _ []tailcfg.RawMessage) bool { + maps.DeleteFunc(out, func(k nodecap.Cap, _ []tailcfg.RawMessage) bool { return isUnmodelledTailnetStateCap(k) }) @@ -183,7 +184,7 @@ func stripUnmodelledTailnetStateCaps(cm tailcfg.NodeCapMap) tailcfg.NodeCapMap { return out } -func isUnmodelledTailnetStateCap(k tailcfg.NodeCapability) bool { +func isUnmodelledTailnetStateCap(k nodecap.Cap) bool { if slices.Contains(unmodelledTailnetStateCaps, k) { return true } diff --git a/hscontrol/policy/v2/tailscale_nodeattrs_compat_test.go b/hscontrol/policy/v2/tailscale_nodeattrs_compat_test.go index 8809caed..0a26be5a 100644 --- a/hscontrol/policy/v2/tailscale_nodeattrs_compat_test.go +++ b/hscontrol/policy/v2/tailscale_nodeattrs_compat_test.go @@ -28,6 +28,7 @@ import ( "github.com/juanfont/headscale/hscontrol/types/testcapture" "github.com/stretchr/testify/require" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" "tailscale.com/types/views" ) @@ -321,7 +322,7 @@ func testNodeAttrsSuccess( // capMapFromView materialises a captured CapMap view into the // [tailcfg.NodeCapMap] shape headscale renders, so both sides of the // diff have the same concrete type. -func capMapFromView(view views.MapSlice[tailcfg.NodeCapability, tailcfg.RawMessage]) tailcfg.NodeCapMap { +func capMapFromView(view views.MapSlice[nodecap.Cap, tailcfg.RawMessage]) tailcfg.NodeCapMap { if view.Len() == 0 { return nil } diff --git a/hscontrol/policy/v2/types.go b/hscontrol/policy/v2/types.go index 3f748823..b6c0f725 100644 --- a/hscontrol/policy/v2/types.go +++ b/hscontrol/policy/v2/types.go @@ -18,6 +18,8 @@ import ( "go4.org/netipx" "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" + "tailscale.com/tailcfg/peercap" "tailscale.com/types/views" "tailscale.com/util/multierr" "tailscale.com/util/set" @@ -99,8 +101,8 @@ var ( // without the matching server-side machinery would be misleading — nodes // would advertise a feature that does not work. Reject at policy load and // point operators at the issue. -var nodeAttrUnsupportedCaps = map[tailcfg.NodeCapability]string{ - tailcfg.NodeAttrFunnel: "https://github.com/juanfont/headscale/issues/2527", +var nodeAttrUnsupportedCaps = map[nodecap.Cap]string{ + nodecap.Funnel: "https://github.com/juanfont/headscale/issues/2527", } // Policy validation errors. @@ -1891,9 +1893,9 @@ type Grant struct { // IPPool is parsed and validated for forward compatibility with the IP // allocator; the policy compiler does not consume it yet. type NodeAttrGrant struct { - Targets Aliases `json:"target"` - Attrs []tailcfg.NodeCapability `json:"attr,omitempty"` - IPPool []netip.Prefix `json:"ipPool,omitempty"` + Targets Aliases `json:"target"` + Attrs []nodecap.Cap `json:"attr,omitempty"` + IPPool []netip.Prefix `json:"ipPool,omitempty"` } // aclToGrants converts an [ACL] rule to one or more equivalent [Grant] rules. @@ -2253,7 +2255,7 @@ func validateCapabilityName(name string) error { // Reject caps in the tailscale.com domain unless allowlisted. if strings.HasPrefix(name, "tailscale.com/") { - if !tailscaleCapAllowlist[tailcfg.PeerCapability(name)] { + if !tailscaleCapAllowlist[peercap.Cap(name)] { return ErrCapNameTailscaleDomain } } @@ -2265,16 +2267,16 @@ func validateCapabilityName(name string) error { // that users are allowed to specify in grant app fields. Companion caps // (drive-sharer, relay-target) and internal caps (ingress, funnel) are // generated by the server and cannot be specified by users. -var tailscaleCapAllowlist = map[tailcfg.PeerCapability]bool{ - tailcfg.PeerCapabilityTaildrive: true, // tailscale.com/cap/drive - tailcfg.PeerCapabilityRelay: true, // tailscale.com/cap/relay - tailcfg.PeerCapabilityWebUI: true, // tailscale.com/cap/webui - tailcfg.PeerCapabilityKubernetes: true, // tailscale.com/cap/kubernetes - tailcfg.PeerCapabilityTsIDP: true, // tailscale.com/cap/tsidp +var tailscaleCapAllowlist = map[peercap.Cap]bool{ + peercap.Taildrive: true, // tailscale.com/cap/drive + peercap.Relay: true, // tailscale.com/cap/relay + peercap.WebUI: true, // tailscale.com/cap/webui + peercap.Kubernetes: true, // tailscale.com/cap/kubernetes + peercap.TsIDP: true, // tailscale.com/cap/tsidp // tailscale.com/cap/secrets is the capability used by setec // (github.com/tailscale/setec); allow it so it can be granted via policy. - tailcfg.PeerCapability("tailscale.com/cap/secrets"): true, + peercap.Cap("tailscale.com/cap/secrets"): true, } // validateGrantSrcDstCombination validates [Grant]-specific source/destination diff --git a/hscontrol/servertest/grants_test.go b/hscontrol/servertest/grants_test.go index 63820bce..40e0d89e 100644 --- a/hscontrol/servertest/grants_test.go +++ b/hscontrol/servertest/grants_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/peercap" "tailscale.com/types/netmap" "tailscale.com/wgengine/filter/filtertype" ) @@ -157,13 +158,13 @@ func TestGrantPolicies(t *testing.T) { //nolint:gocyclo for _, m := range nm1.PacketFilter { for _, cm := range m.Caps { switch cm.Cap { //nolint:exhaustive // only checking grant-specific caps - case tailcfg.PeerCapabilityTaildrive: + case peercap.Taildrive: foundDrive = true - case tailcfg.PeerCapabilityTaildriveSharer: + case peercap.TaildriveSharer: foundDriveSharer = true - case tailcfg.PeerCapabilityRelay: + case peercap.Relay: foundRelay = true - case tailcfg.PeerCapabilityRelayTarget: + case peercap.RelayTarget: foundRelayTarget = true } } @@ -464,8 +465,8 @@ func TestGrantPolicies(t *testing.T) { //nolint:gocyclo for _, m := range nm1.PacketFilter { for _, cm := range m.Caps { - if cm.Cap == tailcfg.PeerCapabilityTaildrive || - cm.Cap == tailcfg.PeerCapabilityTaildriveSharer { + if cm.Cap == peercap.Taildrive || + cm.Cap == peercap.TaildriveSharer { foundDriveOrSharer = true } } diff --git a/hscontrol/servertest/nodeattrs_test.go b/hscontrol/servertest/nodeattrs_test.go index 26179bf6..7ef96f29 100644 --- a/hscontrol/servertest/nodeattrs_test.go +++ b/hscontrol/servertest/nodeattrs_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" "tailscale.com/types/netmap" ) @@ -32,7 +33,7 @@ func reloadPolicy(t *testing.T, srv *servertest.TestServer, pol string) { } // hasCap reports whether the given netmap's self CapMap contains want. -func hasCap(nm *netmap.NetworkMap, want tailcfg.NodeCapability) bool { +func hasCap(nm *netmap.NetworkMap, want nodecap.Cap) bool { if nm == nil || !nm.SelfNode.Valid() { return false } @@ -82,11 +83,11 @@ func TestNodeAttrsDeliverToSelfAndPeer(t *testing.T) { c1.WaitForCondition(t, "self randomize-client-port cap on c1", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrRandomizeClientPort) + return hasCap(nm, nodecap.RandomizeClientPort) }) c2.WaitForCondition(t, "self randomize-client-port cap on c2", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrRandomizeClientPort) + return hasCap(nm, nodecap.RandomizeClientPort) }) // randomize-client-port is not in the peer-consumed allowlist and @@ -121,7 +122,7 @@ func TestNodeAttrsUserTargetIsolated(t *testing.T) { a.WaitForCondition(t, "alice gains randomize-client-port", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrRandomizeClientPort) + return hasCap(nm, nodecap.RandomizeClientPort) }) // bob must remain free of the cap; check after alice has converged so we @@ -129,7 +130,7 @@ func TestNodeAttrsUserTargetIsolated(t *testing.T) { b.WaitForPeers(t, 1, 10*time.Second) nmB := b.Netmap() require.NotNil(t, nmB) - assert.False(t, hasCap(nmB, tailcfg.NodeAttrRandomizeClientPort), + assert.False(t, hasCap(nmB, nodecap.RandomizeClientPort), "bob is not in the target set; must not receive the cap") } @@ -154,14 +155,14 @@ func TestNodeAttrsRevokesWhenRemoved(t *testing.T) { c.WaitForCondition(t, "captive cap appears", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrDisableCaptivePortalDetection) + return hasCap(nm, nodecap.DisableCaptivePortalDetection) }) reloadPolicy(t, srv, `{}`) c.WaitForCondition(t, "captive cap disappears", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return !hasCap(nm, tailcfg.NodeAttrDisableCaptivePortalDetection) + return !hasCap(nm, nodecap.DisableCaptivePortalDetection) }) } @@ -184,11 +185,11 @@ func TestNodeAttrsBaselineCapsAlwaysOn(t *testing.T) { return false } - for _, w := range []tailcfg.NodeCapability{ - tailcfg.CapabilityAdmin, - tailcfg.CapabilitySSH, - tailcfg.CapabilityFileSharing, - tailcfg.NodeAttrDefaultAutoUpdate, + for _, w := range []nodecap.Cap{ + nodecap.Admin, + nodecap.SSH, + nodecap.FileSharing, + nodecap.DefaultAutoUpdate, } { if !hasCap(nm, w) { return false @@ -217,9 +218,9 @@ func TestTaildropDisabledWithholdsFileSharingCap(t *testing.T) { return false } - return !hasCap(nm, tailcfg.CapabilityFileSharing) && - hasCap(nm, tailcfg.CapabilityAdmin) && - hasCap(nm, tailcfg.CapabilitySSH) + return !hasCap(nm, nodecap.FileSharing) && + hasCap(nm, nodecap.Admin) && + hasCap(nm, nodecap.SSH) }) } @@ -247,9 +248,9 @@ func TestNodeAttrsAddsToBaseline(t *testing.T) { c.WaitForCondition(t, "policy adds caps on top of baseline", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrRandomizeClientPort) && - hasCap(nm, tailcfg.NodeAttrDisableCaptivePortalDetection) && - hasCap(nm, tailcfg.CapabilitySSH) + return hasCap(nm, nodecap.RandomizeClientPort) && + hasCap(nm, nodecap.DisableCaptivePortalDetection) && + hasCap(nm, nodecap.SSH) }) } @@ -276,7 +277,7 @@ func TestNodeAttrsReloadingSamePolicyDoesNotChurnSelf(t *testing.T) { c.WaitForCondition(t, "policy cap arrives", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrRandomizeClientPort) + return hasCap(nm, nodecap.RandomizeClientPort) }) // Reload identical bytes. Per-node CapMap diff produces an empty @@ -288,7 +289,7 @@ func TestNodeAttrsReloadingSamePolicyDoesNotChurnSelf(t *testing.T) { c.WaitForCondition(t, "cap persists after no-op reload", 5*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrRandomizeClientPort) + return hasCap(nm, nodecap.RandomizeClientPort) }) } @@ -352,7 +353,7 @@ func TestNodeAttrsSuggestExitNodeOnPeerCapMap(t *testing.T) { // usual stamp; nothing special about exit nodes here). exit.WaitForCondition(t, "self suggest-exit-node on exit", 10*time.Second, func(nm *netmap.NetworkMap) bool { - return hasCap(nm, tailcfg.NodeAttrSuggestExitNode) + return hasCap(nm, nodecap.SuggestExitNode) }) // Peer-side: the viewer sees the exit node in its Peers list with @@ -369,7 +370,7 @@ func TestNodeAttrsSuggestExitNodeOnPeerCapMap(t *testing.T) { continue } - return peer.CapMap().Contains(tailcfg.NodeAttrSuggestExitNode) + return peer.CapMap().Contains(nodecap.SuggestExitNode) } return false @@ -389,7 +390,7 @@ func TestNodeAttrsSuggestExitNodeOnPeerCapMap(t *testing.T) { continue } - return !peer.CapMap().Contains(tailcfg.NodeAttrSuggestExitNode) + return !peer.CapMap().Contains(nodecap.SuggestExitNode) } return false diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index da417354..7d856f3e 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -17,6 +17,7 @@ import ( "go4.org/netipx" "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" "tailscale.com/types/key" "tailscale.com/types/views" "tailscale.com/util/dnsname" @@ -39,7 +40,7 @@ type RouteFunc func(id NodeID) []netip.Prefix // node's own IPv4 CGNAT prefix in [tailcfg.Node.Addresses] and // [tailcfg.Node.AllowedIPs]. Subnet routes the node advertises remain. // See https://tailscale.com/docs/reference/troubleshooting/network-configuration/cgnat-conflicts. -const nodeAttrDisableIPv4 tailcfg.NodeCapability = "disable-ipv4" +const nodeAttrDisableIPv4 nodecap.Cap = "disable-ipv4" // filterIPv4 returns ps with every IPv4 prefix dropped. Used by // [NodeView.TailNode] when the node carries the disable-ipv4 nodeAttr. @@ -1200,12 +1201,12 @@ func (nv NodeView) TailNode( // what Tailscale SaaS emits for a default tailnet. // cfg.Taildrop.Enabled gates CapabilityFileSharing. capMap := tailcfg.NodeCapMap{ - tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, - tailcfg.CapabilitySSH: []tailcfg.RawMessage{}, + nodecap.Admin: []tailcfg.RawMessage{}, + nodecap.SSH: []tailcfg.RawMessage{}, } if cfg.Taildrop.Enabled { - capMap[tailcfg.CapabilityFileSharing] = []tailcfg.RawMessage{} + capMap[nodecap.FileSharing] = []tailcfg.RawMessage{} } // default-auto-update is always emitted; the value is a JSON bool @@ -1218,7 +1219,7 @@ func (nv NodeView) TailNode( autoUpdateVal = tailcfg.RawMessage("true") } - capMap[tailcfg.NodeAttrDefaultAutoUpdate] = []tailcfg.RawMessage{autoUpdateVal} + capMap[nodecap.DefaultAutoUpdate] = []tailcfg.RawMessage{autoUpdateVal} // Policy nodeAttrs overlay the baseline on the self view. Peers // pass nil; their CapMap is replaced downstream by [policyv2.PeerCapMap]. diff --git a/integration/grant_cap_test.go b/integration/grant_cap_test.go index b3a89733..af44c626 100644 --- a/integration/grant_cap_test.go +++ b/integration/grant_cap_test.go @@ -14,12 +14,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "tailscale.com/tailcfg" + "tailscale.com/tailcfg/nodecap" + "tailscale.com/tailcfg/peercap" "tailscale.com/wgengine/filter" ) // hasCapMatchInPacketFilter checks if any [filter.Match] entry in the packet // filter contains a [filter.CapMatch] with the given capability name. -func hasCapMatchInPacketFilter(pf []filter.Match, peerCap tailcfg.PeerCapability) bool { +func hasCapMatchInPacketFilter(pf []filter.Match, peerCap peercap.Cap) bool { for _, m := range pf { for _, cm := range m.Caps { if cm.Cap == peerCap { @@ -34,7 +36,7 @@ func hasCapMatchInPacketFilter(pf []filter.Match, peerCap tailcfg.PeerCapability // hasCapMatchForIP checks if any [filter.CapMatch] with the given capability // has a Dst prefix that contains the given IP. This validates that // the cap is directed at the correct node, not just present. -func hasCapMatchForIP(pf []filter.Match, peerCap tailcfg.PeerCapability, ip netip.Addr) bool { +func hasCapMatchForIP(pf []filter.Match, peerCap peercap.Cap, ip netip.Addr) bool { for _, m := range pf { for _, cm := range m.Caps { if cm.Cap == peerCap && cm.Dst.Contains(ip) { @@ -119,7 +121,7 @@ func TestGrantCapRelay(t *testing.T) { Sources: policyv2.Aliases{tagp("tag:client-a"), tagp("tag:client-b")}, Destinations: policyv2.Aliases{tagp("tag:relay")}, App: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityRelay: {tailcfg.RawMessage("{}")}, + peercap.Relay: {tailcfg.RawMessage("{}")}, }, }, }, @@ -280,7 +282,7 @@ func TestGrantCapRelay(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := relayR.PacketFilter() assert.NoError(c, err) - assert.True(c, hasCapMatchForIP(pf, tailcfg.PeerCapabilityRelay, relayIPv4), + assert.True(c, hasCapMatchForIP(pf, peercap.Relay, relayIPv4), "Relay R should have cap/relay with Dst matching relay's IP %s", relayIPv4) }, assertTimeout, 500*time.Millisecond, "R should have cap/relay targeting its own IP") @@ -288,7 +290,7 @@ func TestGrantCapRelay(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := clientA.PacketFilter() assert.NoError(c, err) - assert.True(c, hasCapMatchForIP(pf, tailcfg.PeerCapabilityRelayTarget, clientAIPv4), + assert.True(c, hasCapMatchForIP(pf, peercap.RelayTarget, clientAIPv4), "Client A should have cap/relay-target with Dst matching A's IP %s", clientAIPv4) }, assertTimeout, 500*time.Millisecond, "A should have cap/relay-target targeting its own IP") @@ -296,7 +298,7 @@ func TestGrantCapRelay(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := clientB.PacketFilter() assert.NoError(c, err) - assert.True(c, hasCapMatchForIP(pf, tailcfg.PeerCapabilityRelayTarget, clientBIPv4), + assert.True(c, hasCapMatchForIP(pf, peercap.RelayTarget, clientBIPv4), "Client B should have cap/relay-target with Dst matching B's IP %s", clientBIPv4) }, assertTimeout, 500*time.Millisecond, "B should have cap/relay-target targeting its own IP") @@ -306,7 +308,7 @@ func TestGrantCapRelay(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := relayR.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityRelayTarget), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.RelayTarget), "Relay R should NOT have cap/relay-target") }, 10*time.Second, 500*time.Millisecond, "R should not have cap/relay-target") @@ -314,7 +316,7 @@ func TestGrantCapRelay(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := clientA.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityRelay), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.Relay), "Client A should NOT have cap/relay") }, 10*time.Second, 500*time.Millisecond, "A should not have cap/relay") @@ -322,7 +324,7 @@ func TestGrantCapRelay(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := clientB.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityRelay), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.Relay), "Client B should NOT have cap/relay") }, 10*time.Second, 500*time.Millisecond, "B should not have cap/relay") @@ -560,9 +562,9 @@ func TestGrantCapDrive(t *testing.T) { NodeAttrs: []policyv2.NodeAttrGrant{ { Targets: policyv2.Aliases{policyv2.Wildcard}, - Attrs: []tailcfg.NodeCapability{ - tailcfg.NodeAttrsTaildriveShare, - tailcfg.NodeAttrsTaildriveAccess, + Attrs: []nodecap.Cap{ + nodecap.TaildriveShare, + nodecap.TaildriveAccess, }, }, }, @@ -586,7 +588,7 @@ func TestGrantCapDrive(t *testing.T) { Sources: policyv2.Aliases{tagp("tag:rw-client")}, Destinations: policyv2.Aliases{tagp("tag:sharer")}, App: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildrive: { + peercap.Taildrive: { tailcfg.RawMessage(`{"shares":["*"],"access":"rw"}`), }, }, @@ -596,7 +598,7 @@ func TestGrantCapDrive(t *testing.T) { Sources: policyv2.Aliases{tagp("tag:ro-client")}, Destinations: policyv2.Aliases{tagp("tag:sharer")}, App: tailcfg.PeerCapMap{ - tailcfg.PeerCapabilityTaildrive: { + peercap.Taildrive: { tailcfg.RawMessage(`{"shares":["*"],"access":"ro"}`), }, }, @@ -723,9 +725,9 @@ func TestGrantCapDrive(t *testing.T) { "%s: SelfNode should be valid", node.Hostname()) if nm.SelfNode.Valid() { - assert.True(c, nm.SelfNode.HasCap(tailcfg.NodeAttrsTaildriveShare), + assert.True(c, nm.SelfNode.HasCap(nodecap.TaildriveShare), "%s: should have drive:share cap", node.Hostname()) - assert.True(c, nm.SelfNode.HasCap(tailcfg.NodeAttrsTaildriveAccess), + assert.True(c, nm.SelfNode.HasCap(nodecap.TaildriveAccess), "%s: should have drive:access cap", node.Hostname()) } }, assertTimeout, 500*time.Millisecond, @@ -741,7 +743,7 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := sharer.PacketFilter() assert.NoError(c, err) - assert.True(c, hasCapMatchForIP(pf, tailcfg.PeerCapabilityTaildrive, sharerIPv4), + assert.True(c, hasCapMatchForIP(pf, peercap.Taildrive, sharerIPv4), "Sharer should have cap/drive with Dst matching sharer's IP %s", sharerIPv4) }, assertTimeout, 500*time.Millisecond, "sharer should have cap/drive targeting its own IP") @@ -751,7 +753,7 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := rwClient.PacketFilter() assert.NoError(c, err) - assert.True(c, hasCapMatchForIP(pf, tailcfg.PeerCapabilityTaildriveSharer, rwClientIPv4), + assert.True(c, hasCapMatchForIP(pf, peercap.TaildriveSharer, rwClientIPv4), "RW client should have cap/drive-sharer with Dst matching rw-client's IP %s", rwClientIPv4) }, assertTimeout, 500*time.Millisecond, "rw-client should have cap/drive-sharer") @@ -761,7 +763,7 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := roClient.PacketFilter() assert.NoError(c, err) - assert.True(c, hasCapMatchForIP(pf, tailcfg.PeerCapabilityTaildriveSharer, roClientIPv4), + assert.True(c, hasCapMatchForIP(pf, peercap.TaildriveSharer, roClientIPv4), "RO client should have cap/drive-sharer with Dst matching ro-client's IP %s", roClientIPv4) }, assertTimeout, 500*time.Millisecond, "ro-client should have cap/drive-sharer") @@ -771,9 +773,9 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := noAccess.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityTaildrive), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.Taildrive), "no-access should NOT have cap/drive") - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityTaildriveSharer), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.TaildriveSharer), "no-access should NOT have cap/drive-sharer") }, 10*time.Second, 500*time.Millisecond, "no-access should have no drive caps") @@ -781,7 +783,7 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := sharer.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityTaildriveSharer), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.TaildriveSharer), "sharer should NOT have cap/drive-sharer") }, 10*time.Second, 500*time.Millisecond, "sharer should not have cap/drive-sharer") @@ -789,7 +791,7 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := rwClient.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityTaildrive), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.Taildrive), "rw-client should NOT have cap/drive") }, 10*time.Second, 500*time.Millisecond, "rw-client should not have cap/drive") @@ -797,7 +799,7 @@ func TestGrantCapDrive(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { pf, err := roClient.PacketFilter() assert.NoError(c, err) - assert.False(c, hasCapMatchInPacketFilter(pf, tailcfg.PeerCapabilityTaildrive), + assert.False(c, hasCapMatchInPacketFilter(pf, peercap.Taildrive), "ro-client should NOT have cap/drive") }, 10*time.Second, 500*time.Millisecond, "ro-client should not have cap/drive")