all: upgrade to Go 1.26rc2 and modernize codebase

This commit upgrades the codebase from Go 1.25.5 to Go 1.26rc2 and
adopts new language features.

Toolchain updates:
- go.mod: go 1.25.5 → go 1.26rc2
- flake.nix: buildGo125Module → buildGo126Module, go_1_25 → go_1_26
- flake.nix: build golangci-lint from source with Go 1.26
- Dockerfile.integration: golang:1.25-trixie → golang:1.26rc2-trixie
- Dockerfile.tailscale-HEAD: golang:1.25-alpine → golang:1.26rc2-alpine
- Dockerfile.derper: golang:alpine → golang:1.26rc2-alpine
- .goreleaser.yml: go mod tidy -compat=1.25 → -compat=1.26
- cmd/hi/run.go: fallback Go version 1.25 → 1.26rc2
- .pre-commit-config.yaml: simplify golangci-lint hook entry

Code modernization using Go 1.26 features:
- Replace tsaddr.SortPrefixes with slices.SortFunc + netip.Prefix.Compare
- Replace ptr.To(x) with new(x) syntax
- Replace errors.As with errors.AsType[T]

Lint rule updates:
- Add forbidigo rules to prevent regression to old patterns
This commit is contained in:
Kristoffer Dalby
2026-02-06 21:39:35 +00:00
parent 20dff82f95
commit 0f6d312ada
50 changed files with 508 additions and 521 deletions

View File

@@ -6,8 +6,7 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is this a support request? label: Is this a support request?
description: description: This issue tracker is for bugs and feature requests only. If you need
This issue tracker is for bugs and feature requests only. If you need
help, please use ask in our Discord community help, please use ask in our Discord community
options: options:
- label: This is not a support request - label: This is not a support request
@@ -15,8 +14,7 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? label: Is there an existing issue for this?
description: description: Please search to see if an issue already exists for the bug you
Please search to see if an issue already exists for the bug you
encountered. encountered.
options: options:
- label: I have searched the existing issues - label: I have searched the existing issues

View File

@@ -45,6 +45,16 @@ linters:
Import "github.com/juanfont/headscale/hscontrol/util/zlog/zf" and use Import "github.com/juanfont/headscale/hscontrol/util/zlog/zf" and use
constants like zf.NodeID, zf.UserName, etc. Add new constants to constants like zf.NodeID, zf.UserName, etc. Add new constants to
hscontrol/util/zlog/zf/fields.go if needed. hscontrol/util/zlog/zf/fields.go if needed.
# Forbid ptr.To - use Go 1.26 new(expr) instead
- pattern: 'ptr\.To\('
msg: >-
ptr.To is forbidden. Use Go 1.26's new(expr) syntax instead.
Example: ptr.To(value) → new(value)
# Forbid tsaddr.SortPrefixes - use slices.SortFunc with netip.Prefix.Compare
- pattern: 'tsaddr\.SortPrefixes'
msg: >-
tsaddr.SortPrefixes is forbidden. Use Go 1.26's netip.Prefix.Compare instead.
Example: slices.SortFunc(prefixes, netip.Prefix.Compare)
analyze-types: true analyze-types: true
gocritic: gocritic:
disabled-checks: disabled-checks:

View File

@@ -2,7 +2,7 @@
version: 2 version: 2
before: before:
hooks: hooks:
- go mod tidy -compat=1.25 - go mod tidy -compat=1.26
- go mod vendor - go mod vendor
release: release:

View File

@@ -43,26 +43,12 @@ repos:
entry: prettier --write --list-different entry: prettier --write --list-different
language: system language: system
exclude: ^docs/ exclude: ^docs/
types_or: types_or: [javascript, jsx, ts, tsx, yaml, json, toml, html, css, scss, sass, markdown]
[
javascript,
jsx,
ts,
tsx,
yaml,
json,
toml,
html,
css,
scss,
sass,
markdown,
]
# golangci-lint for Go code quality # golangci-lint for Go code quality
- id: golangci-lint - id: golangci-lint
name: golangci-lint name: golangci-lint
entry: nix develop --command golangci-lint run --new-from-rev=HEAD~1 --timeout=5m --fix entry: golangci-lint run --new-from-rev=HEAD~1 --timeout=5m --fix
language: system language: system
types: [go] types: [go]
pass_filenames: false pass_filenames: false

View File

@@ -1,6 +1,6 @@
# For testing purposes only # For testing purposes only
FROM golang:alpine AS build-env FROM golang:1.26rc2-alpine AS build-env
WORKDIR /go/src WORKDIR /go/src

View File

@@ -2,7 +2,7 @@
# and are in no way endorsed by Headscale's maintainers as an # and are in no way endorsed by Headscale's maintainers as an
# official nor supported release or distribution. # official nor supported release or distribution.
FROM docker.io/golang:1.25-trixie AS builder FROM docker.io/golang:1.26rc2-trixie AS builder
ARG VERSION=dev ARG VERSION=dev
ENV GOPATH /go ENV GOPATH /go
WORKDIR /go/src/headscale WORKDIR /go/src/headscale

View File

@@ -4,7 +4,7 @@
# This Dockerfile is more or less lifted from tailscale/tailscale # This Dockerfile is more or less lifted from tailscale/tailscale
# to ensure a similar build process when testing the HEAD of tailscale. # to ensure a similar build process when testing the HEAD of tailscale.
FROM golang:1.25-alpine AS build-env FROM golang:1.26rc2-alpine AS build-env
WORKDIR /go/src WORKDIR /go/src

View File

@@ -23,8 +23,7 @@ var serveCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
app, err := newHeadscaleServerWithConfig() app, err := newHeadscaleServerWithConfig()
if err != nil { if err != nil {
var squibbleErr squibble.ValidationError if squibbleErr, ok := errors.AsType[squibble.ValidationError](err); ok {
if errors.As(err, &squibbleErr) {
fmt.Printf("SQLite schema failed to validate:\n") fmt.Printf("SQLite schema failed to validate:\n")
fmt.Println(squibbleErr.Diff) fmt.Println(squibbleErr.Diff)
} }

View File

@@ -76,7 +76,7 @@ func detectGoVersion() string {
content, err := os.ReadFile(goModPath) content, err := os.ReadFile(goModPath)
if err != nil { if err != nil {
return "1.25" return "1.26rc2"
} }
lines := splitLines(string(content)) lines := splitLines(string(content))
@@ -91,7 +91,7 @@ func detectGoVersion() string {
} }
} }
return "1.25" return "1.26rc2"
} }
// splitLines splits a string into lines without using strings.Split. // splitLines splits a string into lines without using strings.Split.

View File

@@ -26,7 +26,7 @@
overlays.default = _: prev: overlays.default = _: prev:
let let
pkgs = nixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system}; pkgs = nixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system};
buildGo = pkgs.buildGo125Module; buildGo = pkgs.buildGo126Module;
vendorHash = "sha256-9BvphYDAxzwooyVokI3l+q1wRuRsWn/qM+NpWUgqJH0="; vendorHash = "sha256-9BvphYDAxzwooyVokI3l+q1wRuRsWn/qM+NpWUgqJH0=";
in in
{ {
@@ -94,14 +94,46 @@
subPackages = [ "." ]; subPackages = [ "." ];
}; };
# Upstream does not override buildGoModule properly, # Build golangci-lint with Go 1.26 (upstream uses hardcoded Go version)
# importing a specific module, so comment out for now. golangci-lint = buildGo rec {
# golangci-lint = prev.golangci-lint.override { pname = "golangci-lint";
# buildGoModule = buildGo; version = "2.8.0";
# };
# golangci-lint-langserver = prev.golangci-lint.override { src = pkgs.fetchFromGitHub {
# buildGoModule = buildGo; owner = "golangci";
# }; repo = "golangci-lint";
rev = "v${version}";
hash = "sha256-w6MAOirj8rPHYbKrW4gJeemXCS64fNtteV6IioqIQTQ=";
};
vendorHash = "sha256-/Vqo/yrmGh6XipELQ9NDtlMEO2a654XykmvnMs0BdrI=";
subPackages = [ "cmd/golangci-lint" ];
nativeBuildInputs = [ pkgs.installShellFiles ];
ldflags = [
"-s"
"-w"
"-X main.version=${version}"
"-X main.commit=v${version}"
"-X main.date=1970-01-01T00:00:00Z"
];
postInstall = ''
for shell in bash zsh fish; do
HOME=$TMPDIR $out/bin/golangci-lint completion $shell > golangci-lint.$shell
installShellCompletion golangci-lint.$shell
done
'';
meta = {
description = "Fast linters runner for Go";
homepage = "https://golangci-lint.run/";
changelog = "https://github.com/golangci/golangci-lint/blob/v${version}/CHANGELOG.md";
mainProgram = "golangci-lint";
};
};
# The package uses buildGo125Module, not the convention. # The package uses buildGo125Module, not the convention.
# goreleaser = prev.goreleaser.override { # goreleaser = prev.goreleaser.override {
@@ -132,7 +164,7 @@
overlays = [ self.overlays.default ]; overlays = [ self.overlays.default ];
inherit system; inherit system;
}; };
buildDeps = with pkgs; [ git go_1_25 gnumake ]; buildDeps = with pkgs; [ git go_1_26 gnumake ];
devDeps = with pkgs; devDeps = with pkgs;
buildDeps buildDeps
++ [ ++ [

2
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/juanfont/headscale module github.com/juanfont/headscale
go 1.25.5 go 1.26rc2
require ( require (
github.com/arl/statsviz v0.8.0 github.com/arl/statsviz v0.8.0

View File

@@ -16,7 +16,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
type AuthProvider interface { type AuthProvider interface {
@@ -113,8 +112,7 @@ func (h *Headscale) handleRegister(
resp, err := h.handleRegisterWithAuthKey(req, machineKey) resp, err := h.handleRegisterWithAuthKey(req, machineKey)
if err != nil { if err != nil {
// Preserve HTTPError types so they can be handled properly by the HTTP layer // Preserve HTTPError types so they can be handled properly by the HTTP layer
var httpErr HTTPError if httpErr, ok := errors.AsType[HTTPError](err); ok {
if errors.As(err, &httpErr) {
return nil, httpErr return nil, httpErr
} }
@@ -315,7 +313,7 @@ func (h *Headscale) reqToNewRegisterResponse(
MachineKey: machineKey, MachineKey: machineKey,
NodeKey: req.NodeKey, NodeKey: req.NodeKey,
Hostinfo: hostinfo, Hostinfo: hostinfo,
LastSeen: ptr.To(time.Now()), LastSeen: new(time.Now()),
}, },
) )
@@ -344,8 +342,7 @@ func (h *Headscale) handleRegisterWithAuthKey(
return nil, NewHTTPError(http.StatusUnauthorized, "invalid pre auth key", nil) return nil, NewHTTPError(http.StatusUnauthorized, "invalid pre auth key", nil)
} }
var perr types.PAKError if perr, ok := errors.AsType[types.PAKError](err); ok {
if errors.As(err, &perr) {
return nil, NewHTTPError(http.StatusUnauthorized, perr.Error(), nil) return nil, NewHTTPError(http.StatusUnauthorized, perr.Error(), nil)
} }
@@ -443,7 +440,7 @@ func (h *Headscale) handleRegisterInteractive(
MachineKey: machineKey, MachineKey: machineKey,
NodeKey: req.NodeKey, NodeKey: req.NodeKey,
Hostinfo: hostinfo, Hostinfo: hostinfo,
LastSeen: ptr.To(time.Now()), LastSeen: new(time.Now()),
}, },
) )

View File

@@ -24,7 +24,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
"gorm.io/gorm/schema" "gorm.io/gorm/schema"
"tailscale.com/net/tsaddr"
"zgo.at/zcache/v2" "zgo.at/zcache/v2"
) )
@@ -172,7 +171,7 @@ AND auth_key_id NOT IN (
} }
for nodeID, routes := range nodeRoutes { for nodeID, routes := range nodeRoutes {
tsaddr.SortPrefixes(routes) slices.SortFunc(routes, netip.Prefix.Compare)
routes = slices.Compact(routes) routes = slices.Compact(routes)
data, _ := json.Marshal(routes) data, _ := json.Marshal(routes)

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/types/ptr"
) )
var mpp = func(pref string) *netip.Prefix { var mpp = func(pref string) *netip.Prefix {
@@ -491,8 +490,8 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
alloc, err := NewIPAllocator( alloc, err := NewIPAllocator(
db, db,
ptr.To(tsaddr.CGNATRange()), new(tsaddr.CGNATRange()),
ptr.To(tsaddr.TailscaleULARange()), new(tsaddr.TailscaleULARange()),
types.IPAllocationStrategySequential, types.IPAllocationStrategySequential,
) )
if err != nil { if err != nil {
@@ -500,17 +499,17 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
} }
// Validate that we do not give out 100.100.100.100 // Validate that we do not give out 100.100.100.100
nextQuad100, err := alloc.next(na("100.100.100.99"), ptr.To(tsaddr.CGNATRange())) nextQuad100, err := alloc.next(na("100.100.100.99"), new(tsaddr.CGNATRange()))
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, na("100.100.100.101"), *nextQuad100) assert.Equal(t, na("100.100.100.101"), *nextQuad100)
// Validate that we do not give out fd7a:115c:a1e0::53 // Validate that we do not give out fd7a:115c:a1e0::53
nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), ptr.To(tsaddr.TailscaleULARange())) nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), new(tsaddr.TailscaleULARange()))
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, na("fd7a:115c:a1e0::54"), *nextQuad100v6) assert.Equal(t, na("fd7a:115c:a1e0::54"), *nextQuad100v6)
// Validate that we do not give out fd7a:115c:a1e0::53 // Validate that we do not give out fd7a:115c:a1e0::53
nextChrome, err := alloc.next(na("100.115.91.255"), ptr.To(tsaddr.CGNATRange())) nextChrome, err := alloc.next(na("100.115.91.255"), new(tsaddr.CGNATRange()))
t.Logf("chrome: %s", nextChrome.String()) t.Logf("chrome: %s", nextChrome.String())
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, na("100.115.94.0"), *nextChrome) assert.Equal(t, na("100.115.94.0"), *nextChrome)

View File

@@ -21,7 +21,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
const ( const (
@@ -694,7 +693,7 @@ func (hsdb *HSDatabase) CreateNodeForTest(user *types.User, hostname ...string)
Hostname: nodeName, Hostname: nodeName,
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: new(pak.ID),
} }
err = hsdb.DB.Save(node).Error err = hsdb.DB.Save(node).Error

View File

@@ -22,7 +22,6 @@ import (
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
func TestGetNode(t *testing.T) { func TestGetNode(t *testing.T) {
@@ -115,7 +114,7 @@ func TestExpireNode(t *testing.T) {
Hostname: "testnode", Hostname: "testnode",
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: new(pak.ID),
Expiry: &time.Time{}, Expiry: &time.Time{},
} }
db.DB.Save(node) db.DB.Save(node)
@@ -159,7 +158,7 @@ func TestSetTags(t *testing.T) {
Hostname: "testnode", Hostname: "testnode",
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: new(pak.ID),
} }
trx := db.DB.Save(node) trx := db.DB.Save(node)
@@ -444,7 +443,7 @@ func TestAutoApproveRoutes(t *testing.T) {
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.routes, RoutableIPs: tt.routes,
}, },
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")), IPv4: new(netip.MustParseAddr("100.64.0.1")),
} }
err = adb.DB.Save(&node).Error err = adb.DB.Save(&node).Error
@@ -461,7 +460,7 @@ func TestAutoApproveRoutes(t *testing.T) {
RoutableIPs: tt.routes, RoutableIPs: tt.routes,
}, },
Tags: []string{"tag:exit"}, Tags: []string{"tag:exit"},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")), IPv4: new(netip.MustParseAddr("100.64.0.2")),
} }
err = adb.DB.Save(&nodeTagged).Error err = adb.DB.Save(&nodeTagged).Error
@@ -660,7 +659,7 @@ func TestListEphemeralNodes(t *testing.T) {
Hostname: "test", Hostname: "test",
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: new(pak.ID),
} }
nodeEph := types.Node{ nodeEph := types.Node{
@@ -670,7 +669,7 @@ func TestListEphemeralNodes(t *testing.T) {
Hostname: "ephemeral", Hostname: "ephemeral",
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pakEph.ID), AuthKeyID: new(pakEph.ID),
} }
err = db.DB.Save(&node).Error err = db.DB.Save(&node).Error
@@ -763,8 +762,8 @@ func TestNodeNaming(t *testing.T) {
return err return err
} }
_, _ = RegisterNodeForTest(tx, nodeInvalidHostname, ptr.To(mpp("100.64.0.66/32").Addr()), nil) _, _ = RegisterNodeForTest(tx, nodeInvalidHostname, new(mpp("100.64.0.66/32").Addr()), nil)
_, err = RegisterNodeForTest(tx, nodeShortHostname, ptr.To(mpp("100.64.0.67/32").Addr()), nil) _, err = RegisterNodeForTest(tx, nodeShortHostname, new(mpp("100.64.0.67/32").Addr()), nil)
return err return err
}) })

View File

@@ -11,7 +11,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/types/ptr"
) )
func TestCreatePreAuthKey(t *testing.T) { func TestCreatePreAuthKey(t *testing.T) {
@@ -24,7 +23,7 @@ func TestCreatePreAuthKey(t *testing.T) {
test: func(t *testing.T, db *HSDatabase) { test: func(t *testing.T, db *HSDatabase) {
t.Helper() t.Helper()
_, err := db.CreatePreAuthKey(ptr.To(types.UserID(12345)), true, false, nil, nil) _, err := db.CreatePreAuthKey(new(types.UserID(12345)), true, false, nil, nil)
assert.Error(t, err) assert.Error(t, err)
}, },
}, },
@@ -127,7 +126,7 @@ func TestCannotDeleteAssignedPreAuthKey(t *testing.T) {
Hostname: "testest", Hostname: "testest",
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(key.ID), AuthKeyID: new(key.ID),
} }
db.DB.Save(&node) db.DB.Save(&node)

View File

@@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/types/ptr"
) )
func TestCreateAndDestroyUser(t *testing.T) { func TestCreateAndDestroyUser(t *testing.T) {
@@ -79,7 +78,7 @@ func TestDestroyUserErrors(t *testing.T) {
Hostname: "testnode", Hostname: "testnode",
UserID: &user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: new(pak.ID),
} }
trx := db.DB.Save(&node) trx := db.DB.Save(&node)
require.NoError(t, trx.Error) require.NoError(t, trx.Error)

View File

@@ -387,7 +387,7 @@ func (api headscaleV1APIServer) SetApprovedRoutes(
newApproved = append(newApproved, prefix) newApproved = append(newApproved, prefix)
} }
} }
tsaddr.SortPrefixes(newApproved) slices.SortFunc(newApproved, netip.Prefix.Compare)
newApproved = slices.Compact(newApproved) newApproved = slices.Compact(newApproved)
node, nodeChange, err := api.h.state.SetApprovedRoutes(types.NodeID(request.GetNodeId()), newApproved) node, nodeChange, err := api.h.state.SetApprovedRoutes(types.NodeID(request.GetNodeId()), newApproved)

View File

@@ -36,8 +36,7 @@ const (
// httpError logs an error and sends an HTTP error response with the given. // httpError logs an error and sends an HTTP error response with the given.
func httpError(w http.ResponseWriter, err error) { func httpError(w http.ResponseWriter, err error) {
var herr HTTPError if herr, ok := errors.AsType[HTTPError](err); ok {
if errors.As(err, &herr) {
http.Error(w, herr.Msg, herr.Code) http.Error(w, herr.Msg, herr.Code)
log.Error().Err(herr.Err).Int("code", herr.Code).Msgf("user msg: %s", herr.Msg) log.Error().Err(herr.Err).Int("code", herr.Code).Msgf("user msg: %s", herr.Msg)
} else { } else {

View File

@@ -16,7 +16,6 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
// LockFreeBatcher errors. // LockFreeBatcher errors.
@@ -151,7 +150,7 @@ func (b *LockFreeBatcher) RemoveNode(id types.NodeID, c chan<- *tailcfg.MapRespo
// No active connections - keep the node entry alive for rapid reconnections // No active connections - keep the node entry alive for rapid reconnections
// The node will get a fresh full map when it reconnects // The node will get a fresh full map when it reconnects
nlog.Debug().Caller().Msg("node disconnected from batcher, keeping entry for rapid reconnection") nlog.Debug().Caller().Msg("node disconnected from batcher, keeping entry for rapid reconnection")
b.connected.Store(id, ptr.To(time.Now())) b.connected.Store(id, new(time.Now()))
return false return false
} }

View File

@@ -10,7 +10,6 @@ import (
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/dnstype" "tailscale.com/types/dnstype"
"tailscale.com/types/ptr"
) )
var iap = func(ipStr string) *netip.Addr { var iap = func(ipStr string) *netip.Addr {
@@ -47,7 +46,7 @@ func TestDNSConfigMapResponse(t *testing.T) {
mach := func(hostname, username string, userid uint) *types.Node { mach := func(hostname, username string, userid uint) *types.Node {
return &types.Node{ return &types.Node{
Hostname: hostname, Hostname: hostname,
UserID: ptr.To(userid), UserID: new(userid),
User: &types.User{ User: &types.User{
Name: username, Name: username,
}, },

View File

@@ -13,7 +13,6 @@ import (
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
func TestTailNode(t *testing.T) { func TestTailNode(t *testing.T) {
@@ -98,7 +97,7 @@ func TestTailNode(t *testing.T) {
IPv4: iap("100.64.0.1"), IPv4: iap("100.64.0.1"),
Hostname: "mini", Hostname: "mini",
GivenName: "mini", GivenName: "mini",
UserID: ptr.To(uint(0)), UserID: new(uint(0)),
User: &types.User{ User: &types.User{
Name: "mini", Name: "mini",
}, },
@@ -140,8 +139,8 @@ func TestTailNode(t *testing.T) {
Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")}, Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")},
AllowedIPs: []netip.Prefix{ AllowedIPs: []netip.Prefix{
tsaddr.AllIPv4(), tsaddr.AllIPv4(),
netip.MustParsePrefix("192.168.0.0/24"),
netip.MustParsePrefix("100.64.0.1/32"), netip.MustParsePrefix("100.64.0.1/32"),
netip.MustParsePrefix("192.168.0.0/24"),
tsaddr.AllIPv6(), tsaddr.AllIPv6(),
}, },
PrimaryRoutes: []netip.Prefix{ PrimaryRoutes: []netip.Prefix{

View File

@@ -265,8 +265,7 @@ func (ns *noiseServer) NoiseRegistrationHandler(
resp, err = ns.headscale.handleRegister(req.Context(), regReq, ns.conn.Peer()) resp, err = ns.headscale.handleRegister(req.Context(), regReq, ns.conn.Peer())
if err != nil { if err != nil {
var httpErr HTTPError if httpErr, ok := errors.AsType[HTTPError](err); ok {
if errors.As(err, &httpErr) {
resp = &tailcfg.RegisterResponse{ resp = &tailcfg.RegisterResponse{
Error: httpErr.Msg, Error: httpErr.Msg,
} }

View File

@@ -9,7 +9,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/samber/lo" "github.com/samber/lo"
"tailscale.com/net/tsaddr"
"tailscale.com/types/views" "tailscale.com/types/views"
) )
@@ -111,7 +110,7 @@ func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApprove
} }
// Sort and deduplicate // Sort and deduplicate
tsaddr.SortPrefixes(newApproved) slices.SortFunc(newApproved, netip.Prefix.Compare)
newApproved = slices.Compact(newApproved) newApproved = slices.Compact(newApproved)
newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool { newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool {
return route.IsValid() return route.IsValid()
@@ -120,7 +119,7 @@ func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApprove
// Sort the current approved for comparison // Sort the current approved for comparison
sortedCurrent := make([]netip.Prefix, len(currentApproved)) sortedCurrent := make([]netip.Prefix, len(currentApproved))
copy(sortedCurrent, currentApproved) copy(sortedCurrent, currentApproved)
tsaddr.SortPrefixes(sortedCurrent) slices.SortFunc(sortedCurrent, netip.Prefix.Compare)
// Only update if the routes actually changed // Only update if the routes actually changed
if !slices.Equal(sortedCurrent, newApproved) { if !slices.Equal(sortedCurrent, newApproved) {

View File

@@ -3,6 +3,7 @@ package policy
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"slices"
"testing" "testing"
policyv2 "github.com/juanfont/headscale/hscontrol/policy/v2" policyv2 "github.com/juanfont/headscale/hscontrol/policy/v2"
@@ -11,9 +12,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
"tailscale.com/types/views" "tailscale.com/types/views"
) )
@@ -33,10 +32,10 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test-node", Hostname: "test-node",
UserID: ptr.To(user1.ID), UserID: new(user1.ID),
User: ptr.To(user1), User: new(user1),
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")), IPv4: new(netip.MustParseAddr("100.64.0.1")),
Tags: []string{"tag:test"}, Tags: []string{"tag:test"},
} }
@@ -45,10 +44,10 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "other-node", Hostname: "other-node",
UserID: ptr.To(user2.ID), UserID: new(user2.ID),
User: ptr.To(user2), User: new(user2),
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")), IPv4: new(netip.MustParseAddr("100.64.0.2")),
} }
// Create a policy that auto-approves specific routes // Create a policy that auto-approves specific routes
@@ -195,7 +194,7 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
assert.Equal(t, tt.wantChanged, gotChanged, "changed flag mismatch: %s", tt.description) assert.Equal(t, tt.wantChanged, gotChanged, "changed flag mismatch: %s", tt.description)
// Sort for comparison since ApproveRoutesWithPolicy sorts the results // Sort for comparison since ApproveRoutesWithPolicy sorts the results
tsaddr.SortPrefixes(tt.wantApproved) slices.SortFunc(tt.wantApproved, netip.Prefix.Compare)
assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch: %s", tt.description) assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch: %s", tt.description)
// Verify that all previously approved routes are still present // Verify that all previously approved routes are still present
@@ -305,10 +304,10 @@ func TestApproveRoutesWithPolicy_NilAndEmptyCases(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: ptr.To(user.ID), UserID: new(user.ID),
User: ptr.To(user), User: new(user),
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")), IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: tt.currentApproved, ApprovedRoutes: tt.currentApproved,
} }
nodes := types.Nodes{&node} nodes := types.Nodes{&node}
@@ -334,7 +333,7 @@ func TestApproveRoutesWithPolicy_NilAndEmptyCases(t *testing.T) {
if tt.wantApproved == nil { if tt.wantApproved == nil {
assert.Nil(t, gotApproved, "expected nil approved routes") assert.Nil(t, gotApproved, "expected nil approved routes")
} else { } else {
tsaddr.SortPrefixes(tt.wantApproved) slices.SortFunc(tt.wantApproved, netip.Prefix.Compare)
assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch") assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch")
} }
}) })

View File

@@ -13,7 +13,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) { func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
@@ -92,8 +91,8 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
announcedRoutes: []netip.Prefix{}, // No routes announced anymore announcedRoutes: []netip.Prefix{}, // No routes announced anymore
nodeUser: "test", nodeUser: "test",
wantApproved: []netip.Prefix{ wantApproved: []netip.Prefix{
netip.MustParsePrefix("172.16.0.0/16"),
netip.MustParsePrefix("10.0.0.0/24"), netip.MustParsePrefix("10.0.0.0/24"),
netip.MustParsePrefix("172.16.0.0/16"),
netip.MustParsePrefix("192.168.0.0/24"), netip.MustParsePrefix("192.168.0.0/24"),
}, },
wantChanged: false, wantChanged: false,
@@ -124,8 +123,8 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
nodeUser: "test", nodeUser: "test",
nodeTags: []string{"tag:approved"}, nodeTags: []string{"tag:approved"},
wantApproved: []netip.Prefix{ wantApproved: []netip.Prefix{
netip.MustParsePrefix("172.16.0.0/16"), // New tag-approved
netip.MustParsePrefix("10.0.0.0/24"), // Previous approval preserved netip.MustParsePrefix("10.0.0.0/24"), // Previous approval preserved
netip.MustParsePrefix("172.16.0.0/16"), // New tag-approved
}, },
wantChanged: true, wantChanged: true,
}, },
@@ -168,13 +167,13 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: tt.nodeHostname, Hostname: tt.nodeHostname,
UserID: ptr.To(user.ID), UserID: new(user.ID),
User: ptr.To(user), User: new(user),
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.announcedRoutes, RoutableIPs: tt.announcedRoutes,
}, },
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")), IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: tt.currentApproved, ApprovedRoutes: tt.currentApproved,
Tags: tt.nodeTags, Tags: tt.nodeTags,
} }
@@ -294,13 +293,13 @@ func TestApproveRoutesWithPolicy_EdgeCases(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: ptr.To(user.ID), UserID: new(user.ID),
User: ptr.To(user), User: new(user),
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.announcedRoutes, RoutableIPs: tt.announcedRoutes,
}, },
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")), IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: tt.currentApproved, ApprovedRoutes: tt.currentApproved,
} }
nodes := types.Nodes{&node} nodes := types.Nodes{&node}
@@ -343,13 +342,13 @@ func TestApproveRoutesWithPolicy_NilPolicyManagerCase(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: ptr.To(user.ID), UserID: new(user.ID),
User: ptr.To(user), User: new(user),
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: announcedRoutes, RoutableIPs: announcedRoutes,
}, },
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")), IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: currentApproved, ApprovedRoutes: currentApproved,
} }

View File

@@ -14,7 +14,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
var ap = func(ipStr string) *netip.Addr { var ap = func(ipStr string) *netip.Addr {
@@ -1084,21 +1083,21 @@ func TestSSHPolicyRules(t *testing.T) {
nodeUser1 := types.Node{ nodeUser1 := types.Node{
Hostname: "user1-device", Hostname: "user1-device",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
UserID: ptr.To(uint(1)), UserID: new(uint(1)),
User: ptr.To(users[0]), User: new(users[0]),
} }
nodeUser2 := types.Node{ nodeUser2 := types.Node{
Hostname: "user2-device", Hostname: "user2-device",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
UserID: ptr.To(uint(2)), UserID: new(uint(2)),
User: ptr.To(users[1]), User: new(users[1]),
} }
taggedClient := types.Node{ taggedClient := types.Node{
Hostname: "tagged-client", Hostname: "tagged-client",
IPv4: ap("100.64.0.4"), IPv4: ap("100.64.0.4"),
UserID: ptr.To(uint(2)), UserID: new(uint(2)),
User: ptr.To(users[1]), User: new(users[1]),
Tags: []string{"tag:client"}, Tags: []string{"tag:client"},
} }
@@ -1106,8 +1105,8 @@ func TestSSHPolicyRules(t *testing.T) {
nodeTaggedServer := types.Node{ nodeTaggedServer := types.Node{
Hostname: "tagged-server", Hostname: "tagged-server",
IPv4: ap("100.64.0.5"), IPv4: ap("100.64.0.5"),
UserID: ptr.To(uint(1)), UserID: new(uint(1)),
User: ptr.To(users[0]), User: new(users[0]),
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
} }

View File

@@ -17,7 +17,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
"tailscale.com/util/must" "tailscale.com/util/must"
) )
@@ -145,13 +144,13 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"), IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
User: ptr.To(users[0]), User: new(users[0]),
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"), IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
User: ptr.To(users[0]), User: new(users[0]),
}, },
}, },
want: []tailcfg.FilterRule{}, want: []tailcfg.FilterRule{},
@@ -192,7 +191,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{ RoutableIPs: []netip.Prefix{
netip.MustParsePrefix("10.33.0.0/16"), netip.MustParsePrefix("10.33.0.0/16"),
@@ -203,7 +202,7 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
}, },
want: []tailcfg.FilterRule{ want: []tailcfg.FilterRule{
@@ -274,19 +273,19 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]), User: new(users[2]),
}, },
// "internal" exit node // "internal" exit node
&types.Node{ &types.Node{
IPv4: ap("100.64.0.100"), IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"), IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]), User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(), RoutableIPs: tsaddr.ExitRoutes(),
}, },
@@ -335,7 +334,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.100"), IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"), IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]), User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(), RoutableIPs: tsaddr.ExitRoutes(),
}, },
@@ -344,12 +343,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]), User: new(users[2]),
}, },
&types.Node{ &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
}, },
want: []tailcfg.FilterRule{ want: []tailcfg.FilterRule{
@@ -442,7 +441,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.100"), IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"), IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]), User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(), RoutableIPs: tsaddr.ExitRoutes(),
}, },
@@ -451,12 +450,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]), User: new(users[2]),
}, },
&types.Node{ &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
}, },
want: []tailcfg.FilterRule{ want: []tailcfg.FilterRule{
@@ -549,7 +548,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.100"), IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"), IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]), User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16")}, RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16")},
}, },
@@ -558,12 +557,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]), User: new(users[2]),
}, },
&types.Node{ &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
}, },
want: []tailcfg.FilterRule{ want: []tailcfg.FilterRule{
@@ -634,7 +633,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.100"), IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"), IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]), User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8")}, RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8")},
}, },
@@ -643,12 +642,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{ &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]), User: new(users[2]),
}, },
&types.Node{ &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
}, },
want: []tailcfg.FilterRule{ want: []tailcfg.FilterRule{
@@ -711,7 +710,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.100"), IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"), IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]), User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")}, RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
}, },
@@ -721,7 +720,7 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{ &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
}, },
}, },
want: []tailcfg.FilterRule{ want: []tailcfg.FilterRule{
@@ -778,13 +777,13 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{ node: &types.Node{
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[3]), User: new(users[3]),
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]), User: new(users[1]),
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{p("172.16.0.0/24"), p("10.10.11.0/24"), p("10.10.12.0/24")}, RoutableIPs: []netip.Prefix{p("172.16.0.0/24"), p("10.10.11.0/24"), p("10.10.12.0/24")},
}, },

View File

@@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/types/ptr"
) )
func TestNodeCanApproveRoute(t *testing.T) { func TestNodeCanApproveRoute(t *testing.T) {
@@ -25,24 +24,24 @@ func TestNodeCanApproveRoute(t *testing.T) {
ID: 1, ID: 1,
Hostname: "user1-device", Hostname: "user1-device",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
UserID: ptr.To(uint(1)), UserID: new(uint(1)),
User: ptr.To(users[0]), User: new(users[0]),
} }
exitNode := types.Node{ exitNode := types.Node{
ID: 2, ID: 2,
Hostname: "user2-device", Hostname: "user2-device",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
UserID: ptr.To(uint(2)), UserID: new(uint(2)),
User: ptr.To(users[1]), User: new(users[1]),
} }
taggedNode := types.Node{ taggedNode := types.Node{
ID: 3, ID: 3,
Hostname: "tagged-server", Hostname: "tagged-server",
IPv4: ap("100.64.0.3"), IPv4: ap("100.64.0.3"),
UserID: ptr.To(uint(3)), UserID: new(uint(3)),
User: ptr.To(users[2]), User: new(users[2]),
Tags: []string{"tag:router"}, Tags: []string{"tag:router"},
} }
@@ -50,8 +49,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
ID: 4, ID: 4,
Hostname: "multi-tag-node", Hostname: "multi-tag-node",
IPv4: ap("100.64.0.4"), IPv4: ap("100.64.0.4"),
UserID: ptr.To(uint(2)), UserID: new(uint(2)),
User: ptr.To(users[1]), User: new(users[1]),
Tags: []string{"tag:router", "tag:server"}, Tags: []string{"tag:router", "tag:server"},
} }

View File

@@ -15,7 +15,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
// aliasWithPorts creates an AliasWithPorts structure from an alias and ports. // aliasWithPorts creates an AliasWithPorts structure from an alias and ports.
@@ -411,15 +410,15 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
nodeTaggedServer := types.Node{ nodeTaggedServer := types.Node{
Hostname: "tagged-server", Hostname: "tagged-server",
IPv4: createAddr("100.64.0.1"), IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
User: ptr.To(users[0]), User: new(users[0]),
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
} }
nodeTaggedDB := types.Node{ nodeTaggedDB := types.Node{
Hostname: "tagged-db", Hostname: "tagged-db",
IPv4: createAddr("100.64.0.2"), IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
User: ptr.To(users[1]), User: new(users[1]),
Tags: []string{"tag:database"}, Tags: []string{"tag:database"},
} }
// Add untagged node for user2 - this will be the SSH source // Add untagged node for user2 - this will be the SSH source
@@ -427,8 +426,8 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
nodeUser2Untagged := types.Node{ nodeUser2Untagged := types.Node{
Hostname: "user2-device", Hostname: "user2-device",
IPv4: createAddr("100.64.0.3"), IPv4: createAddr("100.64.0.3"),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
User: ptr.To(users[1]), User: new(users[1]),
} }
nodes := types.Nodes{&nodeTaggedServer, &nodeTaggedDB, &nodeUser2Untagged} nodes := types.Nodes{&nodeTaggedServer, &nodeTaggedDB, &nodeUser2Untagged}
@@ -658,15 +657,15 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
nodeTaggedServer := types.Node{ nodeTaggedServer := types.Node{
Hostname: "tagged-server", Hostname: "tagged-server",
IPv4: createAddr("100.64.0.1"), IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
User: ptr.To(users[0]), User: new(users[0]),
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
} }
nodeUser2 := types.Node{ nodeUser2 := types.Node{
Hostname: "user2-device", Hostname: "user2-device",
IPv4: createAddr("100.64.0.2"), IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
User: ptr.To(users[1]), User: new(users[1]),
} }
nodes := types.Nodes{&nodeTaggedServer, &nodeUser2} nodes := types.Nodes{&nodeTaggedServer, &nodeUser2}
@@ -723,15 +722,15 @@ func TestSSHIntegrationReproduction(t *testing.T) {
node1 := &types.Node{ node1 := &types.Node{
Hostname: "user1-node", Hostname: "user1-node",
IPv4: createAddr("100.64.0.1"), IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
User: ptr.To(users[0]), User: new(users[0]),
} }
node2 := &types.Node{ node2 := &types.Node{
Hostname: "user2-node", Hostname: "user2-node",
IPv4: createAddr("100.64.0.2"), IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
User: ptr.To(users[1]), User: new(users[1]),
} }
nodes := types.Nodes{node1, node2} nodes := types.Nodes{node1, node2}
@@ -848,19 +847,19 @@ func TestCompileFilterRulesForNodeWithAutogroupSelf(t *testing.T) {
nodes := types.Nodes{ nodes := types.Nodes{
{ {
User: ptr.To(users[0]), User: new(users[0]),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
}, },
{ {
User: ptr.To(users[0]), User: new(users[0]),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
}, },
{ {
User: ptr.To(users[1]), User: new(users[1]),
IPv4: ap("100.64.0.3"), IPv4: ap("100.64.0.3"),
}, },
{ {
User: ptr.To(users[1]), User: new(users[1]),
IPv4: ap("100.64.0.4"), IPv4: ap("100.64.0.4"),
}, },
// Tagged device for user1 // Tagged device for user1
@@ -983,11 +982,11 @@ func TestTagUserMutualExclusivity(t *testing.T) {
nodes := types.Nodes{ nodes := types.Nodes{
// User-owned nodes // User-owned nodes
{ {
User: ptr.To(users[0]), User: new(users[0]),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
}, },
{ {
User: ptr.To(users[1]), User: new(users[1]),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
}, },
// Tagged nodes // Tagged nodes
@@ -1005,8 +1004,8 @@ func TestTagUserMutualExclusivity(t *testing.T) {
policy := &Policy{ policy := &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:server"): Owners{ptr.To(Username("user1@"))}, Tag("tag:server"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))}, Tag("tag:database"): Owners{new(Username("user2@"))},
}, },
ACLs: []ACL{ ACLs: []ACL{
// Rule 1: user1 (user-owned) should NOT be able to reach tagged nodes // Rule 1: user1 (user-owned) should NOT be able to reach tagged nodes
@@ -1101,11 +1100,11 @@ func TestAutogroupTagged(t *testing.T) {
nodes := types.Nodes{ nodes := types.Nodes{
// User-owned nodes (not tagged) // User-owned nodes (not tagged)
{ {
User: ptr.To(users[0]), User: new(users[0]),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
}, },
{ {
User: ptr.To(users[1]), User: new(users[1]),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
}, },
// Tagged nodes // Tagged nodes
@@ -1128,10 +1127,10 @@ func TestAutogroupTagged(t *testing.T) {
policy := &Policy{ policy := &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:server"): Owners{ptr.To(Username("user1@"))}, Tag("tag:server"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))}, Tag("tag:database"): Owners{new(Username("user2@"))},
Tag("tag:web"): Owners{ptr.To(Username("user1@"))}, Tag("tag:web"): Owners{new(Username("user1@"))},
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))}, Tag("tag:prod"): Owners{new(Username("user1@"))},
}, },
ACLs: []ACL{ ACLs: []ACL{
// Rule: autogroup:tagged can reach user-owned nodes // Rule: autogroup:tagged can reach user-owned nodes
@@ -1252,10 +1251,10 @@ func TestAutogroupSelfWithSpecificUserSource(t *testing.T) {
} }
nodes := types.Nodes{ nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")}, {User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")}, {User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")}, {User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")}, {User: new(users[1]), IPv4: ap("100.64.0.4")},
} }
policy := &Policy{ policy := &Policy{
@@ -1320,11 +1319,11 @@ func TestAutogroupSelfWithGroupSource(t *testing.T) {
} }
nodes := types.Nodes{ nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")}, {User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")}, {User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")}, {User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")}, {User: new(users[1]), IPv4: ap("100.64.0.4")},
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")}, {User: new(users[2]), IPv4: ap("100.64.0.5")},
} }
policy := &Policy{ policy := &Policy{
@@ -1389,13 +1388,13 @@ func TestSSHWithAutogroupSelfInDestination(t *testing.T) {
nodes := types.Nodes{ nodes := types.Nodes{
// User1's nodes // User1's nodes
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-node1"}, {User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-node1"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"}, {User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"},
// User2's nodes // User2's nodes
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-node1"}, {User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-node1"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"}, {User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"},
// Tagged node for user1 (should be excluded) // Tagged node for user1 (should be excluded)
{User: ptr.To(users[0]), IPv4: ap("100.64.0.5"), Hostname: "user1-tagged", Tags: []string{"tag:server"}}, {User: new(users[0]), IPv4: ap("100.64.0.5"), Hostname: "user1-tagged", Tags: []string{"tag:server"}},
} }
policy := &Policy{ policy := &Policy{
@@ -1470,10 +1469,10 @@ func TestSSHWithAutogroupSelfAndSpecificUser(t *testing.T) {
} }
nodes := types.Nodes{ nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")}, {User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")}, {User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")}, {User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")}, {User: new(users[1]), IPv4: ap("100.64.0.4")},
} }
policy := &Policy{ policy := &Policy{
@@ -1526,11 +1525,11 @@ func TestSSHWithAutogroupSelfAndGroup(t *testing.T) {
} }
nodes := types.Nodes{ nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")}, {User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")}, {User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")}, {User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")}, {User: new(users[1]), IPv4: ap("100.64.0.4")},
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")}, {User: new(users[2]), IPv4: ap("100.64.0.5")},
} }
policy := &Policy{ policy := &Policy{
@@ -1585,10 +1584,10 @@ func TestSSHWithAutogroupSelfExcludesTaggedDevices(t *testing.T) {
} }
nodes := types.Nodes{ nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"}, {User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"}, {User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}}, {User: new(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}}, {User: new(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}},
} }
policy := &Policy{ policy := &Policy{
@@ -1647,10 +1646,10 @@ func TestSSHWithAutogroupSelfAndMixedDestinations(t *testing.T) {
} }
nodes := types.Nodes{ nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"}, {User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"}, {User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"}, {User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}}, {User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}},
} }
policy := &Policy{ policy := &Policy{
@@ -1722,11 +1721,11 @@ func TestAutogroupSelfWithNonExistentUserInGroup(t *testing.T) {
nodes := types.Nodes{ nodes := types.Nodes{
// superadmin's device // superadmin's device
{ID: 1, User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "superadmin-device"}, {ID: 1, User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "superadmin-device"},
// admin's device // admin's device
{ID: 2, User: ptr.To(users[1]), IPv4: ap("100.64.0.2"), Hostname: "admin-device"}, {ID: 2, User: new(users[1]), IPv4: ap("100.64.0.2"), Hostname: "admin-device"},
// direction's device // direction's device
{ID: 3, User: ptr.To(users[2]), IPv4: ap("100.64.0.3"), Hostname: "direction-device"}, {ID: 3, User: new(users[2]), IPv4: ap("100.64.0.3"), Hostname: "direction-device"},
// tagged servers // tagged servers
{ID: 4, IPv4: ap("100.64.0.10"), Hostname: "common-server", Tags: []string{"tag:common"}}, {ID: 4, IPv4: ap("100.64.0.10"), Hostname: "common-server", Tags: []string{"tag:common"}},
{ID: 5, IPv4: ap("100.64.0.11"), Hostname: "tech-server", Tags: []string{"tag:tech"}}, {ID: 5, IPv4: ap("100.64.0.11"), Hostname: "tech-server", Tags: []string{"tag:tech"}},

View File

@@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
func node(name, ipv4, ipv6 string, user types.User) *types.Node { func node(name, ipv4, ipv6 string, user types.User) *types.Node {
@@ -20,8 +19,8 @@ func node(name, ipv4, ipv6 string, user types.User) *types.Node {
Hostname: name, Hostname: name,
IPv4: ap(ipv4), IPv4: ap(ipv4),
IPv6: ap(ipv6), IPv6: ap(ipv6),
User: ptr.To(user), User: new(user),
UserID: ptr.To(user.ID), UserID: new(user.ID),
} }
} }
@@ -463,8 +462,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
Hostname: "test-1-device", Hostname: "test-1-device",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -474,8 +473,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
Hostname: "test-2-router", Hostname: "test-2-router",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
Tags: []string{"tag:node-router"}, Tags: []string{"tag:node-router"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -543,8 +542,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
Hostname: "test-1-device", Hostname: "test-1-device",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -553,8 +552,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
Hostname: "test-2-device", Hostname: "test-2-device",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -653,8 +652,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user1-node", Hostname: "user1-node",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
Tags: []string{"tag:web", "tag:internal"}, Tags: []string{"tag:web", "tag:internal"},
} }
@@ -664,8 +663,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user2-node", Hostname: "user2-node",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
} }
initialNodes := types.Nodes{user1Node, user2Node} initialNodes := types.Nodes{user1Node, user2Node}
@@ -692,8 +691,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user1-node", Hostname: "user1-node",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
Tags: []string{"tag:internal"}, // tag:web removed! Tags: []string{"tag:internal"}, // tag:web removed!
} }
@@ -755,8 +754,8 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
Hostname: "admin-device", Hostname: "admin-device",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -766,8 +765,8 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
Hostname: "user1-server", Hostname: "user1-server",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -838,8 +837,8 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
Hostname: "device-a", Hostname: "device-a",
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -849,8 +848,8 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
Hostname: "device-b", Hostname: "device-b",
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
Tags: []string{"tag:web"}, Tags: []string{"tag:web"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -928,8 +927,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
superadminDevice := &types.Node{ superadminDevice := &types.Node{
ID: 1, ID: 1,
Hostname: "superadmin-laptop", Hostname: "superadmin-laptop",
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -937,8 +936,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
adminDevice := &types.Node{ adminDevice := &types.Node{
ID: 2, ID: 2,
Hostname: "admin-laptop", Hostname: "admin-laptop",
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -946,8 +945,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
directionDevice := &types.Node{ directionDevice := &types.Node{
ID: 3, ID: 3,
Hostname: "direction-laptop", Hostname: "direction-laptop",
User: ptr.To(users[2]), User: new(users[2]),
UserID: ptr.To(users[2].ID), UserID: new(users[2].ID),
IPv4: ap("100.64.0.3"), IPv4: ap("100.64.0.3"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -955,8 +954,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
commonServer := &types.Node{ commonServer := &types.Node{
ID: 4, ID: 4,
Hostname: "common-server", Hostname: "common-server",
User: ptr.To(users[3]), User: new(users[3]),
UserID: ptr.To(users[3].ID), UserID: new(users[3].ID),
IPv4: ap("100.64.0.4"), IPv4: ap("100.64.0.4"),
Tags: []string{"tag:common"}, Tags: []string{"tag:common"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
@@ -965,8 +964,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
techServer := &types.Node{ techServer := &types.Node{
ID: 5, ID: 5,
Hostname: "tech-server", Hostname: "tech-server",
User: ptr.To(users[3]), User: new(users[3]),
UserID: ptr.To(users[3].ID), UserID: new(users[3].ID),
IPv4: ap("100.64.0.5"), IPv4: ap("100.64.0.5"),
Tags: []string{"tag:tech"}, Tags: []string{"tag:tech"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
@@ -975,8 +974,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
privilegedServer := &types.Node{ privilegedServer := &types.Node{
ID: 6, ID: 6,
Hostname: "privileged-server", Hostname: "privileged-server",
User: ptr.To(users[3]), User: new(users[3]),
UserID: ptr.To(users[3].ID), UserID: new(users[3].ID),
IPv4: ap("100.64.0.6"), IPv4: ap("100.64.0.6"),
Tags: []string{"tag:privileged"}, Tags: []string{"tag:privileged"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
@@ -1088,8 +1087,8 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
adminDevice := &types.Node{ adminDevice := &types.Node{
ID: 1, ID: 1,
Hostname: "admin-laptop", Hostname: "admin-laptop",
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -1099,8 +1098,8 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
taggedServer := &types.Node{ taggedServer := &types.Node{
ID: 2, ID: 2,
Hostname: "server", Hostname: "server",
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
@@ -1157,8 +1156,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
adminLaptop := &types.Node{ adminLaptop := &types.Node{
ID: 1, ID: 1,
Hostname: "admin-laptop", Hostname: "admin-laptop",
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -1166,8 +1165,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
adminPhone := &types.Node{ adminPhone := &types.Node{
ID: 2, ID: 2,
Hostname: "admin-phone", Hostname: "admin-phone",
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@@ -1176,8 +1175,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
webServer := &types.Node{ webServer := &types.Node{
ID: 3, ID: 3,
Hostname: "web-server", Hostname: "web-server",
User: ptr.To(users[1]), User: new(users[1]),
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
IPv4: ap("100.64.0.3"), IPv4: ap("100.64.0.3"),
Tags: []string{"tag:web"}, Tags: []string{"tag:web"},
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
@@ -1252,8 +1251,8 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
node1 := &types.Node{ node1 := &types.Node{
ID: 1, ID: 1,
Hostname: "node1", Hostname: "node1",
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"), IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"), IPv6: ap("fd7a:115c:a1e0::1"),
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
@@ -1263,8 +1262,8 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
node2 := &types.Node{ node2 := &types.Node{
ID: 2, ID: 2,
Hostname: "node2", Hostname: "node2",
User: ptr.To(users[0]), User: new(users[0]),
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
IPv4: ap("100.64.0.2"), IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"), IPv6: ap("fd7a:115c:a1e0::2"),
Tags: []string{"tag:admin"}, Tags: []string{"tag:admin"},

View File

@@ -16,7 +16,6 @@ import (
"go4.org/netipx" "go4.org/netipx"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
"tailscale.com/types/views" "tailscale.com/types/views"
"tailscale.com/util/multierr" "tailscale.com/util/multierr"
"tailscale.com/util/slicesx" "tailscale.com/util/slicesx"
@@ -762,17 +761,17 @@ func parseAlias(vs string) (Alias, error) {
case isWildcard(vs): case isWildcard(vs):
return Wildcard, nil return Wildcard, nil
case isUser(vs): case isUser(vs):
return ptr.To(Username(vs)), nil return new(Username(vs)), nil
case isGroup(vs): case isGroup(vs):
return ptr.To(Group(vs)), nil return new(Group(vs)), nil
case isTag(vs): case isTag(vs):
return ptr.To(Tag(vs)), nil return new(Tag(vs)), nil
case isAutoGroup(vs): case isAutoGroup(vs):
return ptr.To(AutoGroup(vs)), nil return new(AutoGroup(vs)), nil
} }
if isHost(vs) { if isHost(vs) {
return ptr.To(Host(vs)), nil return new(Host(vs)), nil
} }
return nil, fmt.Errorf("%w: %q", ErrInvalidAlias, vs) return nil, fmt.Errorf("%w: %q", ErrInvalidAlias, vs)
@@ -933,11 +932,11 @@ func (aa AutoApprovers) MarshalJSON() ([]byte, error) {
func parseAutoApprover(s string) (AutoApprover, error) { func parseAutoApprover(s string) (AutoApprover, error) {
switch { switch {
case isUser(s): case isUser(s):
return ptr.To(Username(s)), nil return new(Username(s)), nil
case isGroup(s): case isGroup(s):
return ptr.To(Group(s)), nil return new(Group(s)), nil
case isTag(s): case isTag(s):
return ptr.To(Tag(s)), nil return new(Tag(s)), nil
} }
return nil, fmt.Errorf("%w: %q", ErrInvalidAutoApprover, s) return nil, fmt.Errorf("%w: %q", ErrInvalidAutoApprover, s)
@@ -1027,11 +1026,11 @@ func (o Owners) MarshalJSON() ([]byte, error) {
func parseOwner(s string) (Owner, error) { func parseOwner(s string) (Owner, error) {
switch { switch {
case isUser(s): case isUser(s):
return ptr.To(Username(s)), nil return new(Username(s)), nil
case isGroup(s): case isGroup(s):
return ptr.To(Group(s)), nil return new(Group(s)), nil
case isTag(s): case isTag(s):
return ptr.To(Tag(s)), nil return new(Tag(s)), nil
} }
return nil, fmt.Errorf("%w: %q", ErrInvalidOwner, s) return nil, fmt.Errorf("%w: %q", ErrInvalidOwner, s)
@@ -2299,8 +2298,7 @@ func unmarshalPolicy(b []byte) (*Policy, error) {
ast.Standardize() ast.Standardize()
if err = json.Unmarshal(ast.Pack(), &policy, policyJSONOpts...); err != nil { //nolint:noinlineerr if err = json.Unmarshal(ast.Pack(), &policy, policyJSONOpts...); err != nil { //nolint:noinlineerr
var serr *json.SemanticError if serr, ok := errors.AsType[*json.SemanticError](err); ok && errors.Is(serr.Err, json.ErrUnknownName) {
if errors.As(err, &serr) && errors.Is(serr.Err, json.ErrUnknownName) {
ptr := serr.JSONPointer ptr := serr.JSONPointer
name := ptr.LastToken() name := ptr.LastToken()

View File

@@ -19,7 +19,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
// TestUnmarshalPolicy tests the unmarshalling of JSON into Policy objects and the marshalling // TestUnmarshalPolicy tests the unmarshalling of JSON into Policy objects and the marshalling
@@ -53,11 +52,11 @@ func TestMarshalJSON(t *testing.T) {
Action: "accept", Action: "accept",
Protocol: "tcp", Protocol: "tcp",
Sources: Aliases{ Sources: Aliases{
ptr.To(Username("user@example.com")), new(Username("user@example.com")),
}, },
Destinations: []AliasWithPorts{ Destinations: []AliasWithPorts{
{ {
Alias: ptr.To(Username("other@example.com")), Alias: new(Username("other@example.com")),
Ports: []tailcfg.PortRange{{First: 80, Last: 80}}, Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
}, },
}, },
@@ -254,11 +253,11 @@ func TestUnmarshalPolicy(t *testing.T) {
Action: "accept", Action: "accept",
Protocol: "tcp", Protocol: "tcp",
Sources: Aliases{ Sources: Aliases{
ptr.To(Username("testuser@headscale.net")), new(Username("testuser@headscale.net")),
}, },
Destinations: []AliasWithPorts{ Destinations: []AliasWithPorts{
{ {
Alias: ptr.To(Username("otheruser@headscale.net")), Alias: new(Username("otheruser@headscale.net")),
Ports: []tailcfg.PortRange{{First: 80, Last: 80}}, Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
}, },
}, },
@@ -547,7 +546,7 @@ func TestUnmarshalPolicy(t *testing.T) {
}, },
Destinations: []AliasWithPorts{ Destinations: []AliasWithPorts{
{ {
Alias: ptr.To(AutoGroup("autogroup:internet")), Alias: new(AutoGroup("autogroup:internet")),
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny}, Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
}, },
}, },
@@ -684,8 +683,8 @@ func TestUnmarshalPolicy(t *testing.T) {
`, `,
want: &Policy{ want: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:web"): Owners{ptr.To(Username("admin@example.com"))}, Tag("tag:web"): Owners{new(Username("admin@example.com"))},
Tag("tag:server"): Owners{ptr.To(Username("admin@example.com"))}, Tag("tag:server"): Owners{new(Username("admin@example.com"))},
}, },
SSHs: []SSH{ SSHs: []SSH{
{ {
@@ -1157,7 +1156,7 @@ func TestUnmarshalPolicy(t *testing.T) {
}, },
Destinations: []AliasWithPorts{ Destinations: []AliasWithPorts{
{ {
Alias: ptr.To(AutoGroup("autogroup:internet")), Alias: new(AutoGroup("autogroup:internet")),
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny}, Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
}, },
}, },
@@ -1494,7 +1493,7 @@ func TestUnmarshalPolicy(t *testing.T) {
want: &Policy{ want: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:bigbrother"): {}, Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))}, Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
}, },
ACLs: []ACL{ ACLs: []ACL{
{ {
@@ -1505,7 +1504,7 @@ func TestUnmarshalPolicy(t *testing.T) {
}, },
Destinations: []AliasWithPorts{ Destinations: []AliasWithPorts{
{ {
Alias: ptr.To(Tag("tag:smallbrother")), Alias: new(Tag("tag:smallbrother")),
Ports: []tailcfg.PortRange{{First: 9000, Last: 9000}}, Ports: []tailcfg.PortRange{{First: 9000, Last: 9000}},
}, },
}, },
@@ -1829,14 +1828,14 @@ func TestUnmarshalPolicy(t *testing.T) {
} }
} }
func gp(s string) *Group { return ptr.To(Group(s)) } func gp(s string) *Group { return new(Group(s)) }
func up(s string) *Username { return ptr.To(Username(s)) } func up(s string) *Username { return new(Username(s)) }
func hp(s string) *Host { return ptr.To(Host(s)) } func hp(s string) *Host { return new(Host(s)) }
func tp(s string) *Tag { return ptr.To(Tag(s)) } func tp(s string) *Tag { return new(Tag(s)) }
func agp(s string) *AutoGroup { return ptr.To(AutoGroup(s)) } func agp(s string) *AutoGroup { return new(AutoGroup(s)) }
func mp(pref string) netip.Prefix { return netip.MustParsePrefix(pref) } func mp(pref string) netip.Prefix { return netip.MustParsePrefix(pref) }
func ap(addr string) *netip.Addr { return ptr.To(netip.MustParseAddr(addr)) } func ap(addr string) *netip.Addr { return new(netip.MustParseAddr(addr)) }
func pp(pref string) *Prefix { return ptr.To(Prefix(mp(pref))) } func pp(pref string) *Prefix { return new(Prefix(mp(pref))) }
func p(pref string) Prefix { return Prefix(mp(pref)) } func p(pref string) Prefix { return Prefix(mp(pref)) }
func TestResolvePolicy(t *testing.T) { func TestResolvePolicy(t *testing.T) {
@@ -1882,31 +1881,31 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "username", name: "username",
toResolve: ptr.To(Username("testuser@")), toResolve: new(Username("testuser@")),
nodes: types.Nodes{ nodes: types.Nodes{
// Not matching other user // Not matching other user
{ {
User: ptr.To(notme), User: new(notme),
IPv4: ap("100.100.101.1"), IPv4: ap("100.100.101.1"),
}, },
// Not matching forced tags // Not matching forced tags
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:anything"}, Tags: []string{"tag:anything"},
IPv4: ap("100.100.101.2"), IPv4: ap("100.100.101.2"),
}, },
// not matching because it's tagged (tags copied from AuthKey) // not matching because it's tagged (tags copied from AuthKey)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"alsotagged"}, Tags: []string{"alsotagged"},
IPv4: ap("100.100.101.3"), IPv4: ap("100.100.101.3"),
}, },
{ {
User: ptr.To(testuser), User: new(testuser),
IPv4: ap("100.100.101.103"), IPv4: ap("100.100.101.103"),
}, },
{ {
User: ptr.To(testuser), User: new(testuser),
IPv4: ap("100.100.101.104"), IPv4: ap("100.100.101.104"),
}, },
}, },
@@ -1914,31 +1913,31 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "group", name: "group",
toResolve: ptr.To(Group("group:testgroup")), toResolve: new(Group("group:testgroup")),
nodes: types.Nodes{ nodes: types.Nodes{
// Not matching other user // Not matching other user
{ {
User: ptr.To(notme), User: new(notme),
IPv4: ap("100.100.101.4"), IPv4: ap("100.100.101.4"),
}, },
// Not matching forced tags // Not matching forced tags
{ {
User: ptr.To(groupuser), User: new(groupuser),
Tags: []string{"tag:anything"}, Tags: []string{"tag:anything"},
IPv4: ap("100.100.101.5"), IPv4: ap("100.100.101.5"),
}, },
// not matching because it's tagged (tags copied from AuthKey) // not matching because it's tagged (tags copied from AuthKey)
{ {
User: ptr.To(groupuser), User: new(groupuser),
Tags: []string{"tag:alsotagged"}, Tags: []string{"tag:alsotagged"},
IPv4: ap("100.100.101.6"), IPv4: ap("100.100.101.6"),
}, },
{ {
User: ptr.To(groupuser), User: new(groupuser),
IPv4: ap("100.100.101.203"), IPv4: ap("100.100.101.203"),
}, },
{ {
User: ptr.To(groupuser), User: new(groupuser),
IPv4: ap("100.100.101.204"), IPv4: ap("100.100.101.204"),
}, },
}, },
@@ -1956,7 +1955,7 @@ func TestResolvePolicy(t *testing.T) {
nodes: types.Nodes{ nodes: types.Nodes{
// Not matching other user // Not matching other user
{ {
User: ptr.To(notme), User: new(notme),
IPv4: ap("100.100.101.9"), IPv4: ap("100.100.101.9"),
}, },
// Not matching forced tags // Not matching forced tags
@@ -1992,7 +1991,7 @@ func TestResolvePolicy(t *testing.T) {
pol: &Policy{ pol: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:bigbrother"): {}, Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))}, Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
}, },
}, },
nodes: types.Nodes{ nodes: types.Nodes{
@@ -2015,7 +2014,7 @@ func TestResolvePolicy(t *testing.T) {
pol: &Policy{ pol: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:bigbrother"): {}, Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))}, Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
}, },
}, },
nodes: types.Nodes{ nodes: types.Nodes{
@@ -2050,14 +2049,14 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "multiple-groups", name: "multiple-groups",
toResolve: ptr.To(Group("group:testgroup")), toResolve: new(Group("group:testgroup")),
nodes: types.Nodes{ nodes: types.Nodes{
{ {
User: ptr.To(groupuser1), User: new(groupuser1),
IPv4: ap("100.100.101.203"), IPv4: ap("100.100.101.203"),
}, },
{ {
User: ptr.To(groupuser2), User: new(groupuser2),
IPv4: ap("100.100.101.204"), IPv4: ap("100.100.101.204"),
}, },
}, },
@@ -2075,10 +2074,10 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "invalid-username", name: "invalid-username",
toResolve: ptr.To(Username("invaliduser@")), toResolve: new(Username("invaliduser@")),
nodes: types.Nodes{ nodes: types.Nodes{
{ {
User: ptr.To(testuser), User: new(testuser),
IPv4: ap("100.100.101.103"), IPv4: ap("100.100.101.103"),
}, },
}, },
@@ -2106,47 +2105,47 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "autogroup-member-comprehensive", name: "autogroup-member-comprehensive",
toResolve: ptr.To(AutoGroupMember), toResolve: new(AutoGroupMember),
nodes: types.Nodes{ nodes: types.Nodes{
// Node with no tags (should be included - is a member) // Node with no tags (should be included - is a member)
{ {
User: ptr.To(testuser), User: new(testuser),
IPv4: ap("100.100.101.1"), IPv4: ap("100.100.101.1"),
}, },
// Node with single tag (should be excluded - tagged nodes are not members) // Node with single tag (should be excluded - tagged nodes are not members)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test"}, Tags: []string{"tag:test"},
IPv4: ap("100.100.101.2"), IPv4: ap("100.100.101.2"),
}, },
// Node with multiple tags, all defined in policy (should be excluded) // Node with multiple tags, all defined in policy (should be excluded)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test", "tag:other"}, Tags: []string{"tag:test", "tag:other"},
IPv4: ap("100.100.101.3"), IPv4: ap("100.100.101.3"),
}, },
// Node with tag not defined in policy (should be excluded - still tagged) // Node with tag not defined in policy (should be excluded - still tagged)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:undefined"}, Tags: []string{"tag:undefined"},
IPv4: ap("100.100.101.4"), IPv4: ap("100.100.101.4"),
}, },
// Node with mixed tags - some defined, some not (should be excluded) // Node with mixed tags - some defined, some not (should be excluded)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test", "tag:undefined"}, Tags: []string{"tag:test", "tag:undefined"},
IPv4: ap("100.100.101.5"), IPv4: ap("100.100.101.5"),
}, },
// Another untagged node from different user (should be included) // Another untagged node from different user (should be included)
{ {
User: ptr.To(testuser2), User: new(testuser2),
IPv4: ap("100.100.101.6"), IPv4: ap("100.100.101.6"),
}, },
}, },
pol: &Policy{ pol: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))}, Tag("tag:test"): Owners{new(Username("testuser@"))},
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))}, Tag("tag:other"): Owners{new(Username("testuser@"))},
}, },
}, },
want: []netip.Prefix{ want: []netip.Prefix{
@@ -2156,54 +2155,54 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "autogroup-tagged", name: "autogroup-tagged",
toResolve: ptr.To(AutoGroupTagged), toResolve: new(AutoGroupTagged),
nodes: types.Nodes{ nodes: types.Nodes{
// Node with no tags (should be excluded - not tagged) // Node with no tags (should be excluded - not tagged)
{ {
User: ptr.To(testuser), User: new(testuser),
IPv4: ap("100.100.101.1"), IPv4: ap("100.100.101.1"),
}, },
// Node with single tag defined in policy (should be included) // Node with single tag defined in policy (should be included)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test"}, Tags: []string{"tag:test"},
IPv4: ap("100.100.101.2"), IPv4: ap("100.100.101.2"),
}, },
// Node with multiple tags, all defined in policy (should be included) // Node with multiple tags, all defined in policy (should be included)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test", "tag:other"}, Tags: []string{"tag:test", "tag:other"},
IPv4: ap("100.100.101.3"), IPv4: ap("100.100.101.3"),
}, },
// Node with tag not defined in policy (should be included - still tagged) // Node with tag not defined in policy (should be included - still tagged)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:undefined"}, Tags: []string{"tag:undefined"},
IPv4: ap("100.100.101.4"), IPv4: ap("100.100.101.4"),
}, },
// Node with mixed tags - some defined, some not (should be included) // Node with mixed tags - some defined, some not (should be included)
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test", "tag:undefined"}, Tags: []string{"tag:test", "tag:undefined"},
IPv4: ap("100.100.101.5"), IPv4: ap("100.100.101.5"),
}, },
// Another untagged node from different user (should be excluded) // Another untagged node from different user (should be excluded)
{ {
User: ptr.To(testuser2), User: new(testuser2),
IPv4: ap("100.100.101.6"), IPv4: ap("100.100.101.6"),
}, },
// Tagged node from different user (should be included) // Tagged node from different user (should be included)
{ {
User: ptr.To(testuser2), User: new(testuser2),
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
IPv4: ap("100.100.101.7"), IPv4: ap("100.100.101.7"),
}, },
}, },
pol: &Policy{ pol: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))}, Tag("tag:test"): Owners{new(Username("testuser@"))},
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))}, Tag("tag:other"): Owners{new(Username("testuser@"))},
Tag("tag:server"): Owners{ptr.To(Username("testuser2@"))}, Tag("tag:server"): Owners{new(Username("testuser2@"))},
}, },
}, },
want: []netip.Prefix{ want: []netip.Prefix{
@@ -2214,37 +2213,37 @@ func TestResolvePolicy(t *testing.T) {
}, },
{ {
name: "autogroup-self", name: "autogroup-self",
toResolve: ptr.To(AutoGroupSelf), toResolve: new(AutoGroupSelf),
nodes: types.Nodes{ nodes: types.Nodes{
{ {
User: ptr.To(testuser), User: new(testuser),
IPv4: ap("100.100.101.1"), IPv4: ap("100.100.101.1"),
}, },
{ {
User: ptr.To(testuser2), User: new(testuser2),
IPv4: ap("100.100.101.2"), IPv4: ap("100.100.101.2"),
}, },
{ {
User: ptr.To(testuser), User: new(testuser),
Tags: []string{"tag:test"}, Tags: []string{"tag:test"},
IPv4: ap("100.100.101.3"), IPv4: ap("100.100.101.3"),
}, },
{ {
User: ptr.To(testuser2), User: new(testuser2),
Tags: []string{"tag:test"}, Tags: []string{"tag:test"},
IPv4: ap("100.100.101.4"), IPv4: ap("100.100.101.4"),
}, },
}, },
pol: &Policy{ pol: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))}, Tag("tag:test"): Owners{new(Username("testuser@"))},
}, },
}, },
wantErr: "autogroup:self requires per-node resolution", wantErr: "autogroup:self requires per-node resolution",
}, },
{ {
name: "autogroup-invalid", name: "autogroup-invalid",
toResolve: ptr.To(AutoGroup("autogroup:invalid")), toResolve: new(AutoGroup("autogroup:invalid")),
wantErr: "unknown autogroup", wantErr: "unknown autogroup",
}, },
} }
@@ -2323,7 +2322,7 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))}, mp("10.0.0.0/24"): {new(Username("user1@"))},
}, },
}, },
}, },
@@ -2338,8 +2337,8 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))}, mp("10.0.0.0/24"): {new(Username("user1@"))},
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))}, mp("10.0.1.0/24"): {new(Username("user2@"))},
}, },
}, },
}, },
@@ -2354,7 +2353,7 @@ func TestResolveAutoApprovers(t *testing.T) {
name: "exit-node", name: "exit-node",
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Username("user1@"))}, ExitNode: AutoApprovers{new(Username("user1@"))},
}, },
}, },
want: map[netip.Prefix]*netipx.IPSet{}, want: map[netip.Prefix]*netipx.IPSet{},
@@ -2369,7 +2368,7 @@ func TestResolveAutoApprovers(t *testing.T) {
}, },
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))}, mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
}, },
}, },
}, },
@@ -2384,20 +2383,20 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
"tag:testtag": Owners{ "tag:testtag": Owners{
ptr.To(Username("user1@")), new(Username("user1@")),
ptr.To(Username("user2@")), new(Username("user2@")),
}, },
"tag:exittest": Owners{ "tag:exittest": Owners{
ptr.To(Group("group:exitgroup")), new(Group("group:exitgroup")),
}, },
}, },
Groups: Groups{ Groups: Groups{
"group:exitgroup": Usernames{"user2@"}, "group:exitgroup": Usernames{"user2@"},
}, },
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Tag("tag:exittest"))}, ExitNode: AutoApprovers{new(Tag("tag:exittest"))},
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.1.0/24"): {ptr.To(Tag("tag:testtag"))}, mp("10.0.1.0/24"): {new(Tag("tag:testtag"))},
}, },
}, },
}, },
@@ -2415,10 +2414,10 @@ func TestResolveAutoApprovers(t *testing.T) {
}, },
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))}, mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))}, mp("10.0.1.0/24"): {new(Username("user3@"))},
}, },
ExitNode: AutoApprovers{ptr.To(Username("user1@"))}, ExitNode: AutoApprovers{new(Username("user1@"))},
}, },
}, },
want: map[netip.Prefix]*netipx.IPSet{ want: map[netip.Prefix]*netipx.IPSet{
@@ -2639,7 +2638,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))}, mp("10.0.0.0/24"): {new(Username("user1@"))},
}, },
}, },
}, },
@@ -2652,8 +2651,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))}, mp("10.0.0.0/24"): {new(Username("user1@"))},
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))}, mp("10.0.1.0/24"): {new(Username("user2@"))},
}, },
}, },
}, },
@@ -2665,7 +2664,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
name: "exit-node-approval", name: "exit-node-approval",
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Username("user1@"))}, ExitNode: AutoApprovers{new(Username("user1@"))},
}, },
}, },
node: nodes[0], node: nodes[0],
@@ -2680,7 +2679,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
}, },
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))}, mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
}, },
}, },
}, },
@@ -2696,10 +2695,10 @@ func TestNodeCanApproveRoute(t *testing.T) {
}, },
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))}, mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))}, mp("10.0.1.0/24"): {new(Username("user3@"))},
}, },
ExitNode: AutoApprovers{ptr.To(Username("user1@"))}, ExitNode: AutoApprovers{new(Username("user1@"))},
}, },
}, },
node: nodes[0], node: nodes[0],
@@ -2711,7 +2710,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{ policy: &Policy{
AutoApprovers: AutoApproverPolicy{ AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{ Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user2@"))}, mp("10.0.0.0/24"): {new(Username("user2@"))},
}, },
}, },
}, },
@@ -2769,7 +2768,7 @@ func TestResolveTagOwners(t *testing.T) {
name: "single-tag-owner", name: "single-tag-owner",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))}, Tag("tag:test"): Owners{new(Username("user1@"))},
}, },
}, },
want: map[Tag]*netipx.IPSet{ want: map[Tag]*netipx.IPSet{
@@ -2781,7 +2780,7 @@ func TestResolveTagOwners(t *testing.T) {
name: "multiple-tag-owners", name: "multiple-tag-owners",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))}, Tag("tag:test"): Owners{new(Username("user1@")), new(Username("user2@"))},
}, },
}, },
want: map[Tag]*netipx.IPSet{ want: map[Tag]*netipx.IPSet{
@@ -2796,7 +2795,7 @@ func TestResolveTagOwners(t *testing.T) {
"group:testgroup": Usernames{"user1@", "user2@"}, "group:testgroup": Usernames{"user1@", "user2@"},
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))}, Tag("tag:test"): Owners{new(Group("group:testgroup"))},
}, },
}, },
want: map[Tag]*netipx.IPSet{ want: map[Tag]*netipx.IPSet{
@@ -2808,8 +2807,8 @@ func TestResolveTagOwners(t *testing.T) {
name: "tag-owns-tag", name: "tag-owns-tag",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Username("user1@"))}, Tag("tag:bigbrother"): Owners{new(Username("user1@"))},
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))}, Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
}, },
}, },
want: map[Tag]*netipx.IPSet{ want: map[Tag]*netipx.IPSet{
@@ -2871,7 +2870,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "single-tag-owner", name: "single-tag-owner",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))}, Tag("tag:test"): Owners{new(Username("user1@"))},
}, },
}, },
node: nodes[0], node: nodes[0],
@@ -2882,7 +2881,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "multiple-tag-owners", name: "multiple-tag-owners",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))}, Tag("tag:test"): Owners{new(Username("user1@")), new(Username("user2@"))},
}, },
}, },
node: nodes[1], node: nodes[1],
@@ -2896,7 +2895,7 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:testgroup": Usernames{"user1@", "user2@"}, "group:testgroup": Usernames{"user1@", "user2@"},
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))}, Tag("tag:test"): Owners{new(Group("group:testgroup"))},
}, },
}, },
node: nodes[1], node: nodes[1],
@@ -2910,7 +2909,7 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:testgroup": Usernames{"invalid"}, "group:testgroup": Usernames{"invalid"},
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))}, Tag("tag:test"): Owners{new(Group("group:testgroup"))},
}, },
}, },
node: nodes[0], node: nodes[0],
@@ -2922,7 +2921,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-cannot-have-tag", name: "node-cannot-have-tag",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user2@"))}, Tag("tag:test"): Owners{new(Username("user2@"))},
}, },
}, },
node: nodes[0], node: nodes[0],
@@ -2933,7 +2932,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-with-unauthorized-tag-different-user", name: "node-with-unauthorized-tag-different-user",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))}, Tag("tag:prod"): Owners{new(Username("user1@"))},
}, },
}, },
node: nodes[2], // user3's node node: nodes[2], // user3's node
@@ -2944,8 +2943,8 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-with-multiple-tags-one-unauthorized", name: "node-with-multiple-tags-one-unauthorized",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:web"): Owners{ptr.To(Username("user1@"))}, Tag("tag:web"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))}, Tag("tag:database"): Owners{new(Username("user2@"))},
}, },
}, },
node: nodes[0], // user1's node node: nodes[0], // user1's node
@@ -2965,7 +2964,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "tag-not-in-tagowners", name: "tag-not-in-tagowners",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))}, Tag("tag:prod"): Owners{new(Username("user1@"))},
}, },
}, },
node: nodes[0], node: nodes[0],
@@ -2978,13 +2977,13 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-user-owns-tag", name: "node-without-ip-user-owns-tag",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))}, Tag("tag:test"): Owners{new(Username("user1@"))},
}, },
}, },
node: &types.Node{ node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration // No IPv4 or IPv6 - simulates new node registration
User: &users[0], User: &users[0],
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
}, },
tag: "tag:test", tag: "tag:test",
want: true, // Should succeed via user-based fallback want: true, // Should succeed via user-based fallback
@@ -2993,13 +2992,13 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-user-does-not-own-tag", name: "node-without-ip-user-does-not-own-tag",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user2@"))}, Tag("tag:test"): Owners{new(Username("user2@"))},
}, },
}, },
node: &types.Node{ node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration // No IPv4 or IPv6 - simulates new node registration
User: &users[0], // user1, but tag owned by user2 User: &users[0], // user1, but tag owned by user2
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
}, },
tag: "tag:test", tag: "tag:test",
want: false, // user1 does not own tag:test want: false, // user1 does not own tag:test
@@ -3011,13 +3010,13 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:admins": Usernames{"user1@", "user2@"}, "group:admins": Usernames{"user1@", "user2@"},
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))}, Tag("tag:admin"): Owners{new(Group("group:admins"))},
}, },
}, },
node: &types.Node{ node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration // No IPv4 or IPv6 - simulates new node registration
User: &users[1], // user2 is in group:admins User: &users[1], // user2 is in group:admins
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
}, },
tag: "tag:admin", tag: "tag:admin",
want: true, // Should succeed via group membership want: true, // Should succeed via group membership
@@ -3029,13 +3028,13 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:admins": Usernames{"user1@"}, "group:admins": Usernames{"user1@"},
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))}, Tag("tag:admin"): Owners{new(Group("group:admins"))},
}, },
}, },
node: &types.Node{ node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration // No IPv4 or IPv6 - simulates new node registration
User: &users[1], // user2 is NOT in group:admins User: &users[1], // user2 is NOT in group:admins
UserID: ptr.To(users[1].ID), UserID: new(users[1].ID),
}, },
tag: "tag:admin", tag: "tag:admin",
want: false, // user2 is not in group:admins want: false, // user2 is not in group:admins
@@ -3044,7 +3043,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-no-user", name: "node-without-ip-no-user",
policy: &Policy{ policy: &Policy{
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))}, Tag("tag:test"): Owners{new(Username("user1@"))},
}, },
}, },
node: &types.Node{ node: &types.Node{
@@ -3061,14 +3060,14 @@ func TestNodeCanHaveTag(t *testing.T) {
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:server"): Owners{ Tag("tag:server"): Owners{
ptr.To(Username("user1@")), new(Username("user1@")),
ptr.To(Group("group:ops")), new(Group("group:ops")),
}, },
}, },
}, },
node: &types.Node{ node: &types.Node{
User: &users[0], // user1 directly owns the tag User: &users[0], // user1 directly owns the tag
UserID: ptr.To(users[0].ID), UserID: new(users[0].ID),
}, },
tag: "tag:server", tag: "tag:server",
want: true, want: true,
@@ -3081,14 +3080,14 @@ func TestNodeCanHaveTag(t *testing.T) {
}, },
TagOwners: TagOwners{ TagOwners: TagOwners{
Tag("tag:server"): Owners{ Tag("tag:server"): Owners{
ptr.To(Username("user1@")), new(Username("user1@")),
ptr.To(Group("group:ops")), new(Group("group:ops")),
}, },
}, },
}, },
node: &types.Node{ node: &types.Node{
User: &users[2], // user3 is in group:ops User: &users[2], // user3 is in group:ops
UserID: ptr.To(users[2].ID), UserID: new(users[2].ID),
}, },
tag: "tag:server", tag: "tag:server",
want: true, want: true,
@@ -3134,14 +3133,14 @@ func TestUserMatchesOwner(t *testing.T) {
name: "username-match", name: "username-match",
policy: &Policy{}, policy: &Policy{},
user: users[0], user: users[0],
owner: ptr.To(Username("user1@")), owner: new(Username("user1@")),
want: true, want: true,
}, },
{ {
name: "username-no-match", name: "username-no-match",
policy: &Policy{}, policy: &Policy{},
user: users[0], user: users[0],
owner: ptr.To(Username("user2@")), owner: new(Username("user2@")),
want: false, want: false,
}, },
{ {
@@ -3152,7 +3151,7 @@ func TestUserMatchesOwner(t *testing.T) {
}, },
}, },
user: users[1], // user2 is in group:admins user: users[1], // user2 is in group:admins
owner: ptr.To(Group("group:admins")), owner: new(Group("group:admins")),
want: true, want: true,
}, },
{ {
@@ -3163,7 +3162,7 @@ func TestUserMatchesOwner(t *testing.T) {
}, },
}, },
user: users[1], // user2 is NOT in group:admins user: users[1], // user2 is NOT in group:admins
owner: ptr.To(Group("group:admins")), owner: new(Group("group:admins")),
want: false, want: false,
}, },
{ {
@@ -3172,7 +3171,7 @@ func TestUserMatchesOwner(t *testing.T) {
Groups: Groups{}, Groups: Groups{},
}, },
user: users[0], user: users[0],
owner: ptr.To(Group("group:undefined")), owner: new(Group("group:undefined")),
want: false, want: false,
}, },
{ {
@@ -3517,20 +3516,20 @@ func TestFlattenTagOwners(t *testing.T) {
{ {
name: "tag-owns-tag", name: "tag-owns-tag",
input: TagOwners{ input: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))}, Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
}, },
want: TagOwners{ want: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:smallbrother"): Owners{new(Group("group:user1"))},
}, },
wantErr: "", wantErr: "",
}, },
{ {
name: "circular-reference", name: "circular-reference",
input: TagOwners{ input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))}, Tag("tag:a"): Owners{new(Tag("tag:b"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))}, Tag("tag:b"): Owners{new(Tag("tag:a"))},
}, },
want: nil, want: nil,
wantErr: "circular reference detected: tag:a -> tag:b", wantErr: "circular reference detected: tag:a -> tag:b",
@@ -3538,83 +3537,83 @@ func TestFlattenTagOwners(t *testing.T) {
{ {
name: "mixed-owners", name: "mixed-owners",
input: TagOwners{ input: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))}, Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
Tag("tag:y"): Owners{ptr.To(Username("user2@"))}, Tag("tag:y"): Owners{new(Username("user2@"))},
}, },
want: TagOwners{ want: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))}, Tag("tag:x"): Owners{new(Username("user1@")), new(Username("user2@"))},
Tag("tag:y"): Owners{ptr.To(Username("user2@"))}, Tag("tag:y"): Owners{new(Username("user2@"))},
}, },
wantErr: "", wantErr: "",
}, },
{ {
name: "mixed-dupe-owners", name: "mixed-dupe-owners",
input: TagOwners{ input: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))}, Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
Tag("tag:y"): Owners{ptr.To(Username("user1@"))}, Tag("tag:y"): Owners{new(Username("user1@"))},
}, },
want: TagOwners{ want: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@"))}, Tag("tag:x"): Owners{new(Username("user1@"))},
Tag("tag:y"): Owners{ptr.To(Username("user1@"))}, Tag("tag:y"): Owners{new(Username("user1@"))},
}, },
wantErr: "", wantErr: "",
}, },
{ {
name: "no-tag-owners", name: "no-tag-owners",
input: TagOwners{ input: TagOwners{
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))}, Tag("tag:solo"): Owners{new(Username("user1@"))},
}, },
want: TagOwners{ want: TagOwners{
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))}, Tag("tag:solo"): Owners{new(Username("user1@"))},
}, },
wantErr: "", wantErr: "",
}, },
{ {
name: "tag-long-owner-chain", name: "tag-long-owner-chain",
input: TagOwners{ input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:a"): Owners{new(Group("group:user1"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))}, Tag("tag:b"): Owners{new(Tag("tag:a"))},
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))}, Tag("tag:c"): Owners{new(Tag("tag:b"))},
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))}, Tag("tag:d"): Owners{new(Tag("tag:c"))},
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))}, Tag("tag:e"): Owners{new(Tag("tag:d"))},
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))}, Tag("tag:f"): Owners{new(Tag("tag:e"))},
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))}, Tag("tag:g"): Owners{new(Tag("tag:f"))},
}, },
want: TagOwners{ want: TagOwners{
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:a"): Owners{new(Group("group:user1"))},
Tag("tag:b"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:b"): Owners{new(Group("group:user1"))},
Tag("tag:c"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:c"): Owners{new(Group("group:user1"))},
Tag("tag:d"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:d"): Owners{new(Group("group:user1"))},
Tag("tag:e"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:e"): Owners{new(Group("group:user1"))},
Tag("tag:f"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:f"): Owners{new(Group("group:user1"))},
Tag("tag:g"): Owners{ptr.To(Group("group:user1"))}, Tag("tag:g"): Owners{new(Group("group:user1"))},
}, },
wantErr: "", wantErr: "",
}, },
{ {
name: "tag-long-circular-chain", name: "tag-long-circular-chain",
input: TagOwners{ input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:g"))}, Tag("tag:a"): Owners{new(Tag("tag:g"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))}, Tag("tag:b"): Owners{new(Tag("tag:a"))},
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))}, Tag("tag:c"): Owners{new(Tag("tag:b"))},
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))}, Tag("tag:d"): Owners{new(Tag("tag:c"))},
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))}, Tag("tag:e"): Owners{new(Tag("tag:d"))},
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))}, Tag("tag:f"): Owners{new(Tag("tag:e"))},
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))}, Tag("tag:g"): Owners{new(Tag("tag:f"))},
}, },
wantErr: "circular reference detected: tag:a -> tag:b -> tag:c -> tag:d -> tag:e -> tag:f -> tag:g", wantErr: "circular reference detected: tag:a -> tag:b -> tag:c -> tag:d -> tag:e -> tag:f -> tag:g",
}, },
{ {
name: "undefined-tag-reference", name: "undefined-tag-reference",
input: TagOwners{ input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:nonexistent"))}, Tag("tag:a"): Owners{new(Tag("tag:nonexistent"))},
}, },
wantErr: `tag "tag:a" references undefined tag "tag:nonexistent"`, wantErr: `tag "tag:a" references undefined tag "tag:nonexistent"`,
}, },
{ {
name: "tag-with-empty-owners-is-valid", name: "tag-with-empty-owners-is-valid",
input: TagOwners{ input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))}, Tag("tag:a"): Owners{new(Tag("tag:b"))},
Tag("tag:b"): Owners{}, // empty owners but exists Tag("tag:b"): Owners{}, // empty owners but exists
}, },
want: TagOwners{ want: TagOwners{

View File

@@ -240,7 +240,7 @@ func (pr *PrimaryRoutes) PrimaryRoutes(id types.NodeID) []netip.Prefix {
} }
} }
tsaddr.SortPrefixes(routes) slices.SortFunc(routes, netip.Prefix.Compare)
return routes return routes
} }
@@ -300,7 +300,7 @@ func (pr *PrimaryRoutes) DebugJSON() DebugRoutes {
// Populate available routes // Populate available routes
for nodeID, routes := range pr.routes { for nodeID, routes := range pr.routes {
prefixes := routes.Slice() prefixes := routes.Slice()
tsaddr.SortPrefixes(prefixes) slices.SortFunc(prefixes, netip.Prefix.Compare)
debug.AvailableRoutes[nodeID] = prefixes debug.AvailableRoutes[nodeID] = prefixes
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/types/ptr"
) )
// TestEphemeralNodeDeleteWithConcurrentUpdate tests the race condition where UpdateNode and DeleteNode // TestEphemeralNodeDeleteWithConcurrentUpdate tests the race condition where UpdateNode and DeleteNode
@@ -55,7 +54,7 @@ func TestEphemeralNodeDeleteWithConcurrentUpdate(t *testing.T) {
go func() { go func() {
updatedNode, updateOk = store.UpdateNode(node.ID, func(n *types.Node) { updatedNode, updateOk = store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
}) })
done <- true done <- true
@@ -114,7 +113,7 @@ func TestUpdateNodeReturnsInvalidWhenDeletedInSameBatch(t *testing.T) {
// Start UpdateNode in goroutine - it will queue and wait for batch // Start UpdateNode in goroutine - it will queue and wait for batch
go func() { go func() {
node, ok := store.UpdateNode(node.ID, func(n *types.Node) { node, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
}) })
resultChan <- struct { resultChan <- struct {
node types.NodeView node types.NodeView
@@ -165,7 +164,7 @@ func TestPersistNodeToDBPreventsRaceCondition(t *testing.T) {
// Simulate UpdateNode being called // Simulate UpdateNode being called
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) { updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
}) })
require.True(t, ok, "UpdateNode should succeed") require.True(t, ok, "UpdateNode should succeed")
require.True(t, updatedNode.Valid(), "UpdateNode should return valid node") require.True(t, updatedNode.Valid(), "UpdateNode should return valid node")
@@ -234,7 +233,7 @@ func TestEphemeralNodeLogoutRaceCondition(t *testing.T) {
// Goroutine 1: UpdateNode (simulates UpdateNodeFromMapRequest) // Goroutine 1: UpdateNode (simulates UpdateNodeFromMapRequest)
go func() { go func() {
updatedNode, updateOk = store.UpdateNode(ephemeralNode.ID, func(n *types.Node) { updatedNode, updateOk = store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
}) })
done <- true done <- true
@@ -310,7 +309,7 @@ func TestUpdateNodeFromMapRequestEphemeralLogoutSequence(t *testing.T) {
go func() { go func() {
node, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) { node, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
endpoint := netip.MustParseAddrPort("10.0.0.1:41641") endpoint := netip.MustParseAddrPort("10.0.0.1:41641")
n.Endpoints = []netip.AddrPort{endpoint} n.Endpoints = []netip.AddrPort{endpoint}
}) })
@@ -380,7 +379,7 @@ func TestUpdateNodeDeletedInSameBatchReturnsInvalid(t *testing.T) {
go func() { go func() {
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) { updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
}) })
updateDone <- struct { updateDone <- struct {
node types.NodeView node types.NodeView
@@ -435,7 +434,7 @@ func TestPersistNodeToDBChecksNodeStoreBeforePersist(t *testing.T) {
// UpdateNode returns a node // UpdateNode returns a node
updatedNode, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) { updatedNode, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now()) n.LastSeen = new(time.Now())
}) })
require.True(t, ok, "UpdateNode should succeed") require.True(t, ok, "UpdateNode should succeed")
require.True(t, updatedNode.Valid(), "updated node should be valid") require.True(t, updatedNode.Valid(), "updated node should be valid")

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
func TestSnapshotFromNodes(t *testing.T) { func TestSnapshotFromNodes(t *testing.T) {
@@ -175,7 +174,7 @@ func createTestNode(nodeID types.NodeID, userID uint, username, hostname string)
DiscoKey: discoKey.Public(), DiscoKey: discoKey.Public(),
Hostname: hostname, Hostname: hostname,
GivenName: hostname, GivenName: hostname,
UserID: ptr.To(userID), UserID: new(userID),
User: &types.User{ User: &types.User{
Name: username, Name: username,
DisplayName: username, DisplayName: username,
@@ -883,7 +882,7 @@ func createConcurrentTestNode(id types.NodeID, hostname string) types.Node {
Hostname: hostname, Hostname: hostname,
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
UserID: ptr.To(uint(1)), UserID: new(uint(1)),
User: &types.User{ User: &types.User{
Name: "concurrent-test-user", Name: "concurrent-test-user",
}, },

View File

@@ -27,10 +27,8 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
"tailscale.com/types/views" "tailscale.com/types/views"
zcache "zgo.at/zcache/v2" zcache "zgo.at/zcache/v2"
) )
@@ -135,7 +133,7 @@ func NewState(cfg *types.Config) (*State, error) {
// On startup, all nodes should be marked as offline until they reconnect // On startup, all nodes should be marked as offline until they reconnect
// This ensures we don't have stale online status from previous runs // This ensures we don't have stale online status from previous runs
for _, node := range nodes { for _, node := range nodes {
node.IsOnline = ptr.To(false) node.IsOnline = new(false)
} }
users, err := db.ListUsers() users, err := db.ListUsers()
@@ -479,7 +477,7 @@ func (s *State) Connect(id types.NodeID) []change.Change {
// the NodeStore already reflects the correct online status for full map generation. // the NodeStore already reflects the correct online status for full map generation.
// now := time.Now() // now := time.Now()
node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) { node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) {
n.IsOnline = ptr.To(true) n.IsOnline = new(true)
// n.LastSeen = ptr.To(now) // n.LastSeen = ptr.To(now)
}) })
if !ok { if !ok {
@@ -507,9 +505,9 @@ func (s *State) Disconnect(id types.NodeID) ([]change.Change, error) {
now := time.Now() now := time.Now()
node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) { node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) {
n.LastSeen = ptr.To(now) n.LastSeen = new(now)
// NodeStore is the source of truth for all node state including online status. // NodeStore is the source of truth for all node state including online status.
n.IsOnline = ptr.To(false) n.IsOnline = new(false)
}) })
if !ok { if !ok {
@@ -805,7 +803,7 @@ func (s *State) BackfillNodeIPs() ([]string, error) {
// Preserve online status and NetInfo when refreshing from database // Preserve online status and NetInfo when refreshing from database
existingNode, exists := s.nodeStore.GetNode(node.ID) existingNode, exists := s.nodeStore.GetNode(node.ID)
if exists && existingNode.Valid() { if exists && existingNode.Valid() {
node.IsOnline = ptr.To(existingNode.IsOnline().Get()) node.IsOnline = new(existingNode.IsOnline().Get())
// TODO(kradalby): We should ensure we use the same hostinfo and node merge semantics // TODO(kradalby): We should ensure we use the same hostinfo and node merge semantics
// when a node re-registers as we do when it sends a map request (UpdateNodeFromMapRequest). // when a node re-registers as we do when it sends a map request (UpdateNodeFromMapRequest).
@@ -1183,8 +1181,8 @@ func (s *State) applyAuthNodeUpdate(params authNodeUpdateParams) (types.NodeView
) )
node.Endpoints = params.RegEntry.Node.Endpoints node.Endpoints = params.RegEntry.Node.Endpoints
node.IsOnline = ptr.To(false) node.IsOnline = new(false)
node.LastSeen = ptr.To(time.Now()) node.LastSeen = new(time.Now())
// Set RegisterMethod - for conversion this is the new method, // Set RegisterMethod - for conversion this is the new method,
// for reauth we preserve the existing one from regEntry // for reauth we preserve the existing one from regEntry
@@ -1286,7 +1284,7 @@ func (s *State) createAndSaveNewNode(params newNodeParams) (types.NodeView, erro
DiscoKey: params.DiscoKey, DiscoKey: params.DiscoKey,
Hostinfo: params.Hostinfo, Hostinfo: params.Hostinfo,
Endpoints: params.Endpoints, Endpoints: params.Endpoints,
LastSeen: ptr.To(time.Now()), LastSeen: new(time.Now()),
RegisterMethod: params.RegisterMethod, RegisterMethod: params.RegisterMethod,
Expiry: params.Expiry, Expiry: params.Expiry,
} }
@@ -1822,8 +1820,8 @@ func (s *State) HandleNodeFromPreAuthKey(
// Only update AuthKey reference // Only update AuthKey reference
node.AuthKey = pak node.AuthKey = pak
node.AuthKeyID = &pak.ID node.AuthKeyID = &pak.ID
node.IsOnline = ptr.To(false) node.IsOnline = new(false)
node.LastSeen = ptr.To(time.Now()) node.LastSeen = new(time.Now())
// Tagged nodes keep their existing expiry (disabled). // Tagged nodes keep their existing expiry (disabled).
// User-owned nodes update expiry from the client request. // User-owned nodes update expiry from the client request.
@@ -2281,8 +2279,8 @@ func routesChanged(oldNode types.NodeView, newHI *tailcfg.Hostinfo) bool {
newRoutes = []netip.Prefix{} newRoutes = []netip.Prefix{}
} }
tsaddr.SortPrefixes(oldRoutes) slices.SortFunc(oldRoutes, netip.Prefix.Compare)
tsaddr.SortPrefixes(newRoutes) slices.SortFunc(newRoutes, netip.Prefix.Compare)
return !slices.Equal(oldRoutes, newRoutes) return !slices.Equal(oldRoutes, newRoutes)
} }

View File

@@ -333,7 +333,7 @@ func NodeOnline(nodeID types.NodeID) Change {
PeerPatches: []*tailcfg.PeerChange{ PeerPatches: []*tailcfg.PeerChange{
{ {
NodeID: nodeID.NodeID(), NodeID: nodeID.NodeID(),
Online: ptrTo(true), Online: new(true),
}, },
}, },
} }
@@ -346,7 +346,7 @@ func NodeOffline(nodeID types.NodeID) Change {
PeerPatches: []*tailcfg.PeerChange{ PeerPatches: []*tailcfg.PeerChange{
{ {
NodeID: nodeID.NodeID(), NodeID: nodeID.NodeID(),
Online: ptrTo(false), Online: new(false),
}, },
}, },
} }
@@ -367,7 +367,7 @@ func KeyExpiry(nodeID types.NodeID, expiry *time.Time) Change {
// ptrTo returns a pointer to the given value. // ptrTo returns a pointer to the given value.
func ptrTo[T any](v T) *T { func ptrTo[T any](v T) *T {
return &v return new(v)
} }
// High-level change constructors // High-level change constructors

View File

@@ -16,8 +16,8 @@ func TestChange_FieldSync(t *testing.T) {
typ := reflect.TypeFor[Change]() typ := reflect.TypeFor[Change]()
boolCount := 0 boolCount := 0
for i := range typ.NumField() { for field := range typ.Fields() {
if typ.Field(i).Type.Kind() == reflect.Bool { if field.Type.Kind() == reflect.Bool {
boolCount++ boolCount++
} }
} }

View File

@@ -407,8 +407,7 @@ func LoadConfig(path string, isFile bool) error {
err := viper.ReadInConfig() err := viper.ReadInConfig()
if err != nil { if err != nil {
var configFileNotFoundError viper.ConfigFileNotFoundError if _, ok := errors.AsType[viper.ConfigFileNotFoundError](err); ok {
if errors.As(err, &configFileNotFoundError) {
log.Warn().Msg("no config file found, using defaults") log.Warn().Msg("no config file found, using defaults")
return nil return nil
} }

View File

@@ -1104,7 +1104,7 @@ func (nv NodeView) TailNode(
primaryRoutes := primaryRouteFunc(nv.ID()) primaryRoutes := primaryRouteFunc(nv.ID())
allowedIPs := slices.Concat(nv.Prefixes(), primaryRoutes, nv.ExitRoutes()) allowedIPs := slices.Concat(nv.Prefixes(), primaryRoutes, nv.ExitRoutes())
tsaddr.SortPrefixes(allowedIPs) slices.SortFunc(allowedIPs, netip.Prefix.Compare)
capMap := tailcfg.NodeCapMap{ capMap := tailcfg.NodeCapMap{
tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, tailcfg.CapabilityAdmin: []tailcfg.RawMessage{},

View File

@@ -6,7 +6,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/types/ptr"
) )
// TestNodeIsTagged tests the IsTagged() method for determining if a node is tagged. // TestNodeIsTagged tests the IsTagged() method for determining if a node is tagged.
@@ -69,7 +68,7 @@ func TestNodeIsTagged(t *testing.T) {
{ {
name: "node with user and no tags - not tagged", name: "node with user and no tags - not tagged",
node: Node{ node: Node{
UserID: ptr.To(uint(42)), UserID: new(uint(42)),
Tags: []string{}, Tags: []string{},
}, },
want: false, want: false,
@@ -112,7 +111,7 @@ func TestNodeViewIsTagged(t *testing.T) {
{ {
name: "user-owned node", name: "user-owned node",
node: Node{ node: Node{
UserID: ptr.To(uint(1)), UserID: new(uint(1)),
}, },
want: false, want: false,
}, },
@@ -223,7 +222,7 @@ func TestNodeTagsImmutableAfterRegistration(t *testing.T) {
// Test that a user-owned node is not tagged // Test that a user-owned node is not tagged
userNode := Node{ userNode := Node{
ID: 2, ID: 2,
UserID: ptr.To(uint(42)), UserID: new(uint(42)),
Tags: []string{}, Tags: []string{},
RegisterMethod: util.RegisterMethodOIDC, RegisterMethod: util.RegisterMethodOIDC,
} }
@@ -243,7 +242,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "tagged node has tags, UserID is informational", name: "tagged node has tags, UserID is informational",
node: Node{ node: Node{
ID: 1, ID: 1,
UserID: ptr.To(uint(5)), // "created by" user 5 UserID: new(uint(5)), // "created by" user 5
Tags: []string{"tag:server"}, Tags: []string{"tag:server"},
}, },
wantIsTagged: true, wantIsTagged: true,
@@ -253,7 +252,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "user-owned node has no tags", name: "user-owned node has no tags",
node: Node{ node: Node{
ID: 2, ID: 2,
UserID: ptr.To(uint(5)), UserID: new(uint(5)),
Tags: []string{}, Tags: []string{},
}, },
wantIsTagged: false, wantIsTagged: false,
@@ -265,7 +264,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "node with only authkey tags - not tagged (tags should be copied)", name: "node with only authkey tags - not tagged (tags should be copied)",
node: Node{ node: Node{
ID: 3, ID: 3,
UserID: ptr.To(uint(5)), // "created by" user 5 UserID: new(uint(5)), // "created by" user 5
AuthKey: &PreAuthKey{ AuthKey: &PreAuthKey{
Tags: []string{"tag:database"}, Tags: []string{"tag:database"},
}, },

View File

@@ -110,9 +110,7 @@ func TestCanUsePreAuthKey(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("expected error but got none") t.Errorf("expected error but got none")
} else { } else {
var httpErr PAKError httpErr, ok := errors.AsType[PAKError](err)
ok := errors.As(err, &httpErr)
if !ok { if !ok {
t.Errorf("expected HTTPError but got %T", err) t.Errorf("expected HTTPError but got %T", err)
} else { } else {

View File

@@ -10,7 +10,7 @@ import (
) )
var PrefixComparer = cmp.Comparer(func(x, y netip.Prefix) bool { var PrefixComparer = cmp.Comparer(func(x, y netip.Prefix) bool {
return x == y return x.Compare(y) == 0
}) })
var IPComparer = cmp.Comparer(func(x, y netip.Addr) bool { var IPComparer = cmp.Comparer(func(x, y netip.Addr) bool {

View File

@@ -20,7 +20,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
var veryLargeDestination = []policyv2.AliasWithPorts{ var veryLargeDestination = []policyv2.AliasWithPorts{
@@ -1284,9 +1283,9 @@ func TestACLAutogroupMember(t *testing.T) {
ACLs: []policyv2.ACL{ ACLs: []policyv2.ACL{
{ {
Action: "accept", Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)}, Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{ Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupMember), tailcfg.PortRangeAny), aliasWithPorts(new(policyv2.AutoGroupMember), tailcfg.PortRangeAny),
}, },
}, },
}, },
@@ -1372,9 +1371,9 @@ func TestACLAutogroupTagged(t *testing.T) {
ACLs: []policyv2.ACL{ ACLs: []policyv2.ACL{
{ {
Action: "accept", Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupTagged)}, Sources: []policyv2.Alias{new(policyv2.AutoGroupTagged)},
Destinations: []policyv2.AliasWithPorts{ Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupTagged), tailcfg.PortRangeAny), aliasWithPorts(new(policyv2.AutoGroupTagged), tailcfg.PortRangeAny),
}, },
}, },
}, },
@@ -1657,9 +1656,9 @@ func TestACLAutogroupSelf(t *testing.T) {
ACLs: []policyv2.ACL{ ACLs: []policyv2.ACL{
{ {
Action: "accept", Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)}, Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{ Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny), aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
}, },
}, },
{ {
@@ -1957,9 +1956,9 @@ func TestACLPolicyPropagationOverTime(t *testing.T) {
ACLs: []policyv2.ACL{ ACLs: []policyv2.ACL{
{ {
Action: "accept", Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)}, Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{ Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny), aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
}, },
}, },
}, },

View File

@@ -17,7 +17,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) { func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) {
@@ -634,7 +633,7 @@ func TestAuthKeyLogoutAndReloginRoutesPreserved(t *testing.T) {
}, },
AutoApprovers: policyv2.AutoApproverPolicy{ AutoApprovers: policyv2.AutoApproverPolicy{
Routes: map[netip.Prefix]policyv2.AutoApprovers{ Routes: map[netip.Prefix]policyv2.AutoApprovers{
netip.MustParsePrefix(advertiseRoute): {ptr.To(policyv2.Username(user + "@test.no"))}, netip.MustParsePrefix(advertiseRoute): {new(policyv2.Username(user + "@test.no"))},
}, },
}, },
}, },

View File

@@ -27,7 +27,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
const ( const (
@@ -906,32 +905,32 @@ func wildcard() policyv2.Alias {
// usernamep returns a pointer to a Username as an Alias for policy v2 configurations. // usernamep returns a pointer to a Username as an Alias for policy v2 configurations.
// Used in ACL rules to reference specific users in network access policies. // Used in ACL rules to reference specific users in network access policies.
func usernamep(name string) policyv2.Alias { func usernamep(name string) policyv2.Alias {
return ptr.To(policyv2.Username(name)) return new(policyv2.Username(name))
} }
// hostp returns a pointer to a Host as an Alias for policy v2 configurations. // hostp returns a pointer to a Host as an Alias for policy v2 configurations.
// Used in ACL rules to reference specific hosts in network access policies. // Used in ACL rules to reference specific hosts in network access policies.
func hostp(name string) policyv2.Alias { func hostp(name string) policyv2.Alias {
return ptr.To(policyv2.Host(name)) return new(policyv2.Host(name))
} }
// groupp returns a pointer to a Group as an Alias for policy v2 configurations. // groupp returns a pointer to a Group as an Alias for policy v2 configurations.
// Used in ACL rules to reference user groups in network access policies. // Used in ACL rules to reference user groups in network access policies.
func groupp(name string) policyv2.Alias { func groupp(name string) policyv2.Alias {
return ptr.To(policyv2.Group(name)) return new(policyv2.Group(name))
} }
// tagp returns a pointer to a Tag as an Alias for policy v2 configurations. // tagp returns a pointer to a Tag as an Alias for policy v2 configurations.
// Used in ACL rules to reference node tags in network access policies. // Used in ACL rules to reference node tags in network access policies.
func tagp(name string) policyv2.Alias { func tagp(name string) policyv2.Alias {
return ptr.To(policyv2.Tag(name)) return new(policyv2.Tag(name))
} }
// prefixp returns a pointer to a Prefix from a CIDR string for policy v2 configurations. // prefixp returns a pointer to a Prefix from a CIDR string for policy v2 configurations.
// Converts CIDR notation to policy prefix format for network range specifications. // Converts CIDR notation to policy prefix format for network range specifications.
func prefixp(cidr string) policyv2.Alias { func prefixp(cidr string) policyv2.Alias {
prefix := netip.MustParsePrefix(cidr) prefix := netip.MustParsePrefix(cidr)
return ptr.To(policyv2.Prefix(prefix)) return new(policyv2.Prefix(prefix))
} }
// aliasWithPorts creates an AliasWithPorts structure from an alias and port ranges. // aliasWithPorts creates an AliasWithPorts structure from an alias and port ranges.
@@ -947,7 +946,7 @@ func aliasWithPorts(alias policyv2.Alias, ports ...tailcfg.PortRange) policyv2.A
// usernameOwner returns a Username as an Owner for use in TagOwners policies. // usernameOwner returns a Username as an Owner for use in TagOwners policies.
// Specifies which users can assign and manage specific tags in ACL configurations. // Specifies which users can assign and manage specific tags in ACL configurations.
func usernameOwner(name string) policyv2.Owner { func usernameOwner(name string) policyv2.Owner {
return ptr.To(policyv2.Username(name)) return new(policyv2.Username(name))
} }
// groupOwner returns a Group as an Owner for use in TagOwners policies. // groupOwner returns a Group as an Owner for use in TagOwners policies.
@@ -955,25 +954,25 @@ func usernameOwner(name string) policyv2.Owner {
// //
//nolint:unused //nolint:unused
func groupOwner(name string) policyv2.Owner { func groupOwner(name string) policyv2.Owner {
return ptr.To(policyv2.Group(name)) return new(policyv2.Group(name))
} }
// usernameApprover returns a Username as an AutoApprover for subnet route policies. // usernameApprover returns a Username as an AutoApprover for subnet route policies.
// Specifies which users can automatically approve subnet route advertisements. // Specifies which users can automatically approve subnet route advertisements.
func usernameApprover(name string) policyv2.AutoApprover { func usernameApprover(name string) policyv2.AutoApprover {
return ptr.To(policyv2.Username(name)) return new(policyv2.Username(name))
} }
// groupApprover returns a Group as an AutoApprover for subnet route policies. // groupApprover returns a Group as an AutoApprover for subnet route policies.
// Specifies which groups can automatically approve subnet route advertisements. // Specifies which groups can automatically approve subnet route advertisements.
func groupApprover(name string) policyv2.AutoApprover { func groupApprover(name string) policyv2.AutoApprover {
return ptr.To(policyv2.Group(name)) return new(policyv2.Group(name))
} }
// tagApprover returns a Tag as an AutoApprover for subnet route policies. // tagApprover returns a Tag as an AutoApprover for subnet route policies.
// Specifies which tagged nodes can automatically approve subnet route advertisements. // Specifies which tagged nodes can automatically approve subnet route advertisements.
func tagApprover(name string) policyv2.AutoApprover { func tagApprover(name string) policyv2.AutoApprover {
return ptr.To(policyv2.Tag(name)) return new(policyv2.Tag(name))
} }
// oidcMockUser creates a MockUser for OIDC authentication testing. // oidcMockUser creates a MockUser for OIDC authentication testing.

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
func isSSHNoAccessStdError(stderr string) bool { func isSSHNoAccessStdError(stderr string) bool {
@@ -85,8 +84,8 @@ func TestSSHOneUserToAll(t *testing.T) {
// Use autogroup:member and autogroup:tagged instead of wildcard // Use autogroup:member and autogroup:tagged instead of wildcard
// since wildcard (*) is no longer supported for SSH destinations // since wildcard (*) is no longer supported for SSH destinations
Destinations: policyv2.SSHDstAliases{ Destinations: policyv2.SSHDstAliases{
ptr.To(policyv2.AutoGroupMember), new(policyv2.AutoGroupMember),
ptr.To(policyv2.AutoGroupTagged), new(policyv2.AutoGroupTagged),
}, },
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")}, Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
}, },
@@ -160,7 +159,7 @@ func TestSSHMultipleUsersAllToAll(t *testing.T) {
// Username destinations (e.g., "user1@") now require the source // Username destinations (e.g., "user1@") now require the source
// to be that exact same user only. For group-to-group SSH access, // to be that exact same user only. For group-to-group SSH access,
// use autogroup:self instead. // use autogroup:self instead.
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)}, Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")}, Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
}, },
}, },
@@ -285,7 +284,7 @@ func TestSSHIsBlockedInACL(t *testing.T) {
{ {
Action: "accept", Action: "accept",
Sources: policyv2.SSHSrcAliases{groupp("group:integration-test")}, Sources: policyv2.SSHSrcAliases{groupp("group:integration-test")},
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)}, Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")}, Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
}, },
}, },
@@ -340,13 +339,13 @@ func TestSSHUserOnlyIsolation(t *testing.T) {
{ {
Action: "accept", Action: "accept",
Sources: policyv2.SSHSrcAliases{groupp("group:ssh1")}, Sources: policyv2.SSHSrcAliases{groupp("group:ssh1")},
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)}, Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")}, Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
}, },
{ {
Action: "accept", Action: "accept",
Sources: policyv2.SSHSrcAliases{groupp("group:ssh2")}, Sources: policyv2.SSHSrcAliases{groupp("group:ssh2")},
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)}, Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")}, Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
}, },
}, },
@@ -522,10 +521,10 @@ func TestSSHAutogroupSelf(t *testing.T) {
{ {
Action: "accept", Action: "accept",
Sources: policyv2.SSHSrcAliases{ Sources: policyv2.SSHSrcAliases{
ptr.To(policyv2.AutoGroupMember), new(policyv2.AutoGroupMember),
}, },
Destinations: policyv2.SSHDstAliases{ Destinations: policyv2.SSHDstAliases{
ptr.To(policyv2.AutoGroupSelf), new(policyv2.AutoGroupSelf),
}, },
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")}, Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
}, },

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/ptr"
) )
const tagTestUser = "taguser" const tagTestUser = "taguser"
@@ -30,9 +29,9 @@ const tagTestUser = "taguser"
func tagsTestPolicy() *policyv2.Policy { func tagsTestPolicy() *policyv2.Policy {
return &policyv2.Policy{ return &policyv2.Policy{
TagOwners: policyv2.TagOwners{ TagOwners: policyv2.TagOwners{
"tag:valid-owned": policyv2.Owners{ptr.To(policyv2.Username(tagTestUser + "@"))}, "tag:valid-owned": policyv2.Owners{new(policyv2.Username(tagTestUser + "@"))},
"tag:second": policyv2.Owners{ptr.To(policyv2.Username(tagTestUser + "@"))}, "tag:second": policyv2.Owners{new(policyv2.Username(tagTestUser + "@"))},
"tag:valid-unowned": policyv2.Owners{ptr.To(policyv2.Username("other-user@"))}, "tag:valid-unowned": policyv2.Owners{new(policyv2.Username("other-user@"))},
// Note: tag:nonexistent deliberately NOT defined // Note: tag:nonexistent deliberately NOT defined
}, },
ACLs: []policyv2.ACL{ ACLs: []policyv2.ACL{