all: inline deprecated tailcfg capability aliases
Some checks failed
Build / build-nix (push) Failing after 4s
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Failing after 3s
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Failing after 4s
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Failing after 3s
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Failing after 1s
Build (main) / container (push) Failing after 0s
Check Generated Files / check-generated (push) Failing after 2s
Build (main) / binaries (amd64, darwin) (push) Failing after 0s
Build (main) / binaries (amd64, linux) (push) Failing after 0s
Build (main) / binaries (arm64, darwin) (push) Failing after 0s
Build (main) / binaries (arm64, linux) (push) Failing after 0s
Deploy docs / deploy (push) Failing after 2s
Nix Flake Checks / gotest (push) Failing after 2s
Nix Flake Checks / build (push) Failing after 3s
Nix Flake Checks / golangci-lint (push) Failing after 2s
Nix Flake Checks / formatting (push) Failing after 1s
NixOS Module Tests / nix-module-check (push) Failing after 2s
Server Tests / servertest (push) Failing after 2s

go 1.27 vet reports the //go:fix inline directives tailscale added to
the tailcfg cap aliases; applied with `go fix -inline ./...`.
This commit is contained in:
Kristoffer Dalby 2026-08-25 15:23:46 +00:00 committed by Kristoffer Dalby
parent 77cd81fe33
commit 63123196cc
14 changed files with 189 additions and 172 deletions

View file

@ -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:<profile>`

View file

@ -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")

View file

@ -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,
},
},
},

View file

@ -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
}

View file

@ -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

View file

@ -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"},
},

View file

@ -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),
},
},
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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].

View file

@ -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")