mirror of
https://github.com/juanfont/headscale.git
synced 2026-02-21 12:10:30 +09:00
all: upgrade to Go 1.26rc2 and modernize codebase
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Check Generated Files / check-generated (push) Has been cancelled
NixOS Module Tests / nix-module-check (push) Has been cancelled
Tests / test (push) Has been cancelled
Close inactive issues / close-issues (push) Has been cancelled
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Check Generated Files / check-generated (push) Has been cancelled
NixOS Module Tests / nix-module-check (push) Has been cancelled
Tests / test (push) Has been cancelled
Close inactive issues / close-issues (push) Has been cancelled
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:
6
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -6,8 +6,7 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is this a support request?
|
||||
description:
|
||||
This issue tracker is for bugs and feature requests only. If you need
|
||||
description: This issue tracker is for bugs and feature requests only. If you need
|
||||
help, please use ask in our Discord community
|
||||
options:
|
||||
- label: This is not a support request
|
||||
@@ -15,8 +14,7 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description:
|
||||
Please search to see if an issue already exists for the bug you
|
||||
description: Please search to see if an issue already exists for the bug you
|
||||
encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
|
||||
@@ -45,6 +45,16 @@ linters:
|
||||
Import "github.com/juanfont/headscale/hscontrol/util/zlog/zf" and use
|
||||
constants like zf.NodeID, zf.UserName, etc. Add new constants to
|
||||
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
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
version: 2
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy -compat=1.25
|
||||
- go mod tidy -compat=1.26
|
||||
- go mod vendor
|
||||
|
||||
release:
|
||||
|
||||
@@ -43,26 +43,12 @@ repos:
|
||||
entry: prettier --write --list-different
|
||||
language: system
|
||||
exclude: ^docs/
|
||||
types_or:
|
||||
[
|
||||
javascript,
|
||||
jsx,
|
||||
ts,
|
||||
tsx,
|
||||
yaml,
|
||||
json,
|
||||
toml,
|
||||
html,
|
||||
css,
|
||||
scss,
|
||||
sass,
|
||||
markdown,
|
||||
]
|
||||
types_or: [javascript, jsx, ts, tsx, yaml, json, toml, html, css, scss, sass, markdown]
|
||||
|
||||
# golangci-lint for Go code quality
|
||||
- id: 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
|
||||
types: [go]
|
||||
pass_filenames: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# For testing purposes only
|
||||
|
||||
FROM golang:alpine AS build-env
|
||||
FROM golang:1.26rc2-alpine AS build-env
|
||||
|
||||
WORKDIR /go/src
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# and are in no way endorsed by Headscale's maintainers as an
|
||||
# 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
|
||||
ENV GOPATH /go
|
||||
WORKDIR /go/src/headscale
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# This Dockerfile is more or less lifted from tailscale/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
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ var serveCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app, err := newHeadscaleServerWithConfig()
|
||||
if err != nil {
|
||||
var squibbleErr squibble.ValidationError
|
||||
if errors.As(err, &squibbleErr) {
|
||||
if squibbleErr, ok := errors.AsType[squibble.ValidationError](err); ok {
|
||||
fmt.Printf("SQLite schema failed to validate:\n")
|
||||
fmt.Println(squibbleErr.Diff)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func detectGoVersion() string {
|
||||
|
||||
content, err := os.ReadFile(goModPath)
|
||||
if err != nil {
|
||||
return "1.25"
|
||||
return "1.26rc2"
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
52
flake.nix
52
flake.nix
@@ -26,7 +26,7 @@
|
||||
overlays.default = _: prev:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system};
|
||||
buildGo = pkgs.buildGo125Module;
|
||||
buildGo = pkgs.buildGo126Module;
|
||||
vendorHash = "sha256-9BvphYDAxzwooyVokI3l+q1wRuRsWn/qM+NpWUgqJH0=";
|
||||
in
|
||||
{
|
||||
@@ -94,14 +94,46 @@
|
||||
subPackages = [ "." ];
|
||||
};
|
||||
|
||||
# Upstream does not override buildGoModule properly,
|
||||
# importing a specific module, so comment out for now.
|
||||
# golangci-lint = prev.golangci-lint.override {
|
||||
# buildGoModule = buildGo;
|
||||
# };
|
||||
# golangci-lint-langserver = prev.golangci-lint.override {
|
||||
# buildGoModule = buildGo;
|
||||
# };
|
||||
# Build golangci-lint with Go 1.26 (upstream uses hardcoded Go version)
|
||||
golangci-lint = buildGo rec {
|
||||
pname = "golangci-lint";
|
||||
version = "2.8.0";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
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.
|
||||
# goreleaser = prev.goreleaser.override {
|
||||
@@ -132,7 +164,7 @@
|
||||
overlays = [ self.overlays.default ];
|
||||
inherit system;
|
||||
};
|
||||
buildDeps = with pkgs; [ git go_1_25 gnumake ];
|
||||
buildDeps = with pkgs; [ git go_1_26 gnumake ];
|
||||
devDeps = with pkgs;
|
||||
buildDeps
|
||||
++ [
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/juanfont/headscale
|
||||
|
||||
go 1.25.5
|
||||
go 1.26rc2
|
||||
|
||||
require (
|
||||
github.com/arl/statsviz v0.8.0
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
type AuthProvider interface {
|
||||
@@ -113,8 +112,7 @@ func (h *Headscale) handleRegister(
|
||||
resp, err := h.handleRegisterWithAuthKey(req, machineKey)
|
||||
if err != nil {
|
||||
// Preserve HTTPError types so they can be handled properly by the HTTP layer
|
||||
var httpErr HTTPError
|
||||
if errors.As(err, &httpErr) {
|
||||
if httpErr, ok := errors.AsType[HTTPError](err); ok {
|
||||
return nil, httpErr
|
||||
}
|
||||
|
||||
@@ -315,7 +313,7 @@ func (h *Headscale) reqToNewRegisterResponse(
|
||||
MachineKey: machineKey,
|
||||
NodeKey: req.NodeKey,
|
||||
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)
|
||||
}
|
||||
|
||||
var perr types.PAKError
|
||||
if errors.As(err, &perr) {
|
||||
if perr, ok := errors.AsType[types.PAKError](err); ok {
|
||||
return nil, NewHTTPError(http.StatusUnauthorized, perr.Error(), nil)
|
||||
}
|
||||
|
||||
@@ -443,7 +440,7 @@ func (h *Headscale) handleRegisterInteractive(
|
||||
MachineKey: machineKey,
|
||||
NodeKey: req.NodeKey,
|
||||
Hostinfo: hostinfo,
|
||||
LastSeen: ptr.To(time.Now()),
|
||||
LastSeen: new(time.Now()),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/schema"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"zgo.at/zcache/v2"
|
||||
)
|
||||
|
||||
@@ -172,7 +171,7 @@ AND auth_key_id NOT IN (
|
||||
}
|
||||
|
||||
for nodeID, routes := range nodeRoutes {
|
||||
tsaddr.SortPrefixes(routes)
|
||||
slices.SortFunc(routes, netip.Prefix.Compare)
|
||||
routes = slices.Compact(routes)
|
||||
|
||||
data, _ := json.Marshal(routes)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
var mpp = func(pref string) *netip.Prefix {
|
||||
@@ -491,8 +490,8 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
|
||||
|
||||
alloc, err := NewIPAllocator(
|
||||
db,
|
||||
ptr.To(tsaddr.CGNATRange()),
|
||||
ptr.To(tsaddr.TailscaleULARange()),
|
||||
new(tsaddr.CGNATRange()),
|
||||
new(tsaddr.TailscaleULARange()),
|
||||
types.IPAllocationStrategySequential,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -500,17 +499,17 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
assert.Equal(t, na("100.100.100.101"), *nextQuad100)
|
||||
|
||||
// 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)
|
||||
assert.Equal(t, na("fd7a:115c:a1e0::54"), *nextQuad100v6)
|
||||
|
||||
// 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())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, na("100.115.94.0"), *nextChrome)
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -694,7 +693,7 @@ func (hsdb *HSDatabase) CreateNodeForTest(user *types.User, hostname ...string)
|
||||
Hostname: nodeName,
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(pak.ID),
|
||||
AuthKeyID: new(pak.ID),
|
||||
}
|
||||
|
||||
err = hsdb.DB.Save(node).Error
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestGetNode(t *testing.T) {
|
||||
@@ -115,7 +114,7 @@ func TestExpireNode(t *testing.T) {
|
||||
Hostname: "testnode",
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(pak.ID),
|
||||
AuthKeyID: new(pak.ID),
|
||||
Expiry: &time.Time{},
|
||||
}
|
||||
db.DB.Save(node)
|
||||
@@ -159,7 +158,7 @@ func TestSetTags(t *testing.T) {
|
||||
Hostname: "testnode",
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(pak.ID),
|
||||
AuthKeyID: new(pak.ID),
|
||||
}
|
||||
|
||||
trx := db.DB.Save(node)
|
||||
@@ -444,7 +443,7 @@ func TestAutoApproveRoutes(t *testing.T) {
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
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
|
||||
@@ -461,7 +460,7 @@ func TestAutoApproveRoutes(t *testing.T) {
|
||||
RoutableIPs: tt.routes,
|
||||
},
|
||||
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
|
||||
@@ -660,7 +659,7 @@ func TestListEphemeralNodes(t *testing.T) {
|
||||
Hostname: "test",
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(pak.ID),
|
||||
AuthKeyID: new(pak.ID),
|
||||
}
|
||||
|
||||
nodeEph := types.Node{
|
||||
@@ -670,7 +669,7 @@ func TestListEphemeralNodes(t *testing.T) {
|
||||
Hostname: "ephemeral",
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(pakEph.ID),
|
||||
AuthKeyID: new(pakEph.ID),
|
||||
}
|
||||
|
||||
err = db.DB.Save(&node).Error
|
||||
@@ -763,8 +762,8 @@ func TestNodeNaming(t *testing.T) {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = RegisterNodeForTest(tx, nodeInvalidHostname, ptr.To(mpp("100.64.0.66/32").Addr()), nil)
|
||||
_, err = RegisterNodeForTest(tx, nodeShortHostname, ptr.To(mpp("100.64.0.67/32").Addr()), nil)
|
||||
_, _ = RegisterNodeForTest(tx, nodeInvalidHostname, new(mpp("100.64.0.66/32").Addr()), nil)
|
||||
_, err = RegisterNodeForTest(tx, nodeShortHostname, new(mpp("100.64.0.67/32").Addr()), nil)
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestCreatePreAuthKey(t *testing.T) {
|
||||
@@ -24,7 +23,7 @@ func TestCreatePreAuthKey(t *testing.T) {
|
||||
test: func(t *testing.T, db *HSDatabase) {
|
||||
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)
|
||||
},
|
||||
},
|
||||
@@ -127,7 +126,7 @@ func TestCannotDeleteAssignedPreAuthKey(t *testing.T) {
|
||||
Hostname: "testest",
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(key.ID),
|
||||
AuthKeyID: new(key.ID),
|
||||
}
|
||||
db.DB.Save(&node)
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestCreateAndDestroyUser(t *testing.T) {
|
||||
@@ -79,7 +78,7 @@ func TestDestroyUserErrors(t *testing.T) {
|
||||
Hostname: "testnode",
|
||||
UserID: &user.ID,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
AuthKeyID: ptr.To(pak.ID),
|
||||
AuthKeyID: new(pak.ID),
|
||||
}
|
||||
trx := db.DB.Save(&node)
|
||||
require.NoError(t, trx.Error)
|
||||
|
||||
@@ -387,7 +387,7 @@ func (api headscaleV1APIServer) SetApprovedRoutes(
|
||||
newApproved = append(newApproved, prefix)
|
||||
}
|
||||
}
|
||||
tsaddr.SortPrefixes(newApproved)
|
||||
slices.SortFunc(newApproved, netip.Prefix.Compare)
|
||||
newApproved = slices.Compact(newApproved)
|
||||
|
||||
node, nodeChange, err := api.h.state.SetApprovedRoutes(types.NodeID(request.GetNodeId()), newApproved)
|
||||
|
||||
@@ -36,8 +36,7 @@ const (
|
||||
|
||||
// httpError logs an error and sends an HTTP error response with the given.
|
||||
func httpError(w http.ResponseWriter, err error) {
|
||||
var herr HTTPError
|
||||
if errors.As(err, &herr) {
|
||||
if herr, ok := errors.AsType[HTTPError](err); ok {
|
||||
http.Error(w, herr.Msg, herr.Code)
|
||||
log.Error().Err(herr.Err).Int("code", herr.Code).Msgf("user msg: %s", herr.Msg)
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
// 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
|
||||
// The node will get a fresh full map when it reconnects
|
||||
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
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
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 {
|
||||
return &types.Node{
|
||||
Hostname: hostname,
|
||||
UserID: ptr.To(userid),
|
||||
UserID: new(userid),
|
||||
User: &types.User{
|
||||
Name: username,
|
||||
},
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestTailNode(t *testing.T) {
|
||||
@@ -98,7 +97,7 @@ func TestTailNode(t *testing.T) {
|
||||
IPv4: iap("100.64.0.1"),
|
||||
Hostname: "mini",
|
||||
GivenName: "mini",
|
||||
UserID: ptr.To(uint(0)),
|
||||
UserID: new(uint(0)),
|
||||
User: &types.User{
|
||||
Name: "mini",
|
||||
},
|
||||
@@ -140,8 +139,8 @@ func TestTailNode(t *testing.T) {
|
||||
Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")},
|
||||
AllowedIPs: []netip.Prefix{
|
||||
tsaddr.AllIPv4(),
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
netip.MustParsePrefix("100.64.0.1/32"),
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
tsaddr.AllIPv6(),
|
||||
},
|
||||
PrimaryRoutes: []netip.Prefix{
|
||||
|
||||
@@ -265,8 +265,7 @@ func (ns *noiseServer) NoiseRegistrationHandler(
|
||||
|
||||
resp, err = ns.headscale.handleRegister(req.Context(), regReq, ns.conn.Peer())
|
||||
if err != nil {
|
||||
var httpErr HTTPError
|
||||
if errors.As(err, &httpErr) {
|
||||
if httpErr, ok := errors.AsType[HTTPError](err); ok {
|
||||
resp = &tailcfg.RegisterResponse{
|
||||
Error: httpErr.Msg,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/samber/lo"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
@@ -111,7 +110,7 @@ func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApprove
|
||||
}
|
||||
|
||||
// Sort and deduplicate
|
||||
tsaddr.SortPrefixes(newApproved)
|
||||
slices.SortFunc(newApproved, netip.Prefix.Compare)
|
||||
newApproved = slices.Compact(newApproved)
|
||||
newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool {
|
||||
return route.IsValid()
|
||||
@@ -120,7 +119,7 @@ func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApprove
|
||||
// Sort the current approved for comparison
|
||||
sortedCurrent := make([]netip.Prefix, len(currentApproved))
|
||||
copy(sortedCurrent, currentApproved)
|
||||
tsaddr.SortPrefixes(sortedCurrent)
|
||||
slices.SortFunc(sortedCurrent, netip.Prefix.Compare)
|
||||
|
||||
// Only update if the routes actually changed
|
||||
if !slices.Equal(sortedCurrent, newApproved) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package policy
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
policyv2 "github.com/juanfont/headscale/hscontrol/policy/v2"
|
||||
@@ -11,9 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
@@ -33,10 +32,10 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
NodeKey: key.NewNode().Public(),
|
||||
Hostname: "test-node",
|
||||
UserID: ptr.To(user1.ID),
|
||||
User: ptr.To(user1),
|
||||
UserID: new(user1.ID),
|
||||
User: new(user1),
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
|
||||
IPv4: new(netip.MustParseAddr("100.64.0.1")),
|
||||
Tags: []string{"tag:test"},
|
||||
}
|
||||
|
||||
@@ -45,10 +44,10 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
NodeKey: key.NewNode().Public(),
|
||||
Hostname: "other-node",
|
||||
UserID: ptr.To(user2.ID),
|
||||
User: ptr.To(user2),
|
||||
UserID: new(user2.ID),
|
||||
User: new(user2),
|
||||
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
|
||||
@@ -195,7 +194,7 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
|
||||
assert.Equal(t, tt.wantChanged, gotChanged, "changed flag mismatch: %s", tt.description)
|
||||
|
||||
// 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)
|
||||
|
||||
// Verify that all previously approved routes are still present
|
||||
@@ -305,10 +304,10 @@ func TestApproveRoutesWithPolicy_NilAndEmptyCases(t *testing.T) {
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
NodeKey: key.NewNode().Public(),
|
||||
Hostname: "testnode",
|
||||
UserID: ptr.To(user.ID),
|
||||
User: ptr.To(user),
|
||||
UserID: new(user.ID),
|
||||
User: new(user),
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
|
||||
IPv4: new(netip.MustParseAddr("100.64.0.1")),
|
||||
ApprovedRoutes: tt.currentApproved,
|
||||
}
|
||||
nodes := types.Nodes{&node}
|
||||
@@ -334,7 +333,7 @@ func TestApproveRoutesWithPolicy_NilAndEmptyCases(t *testing.T) {
|
||||
if tt.wantApproved == nil {
|
||||
assert.Nil(t, gotApproved, "expected nil approved routes")
|
||||
} else {
|
||||
tsaddr.SortPrefixes(tt.wantApproved)
|
||||
slices.SortFunc(tt.wantApproved, netip.Prefix.Compare)
|
||||
assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
|
||||
@@ -92,8 +91,8 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
|
||||
announcedRoutes: []netip.Prefix{}, // No routes announced anymore
|
||||
nodeUser: "test",
|
||||
wantApproved: []netip.Prefix{
|
||||
netip.MustParsePrefix("172.16.0.0/16"),
|
||||
netip.MustParsePrefix("10.0.0.0/24"),
|
||||
netip.MustParsePrefix("172.16.0.0/16"),
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
},
|
||||
wantChanged: false,
|
||||
@@ -124,8 +123,8 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
|
||||
nodeUser: "test",
|
||||
nodeTags: []string{"tag:approved"},
|
||||
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("172.16.0.0/16"), // New tag-approved
|
||||
},
|
||||
wantChanged: true,
|
||||
},
|
||||
@@ -168,13 +167,13 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
NodeKey: key.NewNode().Public(),
|
||||
Hostname: tt.nodeHostname,
|
||||
UserID: ptr.To(user.ID),
|
||||
User: ptr.To(user),
|
||||
UserID: new(user.ID),
|
||||
User: new(user),
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: tt.announcedRoutes,
|
||||
},
|
||||
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
|
||||
IPv4: new(netip.MustParseAddr("100.64.0.1")),
|
||||
ApprovedRoutes: tt.currentApproved,
|
||||
Tags: tt.nodeTags,
|
||||
}
|
||||
@@ -294,13 +293,13 @@ func TestApproveRoutesWithPolicy_EdgeCases(t *testing.T) {
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
NodeKey: key.NewNode().Public(),
|
||||
Hostname: "testnode",
|
||||
UserID: ptr.To(user.ID),
|
||||
User: ptr.To(user),
|
||||
UserID: new(user.ID),
|
||||
User: new(user),
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: tt.announcedRoutes,
|
||||
},
|
||||
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
|
||||
IPv4: new(netip.MustParseAddr("100.64.0.1")),
|
||||
ApprovedRoutes: tt.currentApproved,
|
||||
}
|
||||
nodes := types.Nodes{&node}
|
||||
@@ -343,13 +342,13 @@ func TestApproveRoutesWithPolicy_NilPolicyManagerCase(t *testing.T) {
|
||||
MachineKey: key.NewMachine().Public(),
|
||||
NodeKey: key.NewNode().Public(),
|
||||
Hostname: "testnode",
|
||||
UserID: ptr.To(user.ID),
|
||||
User: ptr.To(user),
|
||||
UserID: new(user.ID),
|
||||
User: new(user),
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: announcedRoutes,
|
||||
},
|
||||
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
|
||||
IPv4: new(netip.MustParseAddr("100.64.0.1")),
|
||||
ApprovedRoutes: currentApproved,
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
var ap = func(ipStr string) *netip.Addr {
|
||||
@@ -1084,21 +1083,21 @@ func TestSSHPolicyRules(t *testing.T) {
|
||||
nodeUser1 := types.Node{
|
||||
Hostname: "user1-device",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
UserID: ptr.To(uint(1)),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: new(uint(1)),
|
||||
User: new(users[0]),
|
||||
}
|
||||
nodeUser2 := types.Node{
|
||||
Hostname: "user2-device",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
UserID: ptr.To(uint(2)),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(uint(2)),
|
||||
User: new(users[1]),
|
||||
}
|
||||
|
||||
taggedClient := types.Node{
|
||||
Hostname: "tagged-client",
|
||||
IPv4: ap("100.64.0.4"),
|
||||
UserID: ptr.To(uint(2)),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(uint(2)),
|
||||
User: new(users[1]),
|
||||
Tags: []string{"tag:client"},
|
||||
}
|
||||
|
||||
@@ -1106,8 +1105,8 @@ func TestSSHPolicyRules(t *testing.T) {
|
||||
nodeTaggedServer := types.Node{
|
||||
Hostname: "tagged-server",
|
||||
IPv4: ap("100.64.0.5"),
|
||||
UserID: ptr.To(uint(1)),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: new(uint(1)),
|
||||
User: new(users[0]),
|
||||
Tags: []string{"tag:server"},
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
"tailscale.com/util/must"
|
||||
)
|
||||
|
||||
@@ -145,13 +144,13 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
|
||||
User: ptr.To(users[0]),
|
||||
User: new(users[0]),
|
||||
},
|
||||
peers: types.Nodes{
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
|
||||
User: ptr.To(users[0]),
|
||||
User: new(users[0]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{},
|
||||
@@ -192,7 +191,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("10.33.0.0/16"),
|
||||
@@ -203,7 +202,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
@@ -274,19 +273,19 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
peers: types.Nodes{
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[2]),
|
||||
User: new(users[2]),
|
||||
},
|
||||
// "internal" exit node
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.100"),
|
||||
IPv6: ap("fd7a:115c:a1e0::100"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: tsaddr.ExitRoutes(),
|
||||
},
|
||||
@@ -335,7 +334,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.100"),
|
||||
IPv6: ap("fd7a:115c:a1e0::100"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: tsaddr.ExitRoutes(),
|
||||
},
|
||||
@@ -344,12 +343,12 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[2]),
|
||||
User: new(users[2]),
|
||||
},
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
@@ -442,7 +441,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.100"),
|
||||
IPv6: ap("fd7a:115c:a1e0::100"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: tsaddr.ExitRoutes(),
|
||||
},
|
||||
@@ -451,12 +450,12 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[2]),
|
||||
User: new(users[2]),
|
||||
},
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
@@ -549,7 +548,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.100"),
|
||||
IPv6: ap("fd7a:115c:a1e0::100"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
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{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[2]),
|
||||
User: new(users[2]),
|
||||
},
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
@@ -634,7 +633,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.100"),
|
||||
IPv6: ap("fd7a:115c:a1e0::100"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
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{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[2]),
|
||||
User: new(users[2]),
|
||||
},
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
@@ -711,7 +710,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.100"),
|
||||
IPv6: ap("fd7a:115c:a1e0::100"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
|
||||
},
|
||||
@@ -721,7 +720,7 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
},
|
||||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
@@ -778,13 +777,13 @@ func TestReduceFilterRules(t *testing.T) {
|
||||
node: &types.Node{
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[3]),
|
||||
User: new(users[3]),
|
||||
},
|
||||
peers: types.Nodes{
|
||||
&types.Node{
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{p("172.16.0.0/24"), p("10.10.11.0/24"), p("10.10.12.0/24")},
|
||||
},
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestNodeCanApproveRoute(t *testing.T) {
|
||||
@@ -25,24 +24,24 @@ func TestNodeCanApproveRoute(t *testing.T) {
|
||||
ID: 1,
|
||||
Hostname: "user1-device",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
UserID: ptr.To(uint(1)),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: new(uint(1)),
|
||||
User: new(users[0]),
|
||||
}
|
||||
|
||||
exitNode := types.Node{
|
||||
ID: 2,
|
||||
Hostname: "user2-device",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
UserID: ptr.To(uint(2)),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(uint(2)),
|
||||
User: new(users[1]),
|
||||
}
|
||||
|
||||
taggedNode := types.Node{
|
||||
ID: 3,
|
||||
Hostname: "tagged-server",
|
||||
IPv4: ap("100.64.0.3"),
|
||||
UserID: ptr.To(uint(3)),
|
||||
User: ptr.To(users[2]),
|
||||
UserID: new(uint(3)),
|
||||
User: new(users[2]),
|
||||
Tags: []string{"tag:router"},
|
||||
}
|
||||
|
||||
@@ -50,8 +49,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
|
||||
ID: 4,
|
||||
Hostname: "multi-tag-node",
|
||||
IPv4: ap("100.64.0.4"),
|
||||
UserID: ptr.To(uint(2)),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(uint(2)),
|
||||
User: new(users[1]),
|
||||
Tags: []string{"tag:router", "tag:server"},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
// aliasWithPorts creates an AliasWithPorts structure from an alias and ports.
|
||||
@@ -411,15 +410,15 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
||||
nodeTaggedServer := types.Node{
|
||||
Hostname: "tagged-server",
|
||||
IPv4: createAddr("100.64.0.1"),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
User: new(users[0]),
|
||||
Tags: []string{"tag:server"},
|
||||
}
|
||||
nodeTaggedDB := types.Node{
|
||||
Hostname: "tagged-db",
|
||||
IPv4: createAddr("100.64.0.2"),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
User: new(users[1]),
|
||||
Tags: []string{"tag:database"},
|
||||
}
|
||||
// Add untagged node for user2 - this will be the SSH source
|
||||
@@ -427,8 +426,8 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
|
||||
nodeUser2Untagged := types.Node{
|
||||
Hostname: "user2-device",
|
||||
IPv4: createAddr("100.64.0.3"),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
User: new(users[1]),
|
||||
}
|
||||
|
||||
nodes := types.Nodes{&nodeTaggedServer, &nodeTaggedDB, &nodeUser2Untagged}
|
||||
@@ -658,15 +657,15 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
|
||||
nodeTaggedServer := types.Node{
|
||||
Hostname: "tagged-server",
|
||||
IPv4: createAddr("100.64.0.1"),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
User: new(users[0]),
|
||||
Tags: []string{"tag:server"},
|
||||
}
|
||||
nodeUser2 := types.Node{
|
||||
Hostname: "user2-device",
|
||||
IPv4: createAddr("100.64.0.2"),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
User: new(users[1]),
|
||||
}
|
||||
|
||||
nodes := types.Nodes{&nodeTaggedServer, &nodeUser2}
|
||||
@@ -723,15 +722,15 @@ func TestSSHIntegrationReproduction(t *testing.T) {
|
||||
node1 := &types.Node{
|
||||
Hostname: "user1-node",
|
||||
IPv4: createAddr("100.64.0.1"),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
User: new(users[0]),
|
||||
}
|
||||
|
||||
node2 := &types.Node{
|
||||
Hostname: "user2-node",
|
||||
IPv4: createAddr("100.64.0.2"),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
User: new(users[1]),
|
||||
}
|
||||
|
||||
nodes := types.Nodes{node1, node2}
|
||||
@@ -848,19 +847,19 @@ func TestCompileFilterRulesForNodeWithAutogroupSelf(t *testing.T) {
|
||||
|
||||
nodes := types.Nodes{
|
||||
{
|
||||
User: ptr.To(users[0]),
|
||||
User: new(users[0]),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(users[0]),
|
||||
User: new(users[0]),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
IPv4: ap("100.64.0.3"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
IPv4: ap("100.64.0.4"),
|
||||
},
|
||||
// Tagged device for user1
|
||||
@@ -983,11 +982,11 @@ func TestTagUserMutualExclusivity(t *testing.T) {
|
||||
nodes := types.Nodes{
|
||||
// User-owned nodes
|
||||
{
|
||||
User: ptr.To(users[0]),
|
||||
User: new(users[0]),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
},
|
||||
// Tagged nodes
|
||||
@@ -1005,8 +1004,8 @@ func TestTagUserMutualExclusivity(t *testing.T) {
|
||||
|
||||
policy := &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:server"): Owners{new(Username("user1@"))},
|
||||
Tag("tag:database"): Owners{new(Username("user2@"))},
|
||||
},
|
||||
ACLs: []ACL{
|
||||
// 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{
|
||||
// User-owned nodes (not tagged)
|
||||
{
|
||||
User: ptr.To(users[0]),
|
||||
User: new(users[0]),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(users[1]),
|
||||
User: new(users[1]),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
},
|
||||
// Tagged nodes
|
||||
@@ -1128,10 +1127,10 @@ func TestAutogroupTagged(t *testing.T) {
|
||||
|
||||
policy := &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:web"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:server"): Owners{new(Username("user1@"))},
|
||||
Tag("tag:database"): Owners{new(Username("user2@"))},
|
||||
Tag("tag:web"): Owners{new(Username("user1@"))},
|
||||
Tag("tag:prod"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
ACLs: []ACL{
|
||||
// Rule: autogroup:tagged can reach user-owned nodes
|
||||
@@ -1252,10 +1251,10 @@ func TestAutogroupSelfWithSpecificUserSource(t *testing.T) {
|
||||
}
|
||||
|
||||
nodes := types.Nodes{
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.4")},
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
@@ -1320,11 +1319,11 @@ func TestAutogroupSelfWithGroupSource(t *testing.T) {
|
||||
}
|
||||
|
||||
nodes := types.Nodes{
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
|
||||
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.4")},
|
||||
{User: new(users[2]), IPv4: ap("100.64.0.5")},
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
@@ -1389,13 +1388,13 @@ func TestSSHWithAutogroupSelfInDestination(t *testing.T) {
|
||||
|
||||
nodes := types.Nodes{
|
||||
// User1's nodes
|
||||
{User: ptr.To(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.1"), Hostname: "user1-node1"},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"},
|
||||
// User2's nodes
|
||||
{User: ptr.To(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.3"), Hostname: "user2-node1"},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"},
|
||||
// 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{
|
||||
@@ -1470,10 +1469,10 @@ func TestSSHWithAutogroupSelfAndSpecificUser(t *testing.T) {
|
||||
}
|
||||
|
||||
nodes := types.Nodes{
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.4")},
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
@@ -1526,11 +1525,11 @@ func TestSSHWithAutogroupSelfAndGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
nodes := types.Nodes{
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
|
||||
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.1")},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.3")},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.4")},
|
||||
{User: new(users[2]), IPv4: ap("100.64.0.5")},
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
@@ -1585,10 +1584,10 @@ func TestSSHWithAutogroupSelfExcludesTaggedDevices(t *testing.T) {
|
||||
}
|
||||
|
||||
nodes := types.Nodes{
|
||||
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"},
|
||||
{User: ptr.To(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: 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.1"), Hostname: "untagged1"},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}},
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
@@ -1647,10 +1646,10 @@ func TestSSHWithAutogroupSelfAndMixedDestinations(t *testing.T) {
|
||||
}
|
||||
|
||||
nodes := types.Nodes{
|
||||
{User: ptr.To(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: ptr.To(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[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"},
|
||||
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"},
|
||||
{User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}},
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
@@ -1722,11 +1721,11 @@ func TestAutogroupSelfWithNonExistentUserInGroup(t *testing.T) {
|
||||
|
||||
nodes := types.Nodes{
|
||||
// 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
|
||||
{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
|
||||
{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
|
||||
{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"}},
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
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,
|
||||
IPv4: ap(ipv4),
|
||||
IPv6: ap(ipv6),
|
||||
User: ptr.To(user),
|
||||
UserID: ptr.To(user.ID),
|
||||
User: new(user),
|
||||
UserID: new(user.ID),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,8 +462,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
|
||||
Hostname: "test-1-device",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
@@ -474,8 +473,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
|
||||
Hostname: "test-2-router",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
Tags: []string{"tag:node-router"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -543,8 +542,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
|
||||
Hostname: "test-1-device",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
@@ -553,8 +552,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
|
||||
Hostname: "test-2-device",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
@@ -653,8 +652,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
|
||||
Hostname: "user1-node",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
Tags: []string{"tag:web", "tag:internal"},
|
||||
}
|
||||
|
||||
@@ -664,8 +663,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
|
||||
Hostname: "user2-node",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
}
|
||||
|
||||
initialNodes := types.Nodes{user1Node, user2Node}
|
||||
@@ -692,8 +691,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
|
||||
Hostname: "user1-node",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
Tags: []string{"tag:internal"}, // tag:web removed!
|
||||
}
|
||||
|
||||
@@ -755,8 +754,8 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
|
||||
Hostname: "admin-device",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
@@ -766,8 +765,8 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
|
||||
Hostname: "user1-server",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
Tags: []string{"tag:server"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -838,8 +837,8 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
|
||||
Hostname: "device-a",
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
|
||||
@@ -849,8 +848,8 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
|
||||
Hostname: "device-b",
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
Tags: []string{"tag:web"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -928,8 +927,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
|
||||
superadminDevice := &types.Node{
|
||||
ID: 1,
|
||||
Hostname: "superadmin-laptop",
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -937,8 +936,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
|
||||
adminDevice := &types.Node{
|
||||
ID: 2,
|
||||
Hostname: "admin-laptop",
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -946,8 +945,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
|
||||
directionDevice := &types.Node{
|
||||
ID: 3,
|
||||
Hostname: "direction-laptop",
|
||||
User: ptr.To(users[2]),
|
||||
UserID: ptr.To(users[2].ID),
|
||||
User: new(users[2]),
|
||||
UserID: new(users[2].ID),
|
||||
IPv4: ap("100.64.0.3"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -955,8 +954,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
|
||||
commonServer := &types.Node{
|
||||
ID: 4,
|
||||
Hostname: "common-server",
|
||||
User: ptr.To(users[3]),
|
||||
UserID: ptr.To(users[3].ID),
|
||||
User: new(users[3]),
|
||||
UserID: new(users[3].ID),
|
||||
IPv4: ap("100.64.0.4"),
|
||||
Tags: []string{"tag:common"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
@@ -965,8 +964,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
|
||||
techServer := &types.Node{
|
||||
ID: 5,
|
||||
Hostname: "tech-server",
|
||||
User: ptr.To(users[3]),
|
||||
UserID: ptr.To(users[3].ID),
|
||||
User: new(users[3]),
|
||||
UserID: new(users[3].ID),
|
||||
IPv4: ap("100.64.0.5"),
|
||||
Tags: []string{"tag:tech"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
@@ -975,8 +974,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
|
||||
privilegedServer := &types.Node{
|
||||
ID: 6,
|
||||
Hostname: "privileged-server",
|
||||
User: ptr.To(users[3]),
|
||||
UserID: ptr.To(users[3].ID),
|
||||
User: new(users[3]),
|
||||
UserID: new(users[3].ID),
|
||||
IPv4: ap("100.64.0.6"),
|
||||
Tags: []string{"tag:privileged"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
@@ -1088,8 +1087,8 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
|
||||
adminDevice := &types.Node{
|
||||
ID: 1,
|
||||
Hostname: "admin-laptop",
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -1099,8 +1098,8 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
|
||||
taggedServer := &types.Node{
|
||||
ID: 2,
|
||||
Hostname: "server",
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
Tags: []string{"tag:server"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
@@ -1157,8 +1156,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
|
||||
adminLaptop := &types.Node{
|
||||
ID: 1,
|
||||
Hostname: "admin-laptop",
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -1166,8 +1165,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
|
||||
adminPhone := &types.Node{
|
||||
ID: 2,
|
||||
Hostname: "admin-phone",
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
}
|
||||
@@ -1176,8 +1175,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
|
||||
webServer := &types.Node{
|
||||
ID: 3,
|
||||
Hostname: "web-server",
|
||||
User: ptr.To(users[1]),
|
||||
UserID: ptr.To(users[1].ID),
|
||||
User: new(users[1]),
|
||||
UserID: new(users[1].ID),
|
||||
IPv4: ap("100.64.0.3"),
|
||||
Tags: []string{"tag:web"},
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
@@ -1252,8 +1251,8 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
|
||||
node1 := &types.Node{
|
||||
ID: 1,
|
||||
Hostname: "node1",
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
IPv4: ap("100.64.0.1"),
|
||||
IPv6: ap("fd7a:115c:a1e0::1"),
|
||||
Hostinfo: &tailcfg.Hostinfo{},
|
||||
@@ -1263,8 +1262,8 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
|
||||
node2 := &types.Node{
|
||||
ID: 2,
|
||||
Hostname: "node2",
|
||||
User: ptr.To(users[0]),
|
||||
UserID: ptr.To(users[0].ID),
|
||||
User: new(users[0]),
|
||||
UserID: new(users[0].ID),
|
||||
IPv4: ap("100.64.0.2"),
|
||||
IPv6: ap("fd7a:115c:a1e0::2"),
|
||||
Tags: []string{"tag:admin"},
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"go4.org/netipx"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/multierr"
|
||||
"tailscale.com/util/slicesx"
|
||||
@@ -762,17 +761,17 @@ func parseAlias(vs string) (Alias, error) {
|
||||
case isWildcard(vs):
|
||||
return Wildcard, nil
|
||||
case isUser(vs):
|
||||
return ptr.To(Username(vs)), nil
|
||||
return new(Username(vs)), nil
|
||||
case isGroup(vs):
|
||||
return ptr.To(Group(vs)), nil
|
||||
return new(Group(vs)), nil
|
||||
case isTag(vs):
|
||||
return ptr.To(Tag(vs)), nil
|
||||
return new(Tag(vs)), nil
|
||||
case isAutoGroup(vs):
|
||||
return ptr.To(AutoGroup(vs)), nil
|
||||
return new(AutoGroup(vs)), nil
|
||||
}
|
||||
|
||||
if isHost(vs) {
|
||||
return ptr.To(Host(vs)), nil
|
||||
return new(Host(vs)), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %q", ErrInvalidAlias, vs)
|
||||
@@ -933,11 +932,11 @@ func (aa AutoApprovers) MarshalJSON() ([]byte, error) {
|
||||
func parseAutoApprover(s string) (AutoApprover, error) {
|
||||
switch {
|
||||
case isUser(s):
|
||||
return ptr.To(Username(s)), nil
|
||||
return new(Username(s)), nil
|
||||
case isGroup(s):
|
||||
return ptr.To(Group(s)), nil
|
||||
return new(Group(s)), nil
|
||||
case isTag(s):
|
||||
return ptr.To(Tag(s)), nil
|
||||
return new(Tag(s)), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %q", ErrInvalidAutoApprover, s)
|
||||
@@ -1027,11 +1026,11 @@ func (o Owners) MarshalJSON() ([]byte, error) {
|
||||
func parseOwner(s string) (Owner, error) {
|
||||
switch {
|
||||
case isUser(s):
|
||||
return ptr.To(Username(s)), nil
|
||||
return new(Username(s)), nil
|
||||
case isGroup(s):
|
||||
return ptr.To(Group(s)), nil
|
||||
return new(Group(s)), nil
|
||||
case isTag(s):
|
||||
return ptr.To(Tag(s)), nil
|
||||
return new(Tag(s)), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %q", ErrInvalidOwner, s)
|
||||
@@ -2299,8 +2298,7 @@ func unmarshalPolicy(b []byte) (*Policy, error) {
|
||||
ast.Standardize()
|
||||
|
||||
if err = json.Unmarshal(ast.Pack(), &policy, policyJSONOpts...); err != nil { //nolint:noinlineerr
|
||||
var serr *json.SemanticError
|
||||
if errors.As(err, &serr) && errors.Is(serr.Err, json.ErrUnknownName) {
|
||||
if serr, ok := errors.AsType[*json.SemanticError](err); ok && errors.Is(serr.Err, json.ErrUnknownName) {
|
||||
ptr := serr.JSONPointer
|
||||
name := ptr.LastToken()
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
// TestUnmarshalPolicy tests the unmarshalling of JSON into Policy objects and the marshalling
|
||||
@@ -53,11 +52,11 @@ func TestMarshalJSON(t *testing.T) {
|
||||
Action: "accept",
|
||||
Protocol: "tcp",
|
||||
Sources: Aliases{
|
||||
ptr.To(Username("user@example.com")),
|
||||
new(Username("user@example.com")),
|
||||
},
|
||||
Destinations: []AliasWithPorts{
|
||||
{
|
||||
Alias: ptr.To(Username("other@example.com")),
|
||||
Alias: new(Username("other@example.com")),
|
||||
Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
|
||||
},
|
||||
},
|
||||
@@ -254,11 +253,11 @@ func TestUnmarshalPolicy(t *testing.T) {
|
||||
Action: "accept",
|
||||
Protocol: "tcp",
|
||||
Sources: Aliases{
|
||||
ptr.To(Username("testuser@headscale.net")),
|
||||
new(Username("testuser@headscale.net")),
|
||||
},
|
||||
Destinations: []AliasWithPorts{
|
||||
{
|
||||
Alias: ptr.To(Username("otheruser@headscale.net")),
|
||||
Alias: new(Username("otheruser@headscale.net")),
|
||||
Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
|
||||
},
|
||||
},
|
||||
@@ -547,7 +546,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
||||
},
|
||||
Destinations: []AliasWithPorts{
|
||||
{
|
||||
Alias: ptr.To(AutoGroup("autogroup:internet")),
|
||||
Alias: new(AutoGroup("autogroup:internet")),
|
||||
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
|
||||
},
|
||||
},
|
||||
@@ -684,8 +683,8 @@ func TestUnmarshalPolicy(t *testing.T) {
|
||||
`,
|
||||
want: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:web"): Owners{ptr.To(Username("admin@example.com"))},
|
||||
Tag("tag:server"): Owners{ptr.To(Username("admin@example.com"))},
|
||||
Tag("tag:web"): Owners{new(Username("admin@example.com"))},
|
||||
Tag("tag:server"): Owners{new(Username("admin@example.com"))},
|
||||
},
|
||||
SSHs: []SSH{
|
||||
{
|
||||
@@ -1157,7 +1156,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
||||
},
|
||||
Destinations: []AliasWithPorts{
|
||||
{
|
||||
Alias: ptr.To(AutoGroup("autogroup:internet")),
|
||||
Alias: new(AutoGroup("autogroup:internet")),
|
||||
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
|
||||
},
|
||||
},
|
||||
@@ -1494,7 +1493,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
||||
want: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:bigbrother"): {},
|
||||
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
|
||||
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
|
||||
},
|
||||
ACLs: []ACL{
|
||||
{
|
||||
@@ -1505,7 +1504,7 @@ func TestUnmarshalPolicy(t *testing.T) {
|
||||
},
|
||||
Destinations: []AliasWithPorts{
|
||||
{
|
||||
Alias: ptr.To(Tag("tag:smallbrother")),
|
||||
Alias: new(Tag("tag:smallbrother")),
|
||||
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 up(s string) *Username { return ptr.To(Username(s)) }
|
||||
func hp(s string) *Host { return ptr.To(Host(s)) }
|
||||
func tp(s string) *Tag { return ptr.To(Tag(s)) }
|
||||
func agp(s string) *AutoGroup { return ptr.To(AutoGroup(s)) }
|
||||
func gp(s string) *Group { return new(Group(s)) }
|
||||
func up(s string) *Username { return new(Username(s)) }
|
||||
func hp(s string) *Host { return new(Host(s)) }
|
||||
func tp(s string) *Tag { return new(Tag(s)) }
|
||||
func agp(s string) *AutoGroup { return new(AutoGroup(s)) }
|
||||
func mp(pref string) netip.Prefix { return netip.MustParsePrefix(pref) }
|
||||
func ap(addr string) *netip.Addr { return ptr.To(netip.MustParseAddr(addr)) }
|
||||
func pp(pref string) *Prefix { return ptr.To(Prefix(mp(pref))) }
|
||||
func ap(addr string) *netip.Addr { return new(netip.MustParseAddr(addr)) }
|
||||
func pp(pref string) *Prefix { return new(Prefix(mp(pref))) }
|
||||
func p(pref string) Prefix { return Prefix(mp(pref)) }
|
||||
|
||||
func TestResolvePolicy(t *testing.T) {
|
||||
@@ -1882,31 +1881,31 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "username",
|
||||
toResolve: ptr.To(Username("testuser@")),
|
||||
toResolve: new(Username("testuser@")),
|
||||
nodes: types.Nodes{
|
||||
// Not matching other user
|
||||
{
|
||||
User: ptr.To(notme),
|
||||
User: new(notme),
|
||||
IPv4: ap("100.100.101.1"),
|
||||
},
|
||||
// Not matching forced tags
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:anything"},
|
||||
IPv4: ap("100.100.101.2"),
|
||||
},
|
||||
// not matching because it's tagged (tags copied from AuthKey)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"alsotagged"},
|
||||
IPv4: ap("100.100.101.3"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
IPv4: ap("100.100.101.103"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
IPv4: ap("100.100.101.104"),
|
||||
},
|
||||
},
|
||||
@@ -1914,31 +1913,31 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "group",
|
||||
toResolve: ptr.To(Group("group:testgroup")),
|
||||
toResolve: new(Group("group:testgroup")),
|
||||
nodes: types.Nodes{
|
||||
// Not matching other user
|
||||
{
|
||||
User: ptr.To(notme),
|
||||
User: new(notme),
|
||||
IPv4: ap("100.100.101.4"),
|
||||
},
|
||||
// Not matching forced tags
|
||||
{
|
||||
User: ptr.To(groupuser),
|
||||
User: new(groupuser),
|
||||
Tags: []string{"tag:anything"},
|
||||
IPv4: ap("100.100.101.5"),
|
||||
},
|
||||
// not matching because it's tagged (tags copied from AuthKey)
|
||||
{
|
||||
User: ptr.To(groupuser),
|
||||
User: new(groupuser),
|
||||
Tags: []string{"tag:alsotagged"},
|
||||
IPv4: ap("100.100.101.6"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(groupuser),
|
||||
User: new(groupuser),
|
||||
IPv4: ap("100.100.101.203"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(groupuser),
|
||||
User: new(groupuser),
|
||||
IPv4: ap("100.100.101.204"),
|
||||
},
|
||||
},
|
||||
@@ -1956,7 +1955,7 @@ func TestResolvePolicy(t *testing.T) {
|
||||
nodes: types.Nodes{
|
||||
// Not matching other user
|
||||
{
|
||||
User: ptr.To(notme),
|
||||
User: new(notme),
|
||||
IPv4: ap("100.100.101.9"),
|
||||
},
|
||||
// Not matching forced tags
|
||||
@@ -1992,7 +1991,7 @@ func TestResolvePolicy(t *testing.T) {
|
||||
pol: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:bigbrother"): {},
|
||||
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
|
||||
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
|
||||
},
|
||||
},
|
||||
nodes: types.Nodes{
|
||||
@@ -2015,7 +2014,7 @@ func TestResolvePolicy(t *testing.T) {
|
||||
pol: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:bigbrother"): {},
|
||||
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
|
||||
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
|
||||
},
|
||||
},
|
||||
nodes: types.Nodes{
|
||||
@@ -2050,14 +2049,14 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "multiple-groups",
|
||||
toResolve: ptr.To(Group("group:testgroup")),
|
||||
toResolve: new(Group("group:testgroup")),
|
||||
nodes: types.Nodes{
|
||||
{
|
||||
User: ptr.To(groupuser1),
|
||||
User: new(groupuser1),
|
||||
IPv4: ap("100.100.101.203"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(groupuser2),
|
||||
User: new(groupuser2),
|
||||
IPv4: ap("100.100.101.204"),
|
||||
},
|
||||
},
|
||||
@@ -2075,10 +2074,10 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid-username",
|
||||
toResolve: ptr.To(Username("invaliduser@")),
|
||||
toResolve: new(Username("invaliduser@")),
|
||||
nodes: types.Nodes{
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
IPv4: ap("100.100.101.103"),
|
||||
},
|
||||
},
|
||||
@@ -2106,47 +2105,47 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "autogroup-member-comprehensive",
|
||||
toResolve: ptr.To(AutoGroupMember),
|
||||
toResolve: new(AutoGroupMember),
|
||||
nodes: types.Nodes{
|
||||
// Node with no tags (should be included - is a member)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
IPv4: ap("100.100.101.1"),
|
||||
},
|
||||
// Node with single tag (should be excluded - tagged nodes are not members)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test"},
|
||||
IPv4: ap("100.100.101.2"),
|
||||
},
|
||||
// Node with multiple tags, all defined in policy (should be excluded)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test", "tag:other"},
|
||||
IPv4: ap("100.100.101.3"),
|
||||
},
|
||||
// Node with tag not defined in policy (should be excluded - still tagged)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:undefined"},
|
||||
IPv4: ap("100.100.101.4"),
|
||||
},
|
||||
// Node with mixed tags - some defined, some not (should be excluded)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test", "tag:undefined"},
|
||||
IPv4: ap("100.100.101.5"),
|
||||
},
|
||||
// Another untagged node from different user (should be included)
|
||||
{
|
||||
User: ptr.To(testuser2),
|
||||
User: new(testuser2),
|
||||
IPv4: ap("100.100.101.6"),
|
||||
},
|
||||
},
|
||||
pol: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
|
||||
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))},
|
||||
Tag("tag:test"): Owners{new(Username("testuser@"))},
|
||||
Tag("tag:other"): Owners{new(Username("testuser@"))},
|
||||
},
|
||||
},
|
||||
want: []netip.Prefix{
|
||||
@@ -2156,54 +2155,54 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "autogroup-tagged",
|
||||
toResolve: ptr.To(AutoGroupTagged),
|
||||
toResolve: new(AutoGroupTagged),
|
||||
nodes: types.Nodes{
|
||||
// Node with no tags (should be excluded - not tagged)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
IPv4: ap("100.100.101.1"),
|
||||
},
|
||||
// Node with single tag defined in policy (should be included)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test"},
|
||||
IPv4: ap("100.100.101.2"),
|
||||
},
|
||||
// Node with multiple tags, all defined in policy (should be included)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test", "tag:other"},
|
||||
IPv4: ap("100.100.101.3"),
|
||||
},
|
||||
// Node with tag not defined in policy (should be included - still tagged)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:undefined"},
|
||||
IPv4: ap("100.100.101.4"),
|
||||
},
|
||||
// Node with mixed tags - some defined, some not (should be included)
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test", "tag:undefined"},
|
||||
IPv4: ap("100.100.101.5"),
|
||||
},
|
||||
// Another untagged node from different user (should be excluded)
|
||||
{
|
||||
User: ptr.To(testuser2),
|
||||
User: new(testuser2),
|
||||
IPv4: ap("100.100.101.6"),
|
||||
},
|
||||
// Tagged node from different user (should be included)
|
||||
{
|
||||
User: ptr.To(testuser2),
|
||||
User: new(testuser2),
|
||||
Tags: []string{"tag:server"},
|
||||
IPv4: ap("100.100.101.7"),
|
||||
},
|
||||
},
|
||||
pol: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
|
||||
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))},
|
||||
Tag("tag:server"): Owners{ptr.To(Username("testuser2@"))},
|
||||
Tag("tag:test"): Owners{new(Username("testuser@"))},
|
||||
Tag("tag:other"): Owners{new(Username("testuser@"))},
|
||||
Tag("tag:server"): Owners{new(Username("testuser2@"))},
|
||||
},
|
||||
},
|
||||
want: []netip.Prefix{
|
||||
@@ -2214,37 +2213,37 @@ func TestResolvePolicy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "autogroup-self",
|
||||
toResolve: ptr.To(AutoGroupSelf),
|
||||
toResolve: new(AutoGroupSelf),
|
||||
nodes: types.Nodes{
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
IPv4: ap("100.100.101.1"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(testuser2),
|
||||
User: new(testuser2),
|
||||
IPv4: ap("100.100.101.2"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(testuser),
|
||||
User: new(testuser),
|
||||
Tags: []string{"tag:test"},
|
||||
IPv4: ap("100.100.101.3"),
|
||||
},
|
||||
{
|
||||
User: ptr.To(testuser2),
|
||||
User: new(testuser2),
|
||||
Tags: []string{"tag:test"},
|
||||
IPv4: ap("100.100.101.4"),
|
||||
},
|
||||
},
|
||||
pol: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
|
||||
Tag("tag:test"): Owners{new(Username("testuser@"))},
|
||||
},
|
||||
},
|
||||
wantErr: "autogroup:self requires per-node resolution",
|
||||
},
|
||||
{
|
||||
name: "autogroup-invalid",
|
||||
toResolve: ptr.To(AutoGroup("autogroup:invalid")),
|
||||
toResolve: new(AutoGroup("autogroup:invalid")),
|
||||
wantErr: "unknown autogroup",
|
||||
},
|
||||
}
|
||||
@@ -2323,7 +2322,7 @@ func TestResolveAutoApprovers(t *testing.T) {
|
||||
policy: &Policy{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
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{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
Routes: map[netip.Prefix]AutoApprovers{
|
||||
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
|
||||
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))},
|
||||
mp("10.0.0.0/24"): {new(Username("user1@"))},
|
||||
mp("10.0.1.0/24"): {new(Username("user2@"))},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2354,7 +2353,7 @@ func TestResolveAutoApprovers(t *testing.T) {
|
||||
name: "exit-node",
|
||||
policy: &Policy{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
|
||||
ExitNode: AutoApprovers{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
want: map[netip.Prefix]*netipx.IPSet{},
|
||||
@@ -2369,7 +2368,7 @@ func TestResolveAutoApprovers(t *testing.T) {
|
||||
},
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
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{
|
||||
TagOwners: TagOwners{
|
||||
"tag:testtag": Owners{
|
||||
ptr.To(Username("user1@")),
|
||||
ptr.To(Username("user2@")),
|
||||
new(Username("user1@")),
|
||||
new(Username("user2@")),
|
||||
},
|
||||
"tag:exittest": Owners{
|
||||
ptr.To(Group("group:exitgroup")),
|
||||
new(Group("group:exitgroup")),
|
||||
},
|
||||
},
|
||||
Groups: Groups{
|
||||
"group:exitgroup": Usernames{"user2@"},
|
||||
},
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
ExitNode: AutoApprovers{ptr.To(Tag("tag:exittest"))},
|
||||
ExitNode: AutoApprovers{new(Tag("tag:exittest"))},
|
||||
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{
|
||||
Routes: map[netip.Prefix]AutoApprovers{
|
||||
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
|
||||
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))},
|
||||
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
|
||||
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{
|
||||
@@ -2639,7 +2638,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
|
||||
policy: &Policy{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
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{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
Routes: map[netip.Prefix]AutoApprovers{
|
||||
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
|
||||
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))},
|
||||
mp("10.0.0.0/24"): {new(Username("user1@"))},
|
||||
mp("10.0.1.0/24"): {new(Username("user2@"))},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2665,7 +2664,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
|
||||
name: "exit-node-approval",
|
||||
policy: &Policy{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
|
||||
ExitNode: AutoApprovers{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0],
|
||||
@@ -2680,7 +2679,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
|
||||
},
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
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{
|
||||
Routes: map[netip.Prefix]AutoApprovers{
|
||||
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
|
||||
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))},
|
||||
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
|
||||
mp("10.0.1.0/24"): {new(Username("user3@"))},
|
||||
},
|
||||
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
|
||||
ExitNode: AutoApprovers{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0],
|
||||
@@ -2711,7 +2710,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
|
||||
policy: &Policy{
|
||||
AutoApprovers: AutoApproverPolicy{
|
||||
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",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:test"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
want: map[Tag]*netipx.IPSet{
|
||||
@@ -2781,7 +2780,7 @@ func TestResolveTagOwners(t *testing.T) {
|
||||
name: "multiple-tag-owners",
|
||||
policy: &Policy{
|
||||
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{
|
||||
@@ -2796,7 +2795,7 @@ func TestResolveTagOwners(t *testing.T) {
|
||||
"group:testgroup": Usernames{"user1@", "user2@"},
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
|
||||
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
|
||||
},
|
||||
},
|
||||
want: map[Tag]*netipx.IPSet{
|
||||
@@ -2808,8 +2807,8 @@ func TestResolveTagOwners(t *testing.T) {
|
||||
name: "tag-owns-tag",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:bigbrother"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))},
|
||||
Tag("tag:bigbrother"): Owners{new(Username("user1@"))},
|
||||
Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
|
||||
},
|
||||
},
|
||||
want: map[Tag]*netipx.IPSet{
|
||||
@@ -2871,7 +2870,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "single-tag-owner",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:test"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0],
|
||||
@@ -2882,7 +2881,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "multiple-tag-owners",
|
||||
policy: &Policy{
|
||||
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],
|
||||
@@ -2896,7 +2895,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
"group:testgroup": Usernames{"user1@", "user2@"},
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
|
||||
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
|
||||
},
|
||||
},
|
||||
node: nodes[1],
|
||||
@@ -2910,7 +2909,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
"group:testgroup": Usernames{"invalid"},
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
|
||||
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0],
|
||||
@@ -2922,7 +2921,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "node-cannot-have-tag",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:test"): Owners{new(Username("user2@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0],
|
||||
@@ -2933,7 +2932,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "node-with-unauthorized-tag-different-user",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:prod"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[2], // user3's node
|
||||
@@ -2944,8 +2943,8 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "node-with-multiple-tags-one-unauthorized",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:web"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:web"): Owners{new(Username("user1@"))},
|
||||
Tag("tag:database"): Owners{new(Username("user2@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0], // user1's node
|
||||
@@ -2965,7 +2964,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "tag-not-in-tagowners",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:prod"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: nodes[0],
|
||||
@@ -2978,13 +2977,13 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "node-without-ip-user-owns-tag",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:test"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
// No IPv4 or IPv6 - simulates new node registration
|
||||
User: &users[0],
|
||||
UserID: ptr.To(users[0].ID),
|
||||
UserID: new(users[0].ID),
|
||||
},
|
||||
tag: "tag:test",
|
||||
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",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:test"): Owners{new(Username("user2@"))},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
// No IPv4 or IPv6 - simulates new node registration
|
||||
User: &users[0], // user1, but tag owned by user2
|
||||
UserID: ptr.To(users[0].ID),
|
||||
UserID: new(users[0].ID),
|
||||
},
|
||||
tag: "tag:test",
|
||||
want: false, // user1 does not own tag:test
|
||||
@@ -3011,13 +3010,13 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
"group:admins": Usernames{"user1@", "user2@"},
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))},
|
||||
Tag("tag:admin"): Owners{new(Group("group:admins"))},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
// No IPv4 or IPv6 - simulates new node registration
|
||||
User: &users[1], // user2 is in group:admins
|
||||
UserID: ptr.To(users[1].ID),
|
||||
UserID: new(users[1].ID),
|
||||
},
|
||||
tag: "tag:admin",
|
||||
want: true, // Should succeed via group membership
|
||||
@@ -3029,13 +3028,13 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
"group:admins": Usernames{"user1@"},
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))},
|
||||
Tag("tag:admin"): Owners{new(Group("group:admins"))},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
// No IPv4 or IPv6 - simulates new node registration
|
||||
User: &users[1], // user2 is NOT in group:admins
|
||||
UserID: ptr.To(users[1].ID),
|
||||
UserID: new(users[1].ID),
|
||||
},
|
||||
tag: "tag:admin",
|
||||
want: false, // user2 is not in group:admins
|
||||
@@ -3044,7 +3043,7 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
name: "node-without-ip-no-user",
|
||||
policy: &Policy{
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:test"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
@@ -3061,14 +3060,14 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{
|
||||
ptr.To(Username("user1@")),
|
||||
ptr.To(Group("group:ops")),
|
||||
new(Username("user1@")),
|
||||
new(Group("group:ops")),
|
||||
},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
User: &users[0], // user1 directly owns the tag
|
||||
UserID: ptr.To(users[0].ID),
|
||||
UserID: new(users[0].ID),
|
||||
},
|
||||
tag: "tag:server",
|
||||
want: true,
|
||||
@@ -3081,14 +3080,14 @@ func TestNodeCanHaveTag(t *testing.T) {
|
||||
},
|
||||
TagOwners: TagOwners{
|
||||
Tag("tag:server"): Owners{
|
||||
ptr.To(Username("user1@")),
|
||||
ptr.To(Group("group:ops")),
|
||||
new(Username("user1@")),
|
||||
new(Group("group:ops")),
|
||||
},
|
||||
},
|
||||
},
|
||||
node: &types.Node{
|
||||
User: &users[2], // user3 is in group:ops
|
||||
UserID: ptr.To(users[2].ID),
|
||||
UserID: new(users[2].ID),
|
||||
},
|
||||
tag: "tag:server",
|
||||
want: true,
|
||||
@@ -3134,14 +3133,14 @@ func TestUserMatchesOwner(t *testing.T) {
|
||||
name: "username-match",
|
||||
policy: &Policy{},
|
||||
user: users[0],
|
||||
owner: ptr.To(Username("user1@")),
|
||||
owner: new(Username("user1@")),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "username-no-match",
|
||||
policy: &Policy{},
|
||||
user: users[0],
|
||||
owner: ptr.To(Username("user2@")),
|
||||
owner: new(Username("user2@")),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
@@ -3152,7 +3151,7 @@ func TestUserMatchesOwner(t *testing.T) {
|
||||
},
|
||||
},
|
||||
user: users[1], // user2 is in group:admins
|
||||
owner: ptr.To(Group("group:admins")),
|
||||
owner: new(Group("group:admins")),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
@@ -3163,7 +3162,7 @@ func TestUserMatchesOwner(t *testing.T) {
|
||||
},
|
||||
},
|
||||
user: users[1], // user2 is NOT in group:admins
|
||||
owner: ptr.To(Group("group:admins")),
|
||||
owner: new(Group("group:admins")),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
@@ -3172,7 +3171,7 @@ func TestUserMatchesOwner(t *testing.T) {
|
||||
Groups: Groups{},
|
||||
},
|
||||
user: users[0],
|
||||
owner: ptr.To(Group("group:undefined")),
|
||||
owner: new(Group("group:undefined")),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
@@ -3517,20 +3516,20 @@ func TestFlattenTagOwners(t *testing.T) {
|
||||
{
|
||||
name: "tag-owns-tag",
|
||||
input: TagOwners{
|
||||
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))},
|
||||
Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
|
||||
},
|
||||
want: TagOwners{
|
||||
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:smallbrother"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:smallbrother"): Owners{new(Group("group:user1"))},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "circular-reference",
|
||||
input: TagOwners{
|
||||
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))},
|
||||
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
|
||||
Tag("tag:a"): Owners{new(Tag("tag:b"))},
|
||||
Tag("tag:b"): Owners{new(Tag("tag:a"))},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: "circular reference detected: tag:a -> tag:b",
|
||||
@@ -3538,83 +3537,83 @@ func TestFlattenTagOwners(t *testing.T) {
|
||||
{
|
||||
name: "mixed-owners",
|
||||
input: TagOwners{
|
||||
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))},
|
||||
Tag("tag:y"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
|
||||
Tag("tag:y"): Owners{new(Username("user2@"))},
|
||||
},
|
||||
want: TagOwners{
|
||||
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
|
||||
Tag("tag:y"): Owners{ptr.To(Username("user2@"))},
|
||||
Tag("tag:x"): Owners{new(Username("user1@")), new(Username("user2@"))},
|
||||
Tag("tag:y"): Owners{new(Username("user2@"))},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "mixed-dupe-owners",
|
||||
input: TagOwners{
|
||||
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))},
|
||||
Tag("tag:y"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
|
||||
Tag("tag:y"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
want: TagOwners{
|
||||
Tag("tag:x"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:y"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:x"): Owners{new(Username("user1@"))},
|
||||
Tag("tag:y"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "no-tag-owners",
|
||||
input: TagOwners{
|
||||
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:solo"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
want: TagOwners{
|
||||
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))},
|
||||
Tag("tag:solo"): Owners{new(Username("user1@"))},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "tag-long-owner-chain",
|
||||
input: TagOwners{
|
||||
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
|
||||
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))},
|
||||
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))},
|
||||
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))},
|
||||
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))},
|
||||
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))},
|
||||
Tag("tag:a"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:b"): Owners{new(Tag("tag:a"))},
|
||||
Tag("tag:c"): Owners{new(Tag("tag:b"))},
|
||||
Tag("tag:d"): Owners{new(Tag("tag:c"))},
|
||||
Tag("tag:e"): Owners{new(Tag("tag:d"))},
|
||||
Tag("tag:f"): Owners{new(Tag("tag:e"))},
|
||||
Tag("tag:g"): Owners{new(Tag("tag:f"))},
|
||||
},
|
||||
want: TagOwners{
|
||||
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:b"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:c"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:d"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:e"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:f"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:g"): Owners{ptr.To(Group("group:user1"))},
|
||||
Tag("tag:a"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:b"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:c"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:d"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:e"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:f"): Owners{new(Group("group:user1"))},
|
||||
Tag("tag:g"): Owners{new(Group("group:user1"))},
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "tag-long-circular-chain",
|
||||
input: TagOwners{
|
||||
Tag("tag:a"): Owners{ptr.To(Tag("tag:g"))},
|
||||
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
|
||||
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))},
|
||||
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))},
|
||||
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))},
|
||||
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))},
|
||||
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))},
|
||||
Tag("tag:a"): Owners{new(Tag("tag:g"))},
|
||||
Tag("tag:b"): Owners{new(Tag("tag:a"))},
|
||||
Tag("tag:c"): Owners{new(Tag("tag:b"))},
|
||||
Tag("tag:d"): Owners{new(Tag("tag:c"))},
|
||||
Tag("tag:e"): Owners{new(Tag("tag:d"))},
|
||||
Tag("tag:f"): Owners{new(Tag("tag:e"))},
|
||||
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",
|
||||
},
|
||||
{
|
||||
name: "undefined-tag-reference",
|
||||
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"`,
|
||||
},
|
||||
{
|
||||
name: "tag-with-empty-owners-is-valid",
|
||||
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
|
||||
},
|
||||
want: TagOwners{
|
||||
|
||||
@@ -240,7 +240,7 @@ func (pr *PrimaryRoutes) PrimaryRoutes(id types.NodeID) []netip.Prefix {
|
||||
}
|
||||
}
|
||||
|
||||
tsaddr.SortPrefixes(routes)
|
||||
slices.SortFunc(routes, netip.Prefix.Compare)
|
||||
|
||||
return routes
|
||||
}
|
||||
@@ -300,7 +300,7 @@ func (pr *PrimaryRoutes) DebugJSON() DebugRoutes {
|
||||
// Populate available routes
|
||||
for nodeID, routes := range pr.routes {
|
||||
prefixes := routes.Slice()
|
||||
tsaddr.SortPrefixes(prefixes)
|
||||
slices.SortFunc(prefixes, netip.Prefix.Compare)
|
||||
debug.AvailableRoutes[nodeID] = prefixes
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
// TestEphemeralNodeDeleteWithConcurrentUpdate tests the race condition where UpdateNode and DeleteNode
|
||||
@@ -55,7 +54,7 @@ func TestEphemeralNodeDeleteWithConcurrentUpdate(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
updatedNode, updateOk = store.UpdateNode(node.ID, func(n *types.Node) {
|
||||
n.LastSeen = ptr.To(time.Now())
|
||||
n.LastSeen = new(time.Now())
|
||||
})
|
||||
|
||||
done <- true
|
||||
@@ -114,7 +113,7 @@ func TestUpdateNodeReturnsInvalidWhenDeletedInSameBatch(t *testing.T) {
|
||||
// Start UpdateNode in goroutine - it will queue and wait for batch
|
||||
go func() {
|
||||
node, ok := store.UpdateNode(node.ID, func(n *types.Node) {
|
||||
n.LastSeen = ptr.To(time.Now())
|
||||
n.LastSeen = new(time.Now())
|
||||
})
|
||||
resultChan <- struct {
|
||||
node types.NodeView
|
||||
@@ -165,7 +164,7 @@ func TestPersistNodeToDBPreventsRaceCondition(t *testing.T) {
|
||||
|
||||
// Simulate UpdateNode being called
|
||||
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, updatedNode.Valid(), "UpdateNode should return valid node")
|
||||
@@ -234,7 +233,7 @@ func TestEphemeralNodeLogoutRaceCondition(t *testing.T) {
|
||||
// Goroutine 1: UpdateNode (simulates UpdateNodeFromMapRequest)
|
||||
go func() {
|
||||
updatedNode, updateOk = store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
|
||||
n.LastSeen = ptr.To(time.Now())
|
||||
n.LastSeen = new(time.Now())
|
||||
})
|
||||
|
||||
done <- true
|
||||
@@ -310,7 +309,7 @@ func TestUpdateNodeFromMapRequestEphemeralLogoutSequence(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
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")
|
||||
n.Endpoints = []netip.AddrPort{endpoint}
|
||||
})
|
||||
@@ -380,7 +379,7 @@ func TestUpdateNodeDeletedInSameBatchReturnsInvalid(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
|
||||
n.LastSeen = ptr.To(time.Now())
|
||||
n.LastSeen = new(time.Now())
|
||||
})
|
||||
updateDone <- struct {
|
||||
node types.NodeView
|
||||
@@ -435,7 +434,7 @@ func TestPersistNodeToDBChecksNodeStoreBeforePersist(t *testing.T) {
|
||||
|
||||
// UpdateNode returns a 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, updatedNode.Valid(), "updated node should be valid")
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestSnapshotFromNodes(t *testing.T) {
|
||||
@@ -175,7 +174,7 @@ func createTestNode(nodeID types.NodeID, userID uint, username, hostname string)
|
||||
DiscoKey: discoKey.Public(),
|
||||
Hostname: hostname,
|
||||
GivenName: hostname,
|
||||
UserID: ptr.To(userID),
|
||||
UserID: new(userID),
|
||||
User: &types.User{
|
||||
Name: username,
|
||||
DisplayName: username,
|
||||
@@ -883,7 +882,7 @@ func createConcurrentTestNode(id types.NodeID, hostname string) types.Node {
|
||||
Hostname: hostname,
|
||||
MachineKey: machineKey.Public(),
|
||||
NodeKey: nodeKey.Public(),
|
||||
UserID: ptr.To(uint(1)),
|
||||
UserID: new(uint(1)),
|
||||
User: &types.User{
|
||||
Name: "concurrent-test-user",
|
||||
},
|
||||
|
||||
@@ -27,10 +27,8 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/ptr"
|
||||
"tailscale.com/types/views"
|
||||
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
|
||||
// This ensures we don't have stale online status from previous runs
|
||||
for _, node := range nodes {
|
||||
node.IsOnline = ptr.To(false)
|
||||
node.IsOnline = new(false)
|
||||
}
|
||||
|
||||
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.
|
||||
// now := time.Now()
|
||||
node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) {
|
||||
n.IsOnline = ptr.To(true)
|
||||
n.IsOnline = new(true)
|
||||
// n.LastSeen = ptr.To(now)
|
||||
})
|
||||
if !ok {
|
||||
@@ -507,9 +505,9 @@ func (s *State) Disconnect(id types.NodeID) ([]change.Change, error) {
|
||||
now := time.Now()
|
||||
|
||||
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.
|
||||
n.IsOnline = ptr.To(false)
|
||||
n.IsOnline = new(false)
|
||||
})
|
||||
|
||||
if !ok {
|
||||
@@ -805,7 +803,7 @@ func (s *State) BackfillNodeIPs() ([]string, error) {
|
||||
// Preserve online status and NetInfo when refreshing from database
|
||||
existingNode, exists := s.nodeStore.GetNode(node.ID)
|
||||
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
|
||||
// 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.IsOnline = ptr.To(false)
|
||||
node.LastSeen = ptr.To(time.Now())
|
||||
node.IsOnline = new(false)
|
||||
node.LastSeen = new(time.Now())
|
||||
|
||||
// Set RegisterMethod - for conversion this is the new method,
|
||||
// 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,
|
||||
Hostinfo: params.Hostinfo,
|
||||
Endpoints: params.Endpoints,
|
||||
LastSeen: ptr.To(time.Now()),
|
||||
LastSeen: new(time.Now()),
|
||||
RegisterMethod: params.RegisterMethod,
|
||||
Expiry: params.Expiry,
|
||||
}
|
||||
@@ -1822,8 +1820,8 @@ func (s *State) HandleNodeFromPreAuthKey(
|
||||
// Only update AuthKey reference
|
||||
node.AuthKey = pak
|
||||
node.AuthKeyID = &pak.ID
|
||||
node.IsOnline = ptr.To(false)
|
||||
node.LastSeen = ptr.To(time.Now())
|
||||
node.IsOnline = new(false)
|
||||
node.LastSeen = new(time.Now())
|
||||
|
||||
// Tagged nodes keep their existing expiry (disabled).
|
||||
// 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{}
|
||||
}
|
||||
|
||||
tsaddr.SortPrefixes(oldRoutes)
|
||||
tsaddr.SortPrefixes(newRoutes)
|
||||
slices.SortFunc(oldRoutes, netip.Prefix.Compare)
|
||||
slices.SortFunc(newRoutes, netip.Prefix.Compare)
|
||||
|
||||
return !slices.Equal(oldRoutes, newRoutes)
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ func NodeOnline(nodeID types.NodeID) Change {
|
||||
PeerPatches: []*tailcfg.PeerChange{
|
||||
{
|
||||
NodeID: nodeID.NodeID(),
|
||||
Online: ptrTo(true),
|
||||
Online: new(true),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -346,7 +346,7 @@ func NodeOffline(nodeID types.NodeID) Change {
|
||||
PeerPatches: []*tailcfg.PeerChange{
|
||||
{
|
||||
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.
|
||||
func ptrTo[T any](v T) *T {
|
||||
return &v
|
||||
return new(v)
|
||||
}
|
||||
|
||||
// High-level change constructors
|
||||
|
||||
@@ -16,8 +16,8 @@ func TestChange_FieldSync(t *testing.T) {
|
||||
typ := reflect.TypeFor[Change]()
|
||||
boolCount := 0
|
||||
|
||||
for i := range typ.NumField() {
|
||||
if typ.Field(i).Type.Kind() == reflect.Bool {
|
||||
for field := range typ.Fields() {
|
||||
if field.Type.Kind() == reflect.Bool {
|
||||
boolCount++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,8 +407,7 @@ func LoadConfig(path string, isFile bool) error {
|
||||
|
||||
err := viper.ReadInConfig()
|
||||
if err != nil {
|
||||
var configFileNotFoundError viper.ConfigFileNotFoundError
|
||||
if errors.As(err, &configFileNotFoundError) {
|
||||
if _, ok := errors.AsType[viper.ConfigFileNotFoundError](err); ok {
|
||||
log.Warn().Msg("no config file found, using defaults")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,7 @@ func (nv NodeView) TailNode(
|
||||
|
||||
primaryRoutes := primaryRouteFunc(nv.ID())
|
||||
allowedIPs := slices.Concat(nv.Prefixes(), primaryRoutes, nv.ExitRoutes())
|
||||
tsaddr.SortPrefixes(allowedIPs)
|
||||
slices.SortFunc(allowedIPs, netip.Prefix.Compare)
|
||||
|
||||
capMap := tailcfg.NodeCapMap{
|
||||
tailcfg.CapabilityAdmin: []tailcfg.RawMessage{},
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
// 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",
|
||||
node: Node{
|
||||
UserID: ptr.To(uint(42)),
|
||||
UserID: new(uint(42)),
|
||||
Tags: []string{},
|
||||
},
|
||||
want: false,
|
||||
@@ -112,7 +111,7 @@ func TestNodeViewIsTagged(t *testing.T) {
|
||||
{
|
||||
name: "user-owned node",
|
||||
node: Node{
|
||||
UserID: ptr.To(uint(1)),
|
||||
UserID: new(uint(1)),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
@@ -223,7 +222,7 @@ func TestNodeTagsImmutableAfterRegistration(t *testing.T) {
|
||||
// Test that a user-owned node is not tagged
|
||||
userNode := Node{
|
||||
ID: 2,
|
||||
UserID: ptr.To(uint(42)),
|
||||
UserID: new(uint(42)),
|
||||
Tags: []string{},
|
||||
RegisterMethod: util.RegisterMethodOIDC,
|
||||
}
|
||||
@@ -243,7 +242,7 @@ func TestNodeOwnershipModel(t *testing.T) {
|
||||
name: "tagged node has tags, UserID is informational",
|
||||
node: Node{
|
||||
ID: 1,
|
||||
UserID: ptr.To(uint(5)), // "created by" user 5
|
||||
UserID: new(uint(5)), // "created by" user 5
|
||||
Tags: []string{"tag:server"},
|
||||
},
|
||||
wantIsTagged: true,
|
||||
@@ -253,7 +252,7 @@ func TestNodeOwnershipModel(t *testing.T) {
|
||||
name: "user-owned node has no tags",
|
||||
node: Node{
|
||||
ID: 2,
|
||||
UserID: ptr.To(uint(5)),
|
||||
UserID: new(uint(5)),
|
||||
Tags: []string{},
|
||||
},
|
||||
wantIsTagged: false,
|
||||
@@ -265,7 +264,7 @@ func TestNodeOwnershipModel(t *testing.T) {
|
||||
name: "node with only authkey tags - not tagged (tags should be copied)",
|
||||
node: Node{
|
||||
ID: 3,
|
||||
UserID: ptr.To(uint(5)), // "created by" user 5
|
||||
UserID: new(uint(5)), // "created by" user 5
|
||||
AuthKey: &PreAuthKey{
|
||||
Tags: []string{"tag:database"},
|
||||
},
|
||||
|
||||
@@ -110,9 +110,7 @@ func TestCanUsePreAuthKey(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Errorf("expected error but got none")
|
||||
} else {
|
||||
var httpErr PAKError
|
||||
|
||||
ok := errors.As(err, &httpErr)
|
||||
httpErr, ok := errors.AsType[PAKError](err)
|
||||
if !ok {
|
||||
t.Errorf("expected HTTPError but got %T", err)
|
||||
} else {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
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 {
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
var veryLargeDestination = []policyv2.AliasWithPorts{
|
||||
@@ -1284,9 +1283,9 @@ func TestACLAutogroupMember(t *testing.T) {
|
||||
ACLs: []policyv2.ACL{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
|
||||
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
|
||||
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{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupTagged)},
|
||||
Sources: []policyv2.Alias{new(policyv2.AutoGroupTagged)},
|
||||
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{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
|
||||
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
|
||||
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{
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
|
||||
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
|
||||
Destinations: []policyv2.AliasWithPorts{
|
||||
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
|
||||
aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) {
|
||||
@@ -634,7 +633,7 @@ func TestAuthKeyLogoutAndReloginRoutesPreserved(t *testing.T) {
|
||||
},
|
||||
AutoApprovers: policyv2.AutoApproverPolicy{
|
||||
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"))},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -906,32 +905,32 @@ func wildcard() policyv2.Alias {
|
||||
// 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.
|
||||
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.
|
||||
// Used in ACL rules to reference specific hosts in network access policies.
|
||||
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.
|
||||
// Used in ACL rules to reference user groups in network access policies.
|
||||
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.
|
||||
// Used in ACL rules to reference node tags in network access policies.
|
||||
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.
|
||||
// Converts CIDR notation to policy prefix format for network range specifications.
|
||||
func prefixp(cidr string) policyv2.Alias {
|
||||
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.
|
||||
@@ -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.
|
||||
// Specifies which users can assign and manage specific tags in ACL configurations.
|
||||
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.
|
||||
@@ -955,25 +954,25 @@ func usernameOwner(name string) policyv2.Owner {
|
||||
//
|
||||
//nolint:unused
|
||||
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.
|
||||
// Specifies which users can automatically approve subnet route advertisements.
|
||||
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.
|
||||
// Specifies which groups can automatically approve subnet route advertisements.
|
||||
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.
|
||||
// Specifies which tagged nodes can automatically approve subnet route advertisements.
|
||||
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.
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
func isSSHNoAccessStdError(stderr string) bool {
|
||||
@@ -85,8 +84,8 @@ func TestSSHOneUserToAll(t *testing.T) {
|
||||
// Use autogroup:member and autogroup:tagged instead of wildcard
|
||||
// since wildcard (*) is no longer supported for SSH destinations
|
||||
Destinations: policyv2.SSHDstAliases{
|
||||
ptr.To(policyv2.AutoGroupMember),
|
||||
ptr.To(policyv2.AutoGroupTagged),
|
||||
new(policyv2.AutoGroupMember),
|
||||
new(policyv2.AutoGroupTagged),
|
||||
},
|
||||
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
|
||||
// to be that exact same user only. For group-to-group SSH access,
|
||||
// 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")},
|
||||
},
|
||||
},
|
||||
@@ -285,7 +284,7 @@ func TestSSHIsBlockedInACL(t *testing.T) {
|
||||
{
|
||||
Action: "accept",
|
||||
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")},
|
||||
},
|
||||
},
|
||||
@@ -340,13 +339,13 @@ func TestSSHUserOnlyIsolation(t *testing.T) {
|
||||
{
|
||||
Action: "accept",
|
||||
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")},
|
||||
},
|
||||
{
|
||||
Action: "accept",
|
||||
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")},
|
||||
},
|
||||
},
|
||||
@@ -522,10 +521,10 @@ func TestSSHAutogroupSelf(t *testing.T) {
|
||||
{
|
||||
Action: "accept",
|
||||
Sources: policyv2.SSHSrcAliases{
|
||||
ptr.To(policyv2.AutoGroupMember),
|
||||
new(policyv2.AutoGroupMember),
|
||||
},
|
||||
Destinations: policyv2.SSHDstAliases{
|
||||
ptr.To(policyv2.AutoGroupSelf),
|
||||
new(policyv2.AutoGroupSelf),
|
||||
},
|
||||
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
|
||||
},
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
const tagTestUser = "taguser"
|
||||
@@ -30,9 +29,9 @@ const tagTestUser = "taguser"
|
||||
func tagsTestPolicy() *policyv2.Policy {
|
||||
return &policyv2.Policy{
|
||||
TagOwners: policyv2.TagOwners{
|
||||
"tag:valid-owned": policyv2.Owners{ptr.To(policyv2.Username(tagTestUser + "@"))},
|
||||
"tag:second": policyv2.Owners{ptr.To(policyv2.Username(tagTestUser + "@"))},
|
||||
"tag:valid-unowned": policyv2.Owners{ptr.To(policyv2.Username("other-user@"))},
|
||||
"tag:valid-owned": policyv2.Owners{new(policyv2.Username(tagTestUser + "@"))},
|
||||
"tag:second": policyv2.Owners{new(policyv2.Username(tagTestUser + "@"))},
|
||||
"tag:valid-unowned": policyv2.Owners{new(policyv2.Username("other-user@"))},
|
||||
// Note: tag:nonexistent deliberately NOT defined
|
||||
},
|
||||
ACLs: []policyv2.ACL{
|
||||
|
||||
Reference in New Issue
Block a user