Remove support for non-noise clients (pre-1.32) (#1611)

This commit is contained in:
Kristoffer Dalby 2023-11-23 08:31:33 +01:00 committed by GitHub
parent b918aa03fc
commit a59aab2081
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 319 additions and 679 deletions

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -46,7 +46,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -23,8 +23,10 @@ after improving the test harness as part of adopting [#1460](https://github.com/
### BREAKING ### BREAKING
Code reorganisation, a lot of code has moved, please review the following PRs accordingly [#1473](https://github.com/juanfont/headscale/pull/1473) - Code reorganisation, a lot of code has moved, please review the following PRs accordingly [#1473](https://github.com/juanfont/headscale/pull/1473)
API: Machine is now Node [#1553](https://github.com/juanfont/headscale/pull/1553) - API: Machine is now Node [#1553](https://github.com/juanfont/headscale/pull/1553)
- Remove support for older Tailscale clients [#1611](https://github.com/juanfont/headscale/pull/1611)
- The latest supported client is 1.32
### Changes ### Changes

View file

@ -9,7 +9,7 @@ RUN go mod download
COPY . . COPY . .
RUN CGO_ENABLED=0 GOOS=linux go install -tags ts2019 -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale RUN CGO_ENABLED=0 GOOS=linux go install -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
RUN strip /go/bin/headscale RUN strip /go/bin/headscale
RUN test -e /go/bin/headscale RUN test -e /go/bin/headscale
@ -17,9 +17,9 @@ RUN test -e /go/bin/headscale
FROM docker.io/debian:bookworm-slim FROM docker.io/debian:bookworm-slim
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y ca-certificates \ && apt-get install -y ca-certificates \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& apt-get clean && apt-get clean
COPY --from=build /go/bin/headscale /bin/headscale COPY --from=build /go/bin/headscale /bin/headscale
ENV TZ UTC ENV TZ UTC

View file

@ -9,7 +9,7 @@ RUN go mod download
COPY . . COPY . .
RUN CGO_ENABLED=0 GOOS=linux go install -tags ts2019 -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale RUN CGO_ENABLED=0 GOOS=linux go install -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
RUN test -e /go/bin/headscale RUN test -e /go/bin/headscale
# Debug image # Debug image
@ -19,9 +19,9 @@ COPY --from=build /go/bin/headscale /bin/headscale
ENV TZ UTC ENV TZ UTC
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --yes less jq \ && apt-get install --no-install-recommends --yes less jq \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& apt-get clean && apt-get clean
RUN mkdir -p /var/run/headscale RUN mkdir -p /var/run/headscale
# Need to reset the entrypoint or everything will run as a busybox script # Need to reset the entrypoint or everything will run as a busybox script

View file

@ -10,8 +10,6 @@ ifeq ($(filter $(GOOS), openbsd netbsd soloaris plan9), )
else else
endif endif
TAGS = -tags ts2019
# GO_SOURCES = $(wildcard *.go) # GO_SOURCES = $(wildcard *.go)
# PROTO_SOURCES = $(wildcard **/*.proto) # PROTO_SOURCES = $(wildcard **/*.proto)
GO_SOURCES = $(call rwildcard,,*.go) GO_SOURCES = $(call rwildcard,,*.go)
@ -24,7 +22,7 @@ build:
dev: lint test build dev: lint test build
test: test:
gotestsum -- $(TAGS) -short -coverprofile=coverage.out ./... gotestsum -- -short -coverprofile=coverage.out ./...
test_integration: test_integration:
docker run \ docker run \
@ -34,7 +32,7 @@ test_integration:
-v $$PWD:$$PWD -w $$PWD/integration \ -v $$PWD:$$PWD -w $$PWD/integration \
-v /var/run/docker.sock:/var/run/docker.sock \ -v /var/run/docker.sock:/var/run/docker.sock \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- $(TAGS) -failfast ./... -timeout 120m -parallel 8 go run gotest.tools/gotestsum@latest -- -failfast ./... -timeout 120m -parallel 8
lint: lint:
golangci-lint run --fix --timeout 10m golangci-lint run --fix --timeout 10m

View file

@ -67,7 +67,6 @@ jobs:
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -67,7 +67,7 @@ var listAPIKeys = &cobra.Command{
} }
if output != "" { if output != "" {
SuccessOutput(response.ApiKeys, "", output) SuccessOutput(response.GetApiKeys(), "", output)
return return
} }
@ -75,11 +75,11 @@ var listAPIKeys = &cobra.Command{
tableData := pterm.TableData{ tableData := pterm.TableData{
{"ID", "Prefix", "Expiration", "Created"}, {"ID", "Prefix", "Expiration", "Created"},
} }
for _, key := range response.ApiKeys { for _, key := range response.GetApiKeys() {
expiration := "-" expiration := "-"
if key.GetExpiration() != nil { if key.GetExpiration() != nil {
expiration = ColourTime(key.Expiration.AsTime()) expiration = ColourTime(key.GetExpiration().AsTime())
} }
tableData = append(tableData, []string{ tableData = append(tableData, []string{
@ -155,7 +155,7 @@ If you loose a key, create a new one and revoke (expire) the old one.`,
return return
} }
SuccessOutput(response.ApiKey, response.ApiKey, output) SuccessOutput(response.GetApiKey(), response.GetApiKey(), output)
}, },
} }

View file

@ -135,6 +135,6 @@ var createNodeCmd = &cobra.Command{
return return
} }
SuccessOutput(response.Node, "Node created", output) SuccessOutput(response.GetNode(), "Node created", output)
}, },
} }

View file

@ -152,8 +152,8 @@ var registerNodeCmd = &cobra.Command{
} }
SuccessOutput( SuccessOutput(
response.Node, response.GetNode(),
fmt.Sprintf("Node %s registered", response.Node.GivenName), output) fmt.Sprintf("Node %s registered", response.GetNode().GetGivenName()), output)
}, },
} }
@ -196,12 +196,12 @@ var listNodesCmd = &cobra.Command{
} }
if output != "" { if output != "" {
SuccessOutput(response.Nodes, "", output) SuccessOutput(response.GetNodes(), "", output)
return return
} }
tableData, err := nodesToPtables(user, showTags, response.Nodes) tableData, err := nodesToPtables(user, showTags, response.GetNodes())
if err != nil { if err != nil {
ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output)
@ -262,7 +262,7 @@ var expireNodeCmd = &cobra.Command{
return return
} }
SuccessOutput(response.Node, "Node expired", output) SuccessOutput(response.GetNode(), "Node expired", output)
}, },
} }
@ -310,7 +310,7 @@ var renameNodeCmd = &cobra.Command{
return return
} }
SuccessOutput(response.Node, "Node renamed", output) SuccessOutput(response.GetNode(), "Node renamed", output)
}, },
} }
@ -364,7 +364,7 @@ var deleteNodeCmd = &cobra.Command{
prompt := &survey.Confirm{ prompt := &survey.Confirm{
Message: fmt.Sprintf( Message: fmt.Sprintf(
"Do you want to remove the node %s?", "Do you want to remove the node %s?",
getResponse.GetNode().Name, getResponse.GetNode().GetName(),
), ),
} }
err = survey.AskOne(prompt, &confirm) err = survey.AskOne(prompt, &confirm)
@ -473,7 +473,7 @@ var moveNodeCmd = &cobra.Command{
return return
} }
SuccessOutput(moveResponse.Node, "Node moved to another user", output) SuccessOutput(moveResponse.GetNode(), "Node moved to another user", output)
}, },
} }
@ -507,21 +507,21 @@ func nodesToPtables(
for _, node := range nodes { for _, node := range nodes {
var ephemeral bool var ephemeral bool
if node.PreAuthKey != nil && node.PreAuthKey.Ephemeral { if node.GetPreAuthKey() != nil && node.GetPreAuthKey().GetEphemeral() {
ephemeral = true ephemeral = true
} }
var lastSeen time.Time var lastSeen time.Time
var lastSeenTime string var lastSeenTime string
if node.LastSeen != nil { if node.GetLastSeen() != nil {
lastSeen = node.LastSeen.AsTime() lastSeen = node.GetLastSeen().AsTime()
lastSeenTime = lastSeen.Format("2006-01-02 15:04:05") lastSeenTime = lastSeen.Format("2006-01-02 15:04:05")
} }
var expiry time.Time var expiry time.Time
var expiryTime string var expiryTime string
if node.Expiry != nil { if node.GetExpiry() != nil {
expiry = node.Expiry.AsTime() expiry = node.GetExpiry().AsTime()
expiryTime = expiry.Format("2006-01-02 15:04:05") expiryTime = expiry.Format("2006-01-02 15:04:05")
} else { } else {
expiryTime = "N/A" expiryTime = "N/A"
@ -529,7 +529,7 @@ func nodesToPtables(
var machineKey key.MachinePublic var machineKey key.MachinePublic
err := machineKey.UnmarshalText( err := machineKey.UnmarshalText(
[]byte(node.MachineKey), []byte(node.GetMachineKey()),
) )
if err != nil { if err != nil {
machineKey = key.MachinePublic{} machineKey = key.MachinePublic{}
@ -537,14 +537,14 @@ func nodesToPtables(
var nodeKey key.NodePublic var nodeKey key.NodePublic
err = nodeKey.UnmarshalText( err = nodeKey.UnmarshalText(
[]byte(node.NodeKey), []byte(node.GetNodeKey()),
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
var online string var online string
if node.Online { if node.GetOnline() {
online = pterm.LightGreen("online") online = pterm.LightGreen("online")
} else { } else {
online = pterm.LightRed("offline") online = pterm.LightRed("offline")
@ -558,36 +558,36 @@ func nodesToPtables(
} }
var forcedTags string var forcedTags string
for _, tag := range node.ForcedTags { for _, tag := range node.GetForcedTags() {
forcedTags += "," + tag forcedTags += "," + tag
} }
forcedTags = strings.TrimLeft(forcedTags, ",") forcedTags = strings.TrimLeft(forcedTags, ",")
var invalidTags string var invalidTags string
for _, tag := range node.InvalidTags { for _, tag := range node.GetInvalidTags() {
if !contains(node.ForcedTags, tag) { if !contains(node.GetForcedTags(), tag) {
invalidTags += "," + pterm.LightRed(tag) invalidTags += "," + pterm.LightRed(tag)
} }
} }
invalidTags = strings.TrimLeft(invalidTags, ",") invalidTags = strings.TrimLeft(invalidTags, ",")
var validTags string var validTags string
for _, tag := range node.ValidTags { for _, tag := range node.GetValidTags() {
if !contains(node.ForcedTags, tag) { if !contains(node.GetForcedTags(), tag) {
validTags += "," + pterm.LightGreen(tag) validTags += "," + pterm.LightGreen(tag)
} }
} }
validTags = strings.TrimLeft(validTags, ",") validTags = strings.TrimLeft(validTags, ",")
var user string var user string
if currentUser == "" || (currentUser == node.User.Name) { if currentUser == "" || (currentUser == node.GetUser().GetName()) {
user = pterm.LightMagenta(node.User.Name) user = pterm.LightMagenta(node.GetUser().GetName())
} else { } else {
// Shared into this user // Shared into this user
user = pterm.LightYellow(node.User.Name) user = pterm.LightYellow(node.GetUser().GetName())
} }
var IPV4Address string var IPV4Address string
var IPV6Address string var IPV6Address string
for _, addr := range node.IpAddresses { for _, addr := range node.GetIpAddresses() {
if netip.MustParseAddr(addr).Is4() { if netip.MustParseAddr(addr).Is4() {
IPV4Address = addr IPV4Address = addr
} else { } else {
@ -596,8 +596,8 @@ func nodesToPtables(
} }
nodeData := []string{ nodeData := []string{
strconv.FormatUint(node.Id, util.Base10), strconv.FormatUint(node.GetId(), util.Base10),
node.Name, node.GetName(),
node.GetGivenName(), node.GetGivenName(),
machineKey.ShortString(), machineKey.ShortString(),
nodeKey.ShortString(), nodeKey.ShortString(),

View file

@ -84,7 +84,7 @@ var listPreAuthKeys = &cobra.Command{
} }
if output != "" { if output != "" {
SuccessOutput(response.PreAuthKeys, "", output) SuccessOutput(response.GetPreAuthKeys(), "", output)
return return
} }
@ -101,10 +101,10 @@ var listPreAuthKeys = &cobra.Command{
"Tags", "Tags",
}, },
} }
for _, key := range response.PreAuthKeys { for _, key := range response.GetPreAuthKeys() {
expiration := "-" expiration := "-"
if key.GetExpiration() != nil { if key.GetExpiration() != nil {
expiration = ColourTime(key.Expiration.AsTime()) expiration = ColourTime(key.GetExpiration().AsTime())
} }
var reusable string var reusable string
@ -116,7 +116,7 @@ var listPreAuthKeys = &cobra.Command{
aclTags := "" aclTags := ""
for _, tag := range key.AclTags { for _, tag := range key.GetAclTags() {
aclTags += "," + tag aclTags += "," + tag
} }
@ -214,7 +214,7 @@ var createPreAuthKeyCmd = &cobra.Command{
return return
} }
SuccessOutput(response.PreAuthKey, response.PreAuthKey.Key, output) SuccessOutput(response.GetPreAuthKey(), response.GetPreAuthKey().GetKey(), output)
}, },
} }

View file

@ -87,12 +87,12 @@ var listRoutesCmd = &cobra.Command{
} }
if output != "" { if output != "" {
SuccessOutput(response.Routes, "", output) SuccessOutput(response.GetRoutes(), "", output)
return return
} }
routes = response.Routes routes = response.GetRoutes()
} else { } else {
response, err := client.GetNodeRoutes(ctx, &v1.GetNodeRoutesRequest{ response, err := client.GetNodeRoutes(ctx, &v1.GetNodeRoutesRequest{
NodeId: machineID, NodeId: machineID,
@ -108,12 +108,12 @@ var listRoutesCmd = &cobra.Command{
} }
if output != "" { if output != "" {
SuccessOutput(response.Routes, "", output) SuccessOutput(response.GetRoutes(), "", output)
return return
} }
routes = response.Routes routes = response.GetRoutes()
} }
tableData := routesToPtables(routes) tableData := routesToPtables(routes)
@ -271,25 +271,25 @@ func routesToPtables(routes []*v1.Route) pterm.TableData {
for _, route := range routes { for _, route := range routes {
var isPrimaryStr string var isPrimaryStr string
prefix, err := netip.ParsePrefix(route.Prefix) prefix, err := netip.ParsePrefix(route.GetPrefix())
if err != nil { if err != nil {
log.Printf("Error parsing prefix %s: %s", route.Prefix, err) log.Printf("Error parsing prefix %s: %s", route.GetPrefix(), err)
continue continue
} }
if prefix == types.ExitRouteV4 || prefix == types.ExitRouteV6 { if prefix == types.ExitRouteV4 || prefix == types.ExitRouteV6 {
isPrimaryStr = "-" isPrimaryStr = "-"
} else { } else {
isPrimaryStr = strconv.FormatBool(route.IsPrimary) isPrimaryStr = strconv.FormatBool(route.GetIsPrimary())
} }
tableData = append(tableData, tableData = append(tableData,
[]string{ []string{
strconv.FormatUint(route.Id, Base10), strconv.FormatUint(route.GetId(), Base10),
route.Node.GivenName, route.GetNode().GetGivenName(),
route.Prefix, route.GetPrefix(),
strconv.FormatBool(route.Advertised), strconv.FormatBool(route.GetAdvertised()),
strconv.FormatBool(route.Enabled), strconv.FormatBool(route.GetEnabled()),
isPrimaryStr, isPrimaryStr,
}) })
} }

View file

@ -67,7 +67,7 @@ var createUserCmd = &cobra.Command{
return return
} }
SuccessOutput(response.User, "User created", output) SuccessOutput(response.GetUser(), "User created", output)
}, },
} }
@ -169,7 +169,7 @@ var listUsersCmd = &cobra.Command{
} }
if output != "" { if output != "" {
SuccessOutput(response.Users, "", output) SuccessOutput(response.GetUsers(), "", output)
return return
} }
@ -236,6 +236,6 @@ var renameUserCmd = &cobra.Command{
return return
} }
SuccessOutput(response.User, "User renamed", output) SuccessOutput(response.GetUser(), "User renamed", output)
}, },
} }

View file

@ -40,19 +40,12 @@ grpc_listen_addr: 127.0.0.1:50443
# are doing. # are doing.
grpc_allow_insecure: false grpc_allow_insecure: false
# Private key used to encrypt the traffic between headscale
# and Tailscale clients.
# The private key file will be autogenerated if it's missing.
#
private_key_path: /var/lib/headscale/private.key
# The Noise section includes specific configuration for the # The Noise section includes specific configuration for the
# TS2021 Noise protocol # TS2021 Noise protocol
noise: noise:
# The Noise private key is used to encrypt the # The Noise private key is used to encrypt the
# traffic between headscale and Tailscale clients when # traffic between headscale and Tailscale clients when
# using the new Noise-based protocol. It must be different # using the new Noise-based protocol.
# from the legacy private key.
private_key_path: /var/lib/headscale/noise_private.key private_key_path: /var/lib/headscale/noise_private.key
# List of IP prefixes to allocate tailaddresses from. # List of IP prefixes to allocate tailaddresses from.
@ -95,6 +88,12 @@ derp:
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/ # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
stun_listen_addr: "0.0.0.0:3478" stun_listen_addr: "0.0.0.0:3478"
# Private key used to encrypt the traffic between headscale DERP
# and Tailscale clients.
# The private key file will be autogenerated if it's missing.
#
private_key_path: /var/lib/headscale/derp_server_private.key
# List of externally available DERP maps encoded in JSON # List of externally available DERP maps encoded in JSON
urls: urls:
- https://controlplane.tailscale.com/derpmap/default - https://controlplane.tailscale.com/derpmap/default

View file

@ -26,8 +26,6 @@
version = headscaleVersion; version = headscaleVersion;
src = pkgs.lib.cleanSource self; src = pkgs.lib.cleanSource self;
tags = ["ts2019"];
# Only run unit tests when testing a build # Only run unit tests when testing a build
checkFlags = ["-short"]; checkFlags = ["-short"];
@ -129,7 +127,6 @@
buildInputs = devDeps; buildInputs = devDeps;
shellHook = '' shellHook = ''
export GOFLAGS=-tags="ts2019"
export PATH="$PWD/result/bin:$PATH" export PATH="$PWD/result/bin:$PATH"
mkdir -p ./ignored mkdir -p ./ignored

View file

@ -77,7 +77,6 @@ type Headscale struct {
dbString string dbString string
dbType string dbType string
dbDebug bool dbDebug bool
privateKey2019 *key.MachinePrivate
noisePrivateKey *key.MachinePrivate noisePrivateKey *key.MachinePrivate
DERPMap *tailcfg.DERPMap DERPMap *tailcfg.DERPMap
@ -101,21 +100,11 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
runtime.SetBlockProfileRate(1) runtime.SetBlockProfileRate(1)
} }
privateKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read or create private key: %w", err)
}
// TS2021 requires to have a different key from the legacy protocol.
noisePrivateKey, err := readOrCreatePrivateKey(cfg.NoisePrivateKeyPath) noisePrivateKey, err := readOrCreatePrivateKey(cfg.NoisePrivateKeyPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read or create Noise protocol private key: %w", err) return nil, fmt.Errorf("failed to read or create Noise protocol private key: %w", err)
} }
if privateKey.Equal(*noisePrivateKey) {
return nil, fmt.Errorf("private key and noise private key are the same: %w", err)
}
var dbString string var dbString string
switch cfg.DBtype { switch cfg.DBtype {
case db.Postgres: case db.Postgres:
@ -156,7 +145,6 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
cfg: cfg, cfg: cfg,
dbType: cfg.DBtype, dbType: cfg.DBtype,
dbString: dbString, dbString: dbString,
privateKey2019: privateKey,
noisePrivateKey: noisePrivateKey, noisePrivateKey: noisePrivateKey,
registrationCache: registrationCache, registrationCache: registrationCache,
pollNetMapStreamWG: sync.WaitGroup{}, pollNetMapStreamWG: sync.WaitGroup{},
@ -199,10 +187,18 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
} }
if cfg.DERP.ServerEnabled { if cfg.DERP.ServerEnabled {
// TODO(kradalby): replace this key with a dedicated DERP key. derpServerKey, err := readOrCreatePrivateKey(cfg.DERP.ServerPrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read or create DERP server private key: %w", err)
}
if derpServerKey.Equal(*noisePrivateKey) {
return nil, fmt.Errorf("DERP server private key and noise private key are the same: %w", err)
}
embeddedDERPServer, err := derpServer.NewDERPServer( embeddedDERPServer, err := derpServer.NewDERPServer(
cfg.ServerURL, cfg.ServerURL,
key.NodePrivate(*privateKey), key.NodePrivate(*derpServerKey),
&cfg.DERP, &cfg.DERP,
) )
if err != nil { if err != nil {
@ -450,7 +446,6 @@ func (h *Headscale) createRouter(grpcMux *grpcRuntime.ServeMux) *mux.Router {
router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet) router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet)
router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet) router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet)
router.HandleFunc("/register/{mkey}", h.RegisterWebAPI).Methods(http.MethodGet) router.HandleFunc("/register/{mkey}", h.RegisterWebAPI).Methods(http.MethodGet)
h.addLegacyHandlers(router)
router.HandleFunc("/oidc/register/{mkey}", h.RegisterOIDC).Methods(http.MethodGet) router.HandleFunc("/oidc/register/{mkey}", h.RegisterOIDC).Methods(http.MethodGet)
router.HandleFunc("/oidc/callback", h.OIDCCallback).Methods(http.MethodGet) router.HandleFunc("/oidc/callback", h.OIDCCallback).Methods(http.MethodGet)
@ -914,12 +909,6 @@ func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) {
var machineKey key.MachinePrivate var machineKey key.MachinePrivate
if err = machineKey.UnmarshalText([]byte(trimmedPrivateKey)); err != nil { if err = machineKey.UnmarshalText([]byte(trimmedPrivateKey)); err != nil {
log.Info().
Str("path", path).
Msg("This might be due to a legacy (headscale pre-0.12) private key. " +
"If the key is in WireGuard format, delete the key and restart headscale. " +
"A new key will automatically be generated. All Tailscale clients will have to be restarted")
return nil, fmt.Errorf("failed to parse private key: %w", err) return nil, fmt.Errorf("failed to parse private key: %w", err)
} }

View file

@ -1,13 +1,13 @@
package hscontrol package hscontrol
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
"time" "time"
"github.com/juanfont/headscale/hscontrol/mapper"
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -16,22 +16,19 @@ import (
"tailscale.com/types/key" "tailscale.com/types/key"
) )
// handleRegister is the common logic for registering a client in the legacy and Noise protocols // handleRegister is the logic for registering a client.
//
// When using Noise, the machineKey is Zero.
func (h *Headscale) handleRegister( func (h *Headscale) handleRegister(
writer http.ResponseWriter, writer http.ResponseWriter,
req *http.Request, req *http.Request,
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
now := time.Now().UTC() now := time.Now().UTC()
node, err := h.db.GetNodeByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey) node, err := h.db.GetNodeByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
// If the node has AuthKey set, handle registration via PreAuthKeys // If the node has AuthKey set, handle registration via PreAuthKeys
if registerRequest.Auth.AuthKey != "" { if registerRequest.Auth.AuthKey != "" {
h.handleAuthKey(writer, registerRequest, machineKey, isNoise) h.handleAuthKey(writer, registerRequest, machineKey)
return return
} }
@ -53,14 +50,13 @@ func (h *Headscale) handleRegister(
Str("node_key", registerRequest.NodeKey.ShortString()). Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()). Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup). Str("follow_up", registerRequest.Followup).
Bool("noise", isNoise).
Msg("Node is waiting for interactive login") Msg("Node is waiting for interactive login")
select { select {
case <-req.Context().Done(): case <-req.Context().Done():
return return
case <-time.After(registrationHoldoff): case <-time.After(registrationHoldoff):
h.handleNewNode(writer, registerRequest, machineKey, isNoise) h.handleNewNode(writer, registerRequest, machineKey)
return return
} }
@ -74,7 +70,6 @@ func (h *Headscale) handleRegister(
Str("node_key", registerRequest.NodeKey.ShortString()). Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()). Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup). Str("follow_up", registerRequest.Followup).
Bool("noise", isNoise).
Msg("New node not yet in the database") Msg("New node not yet in the database")
givenName, err := h.db.GenerateGivenName( givenName, err := h.db.GenerateGivenName(
@ -108,7 +103,6 @@ func (h *Headscale) handleRegister(
if !registerRequest.Expiry.IsZero() { if !registerRequest.Expiry.IsZero() {
log.Trace(). log.Trace().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Time("expiry", registerRequest.Expiry). Time("expiry", registerRequest.Expiry).
Msg("Non-zero expiry time requested") Msg("Non-zero expiry time requested")
@ -121,7 +115,7 @@ func (h *Headscale) handleRegister(
registerCacheExpiration, registerCacheExpiration,
) )
h.handleNewNode(writer, registerRequest, machineKey, isNoise) h.handleNewNode(writer, registerRequest, machineKey)
return return
} }
@ -157,7 +151,7 @@ func (h *Headscale) handleRegister(
// https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648 // https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
if !registerRequest.Expiry.IsZero() && if !registerRequest.Expiry.IsZero() &&
registerRequest.Expiry.UTC().Before(now) { registerRequest.Expiry.UTC().Before(now) {
h.handleNodeLogOut(writer, *node, machineKey, isNoise) h.handleNodeLogOut(writer, *node, machineKey)
return return
} }
@ -165,7 +159,7 @@ func (h *Headscale) handleRegister(
// If node is not expired, and it is register, we have a already accepted this node, // If node is not expired, and it is register, we have a already accepted this node,
// let it proceed with a valid registration // let it proceed with a valid registration
if !node.IsExpired() { if !node.IsExpired() {
h.handleNodeWithValidRegistration(writer, *node, machineKey, isNoise) h.handleNodeWithValidRegistration(writer, *node, machineKey)
return return
} }
@ -179,7 +173,6 @@ func (h *Headscale) handleRegister(
registerRequest, registerRequest,
*node, *node,
machineKey, machineKey,
isNoise,
) )
return return
@ -194,7 +187,7 @@ func (h *Headscale) handleRegister(
} }
// The node has expired or it is logged out // The node has expired or it is logged out
h.handleNodeExpiredOrLoggedOut(writer, registerRequest, *node, machineKey, isNoise) h.handleNodeExpiredOrLoggedOut(writer, registerRequest, *node, machineKey)
// TODO(juan): RegisterRequest includes an Expiry time, that we could optionally use // TODO(juan): RegisterRequest includes an Expiry time, that we could optionally use
node.Expiry = &time.Time{} node.Expiry = &time.Time{}
@ -215,7 +208,6 @@ func (h *Headscale) handleRegister(
} }
// handleAuthKey contains the logic to manage auth key client registration // handleAuthKey contains the logic to manage auth key client registration
// It is used both by the legacy and the new Noise protocol.
// When using Noise, the machineKey is Zero. // When using Noise, the machineKey is Zero.
// //
// TODO: check if any locks are needed around IP allocation. // TODO: check if any locks are needed around IP allocation.
@ -223,12 +215,10 @@ func (h *Headscale) handleAuthKey(
writer http.ResponseWriter, writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
log.Debug(). log.Debug().
Caller(). Caller().
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Bool("noise", isNoise).
Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname) Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
@ -236,17 +226,15 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
Msg("Failed authentication via AuthKey") Msg("Failed authentication via AuthKey")
resp.MachineAuthorized = false resp.MachineAuthorized = false
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
@ -263,14 +251,12 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
} }
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Msg("Failed authentication via AuthKey") Msg("Failed authentication via AuthKey")
@ -286,7 +272,6 @@ func (h *Headscale) handleAuthKey(
log.Debug(). log.Debug().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Msg("Authentication key was valid, proceeding to acquire IP addresses") Msg("Authentication key was valid, proceeding to acquire IP addresses")
@ -300,7 +285,6 @@ func (h *Headscale) handleAuthKey(
if node != nil { if node != nil {
log.Trace(). log.Trace().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("node was already registered before, refreshing with new auth key") Msg("node was already registered before, refreshing with new auth key")
@ -310,7 +294,6 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Err(err). Err(err).
Msg("Failed to refresh node") Msg("Failed to refresh node")
@ -318,7 +301,7 @@ func (h *Headscale) handleAuthKey(
return return
} }
aclTags := pak.Proto().AclTags aclTags := pak.Proto().GetAclTags()
if len(aclTags) > 0 { if len(aclTags) > 0 {
// This conditional preserves the existing behaviour, although SaaS would reset the tags on auth-key login // This conditional preserves the existing behaviour, although SaaS would reset the tags on auth-key login
err = h.db.SetTags(node, aclTags) err = h.db.SetTags(node, aclTags)
@ -326,7 +309,6 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Strs("aclTags", aclTags). Strs("aclTags", aclTags).
Err(err). Err(err).
@ -342,7 +324,6 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("func", "RegistrationHandler"). Str("func", "RegistrationHandler").
Str("hostinfo.name", registerRequest.Hostinfo.Hostname). Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
@ -361,7 +342,7 @@ func (h *Headscale) handleAuthKey(
NodeKey: nodeKey, NodeKey: nodeKey,
LastSeen: &now, LastSeen: &now,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
ForcedTags: pak.Proto().AclTags, ForcedTags: pak.Proto().GetAclTags(),
} }
node, err = h.db.RegisterNode( node, err = h.db.RegisterNode(
@ -370,7 +351,6 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("could not register node") Msg("could not register node")
nodeRegistrations.WithLabelValues("new", util.RegisterMethodAuthKey, "error", pak.User.Name). nodeRegistrations.WithLabelValues("new", util.RegisterMethodAuthKey, "error", pak.User.Name).
@ -385,7 +365,6 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to use pre-auth key") Msg("Failed to use pre-auth key")
nodeRegistrations.WithLabelValues("new", util.RegisterMethodAuthKey, "error", pak.User.Name). nodeRegistrations.WithLabelValues("new", util.RegisterMethodAuthKey, "error", pak.User.Name).
@ -401,11 +380,10 @@ func (h *Headscale) handleAuthKey(
// Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName* // Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName*
resp.Login = *pak.User.TailscaleLogin() resp.Login = *pak.User.TailscaleLogin()
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
@ -423,32 +401,29 @@ func (h *Headscale) handleAuthKey(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
} }
log.Info(). log.Info().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Str("ips", strings.Join(node.IPAddresses.StringSlice(), ", ")). Str("ips", strings.Join(node.IPAddresses.StringSlice(), ", ")).
Msg("Successfully authenticated via AuthKey") Msg("Successfully authenticated via AuthKey")
} }
// handleNewNode exposes for both legacy and Noise the functionality to get a URL // handleNewNode returns the authorisation URL to the client based on what type
// for authorizing the node. This url is then showed to the user by the local Tailscale client. // of registration headscale is configured with.
// This url is then showed to the user by the local Tailscale client.
func (h *Headscale) handleNewNode( func (h *Headscale) handleNewNode(
writer http.ResponseWriter, writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
// The node registration is new, redirect the client to the registration URL // The node registration is new, redirect the client to the registration URL
log.Debug(). log.Debug().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Msg("The node seems to be new, sending auth url") Msg("The node seems to be new, sending auth url")
@ -464,11 +439,10 @@ func (h *Headscale) handleNewNode(
machineKey.String()) machineKey.String())
} }
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError) http.Error(writer, "Internal server error", http.StatusInternalServerError)
@ -481,7 +455,6 @@ func (h *Headscale) handleNewNode(
_, err = writer.Write(respBody) _, err = writer.Write(respBody)
if err != nil { if err != nil {
log.Error(). log.Error().
Bool("noise", isNoise).
Caller(). Caller().
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
@ -489,7 +462,6 @@ func (h *Headscale) handleNewNode(
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Str("AuthURL", resp.AuthURL). Str("AuthURL", resp.AuthURL).
Str("node", registerRequest.Hostinfo.Hostname). Str("node", registerRequest.Hostinfo.Hostname).
Msg("Successfully sent auth url") Msg("Successfully sent auth url")
@ -499,12 +471,10 @@ func (h *Headscale) handleNodeLogOut(
writer http.ResponseWriter, writer http.ResponseWriter,
node types.Node, node types.Node,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
log.Info(). log.Info().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("Client requested logout") Msg("Client requested logout")
@ -513,7 +483,6 @@ func (h *Headscale) handleNodeLogOut(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to expire node") Msg("Failed to expire node")
http.Error(writer, "Internal server error", http.StatusInternalServerError) http.Error(writer, "Internal server error", http.StatusInternalServerError)
@ -525,11 +494,10 @@ func (h *Headscale) handleNodeLogOut(
resp.MachineAuthorized = false resp.MachineAuthorized = false
resp.NodeKeyExpired = true resp.NodeKeyExpired = true
resp.User = *node.User.TailscaleUser() resp.User = *node.User.TailscaleUser()
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError) http.Error(writer, "Internal server error", http.StatusInternalServerError)
@ -542,7 +510,6 @@ func (h *Headscale) handleNodeLogOut(
_, err = writer.Write(respBody) _, err = writer.Write(respBody)
if err != nil { if err != nil {
log.Error(). log.Error().
Bool("noise", isNoise).
Caller(). Caller().
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
@ -564,7 +531,6 @@ func (h *Headscale) handleNodeLogOut(
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("Successfully logged out") Msg("Successfully logged out")
} }
@ -573,14 +539,12 @@ func (h *Headscale) handleNodeWithValidRegistration(
writer http.ResponseWriter, writer http.ResponseWriter,
node types.Node, node types.Node,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
// The node registration is valid, respond with redirect to /map // The node registration is valid, respond with redirect to /map
log.Debug(). log.Debug().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("Client is registered and we have the current NodeKey. All clear to /map") Msg("Client is registered and we have the current NodeKey. All clear to /map")
@ -589,11 +553,10 @@ func (h *Headscale) handleNodeWithValidRegistration(
resp.User = *node.User.TailscaleUser() resp.User = *node.User.TailscaleUser()
resp.Login = *node.User.TailscaleLogin() resp.Login = *node.User.TailscaleLogin()
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
nodeRegistrations.WithLabelValues("update", "web", "error", node.User.Name). nodeRegistrations.WithLabelValues("update", "web", "error", node.User.Name).
@ -611,14 +574,12 @@ func (h *Headscale) handleNodeWithValidRegistration(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
} }
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("Node successfully authorized") Msg("Node successfully authorized")
} }
@ -628,13 +589,11 @@ func (h *Headscale) handleNodeKeyRefresh(
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
node types.Node, node types.Node,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("We have the OldNodeKey in the database. This is a key refresh") Msg("We have the OldNodeKey in the database. This is a key refresh")
@ -651,11 +610,10 @@ func (h *Headscale) handleNodeKeyRefresh(
resp.AuthURL = "" resp.AuthURL = ""
resp.User = *node.User.TailscaleUser() resp.User = *node.User.TailscaleUser()
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError) http.Error(writer, "Internal server error", http.StatusInternalServerError)
@ -669,14 +627,12 @@ func (h *Headscale) handleNodeKeyRefresh(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
} }
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Str("node_key", registerRequest.NodeKey.ShortString()). Str("node_key", registerRequest.NodeKey.ShortString()).
Str("old_node_key", registerRequest.OldNodeKey.ShortString()). Str("old_node_key", registerRequest.OldNodeKey.ShortString()).
Str("node", node.Hostname). Str("node", node.Hostname).
@ -688,12 +644,11 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
node types.Node, node types.Node,
machineKey key.MachinePublic, machineKey key.MachinePublic,
isNoise bool,
) { ) {
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
if registerRequest.Auth.AuthKey != "" { if registerRequest.Auth.AuthKey != "" {
h.handleAuthKey(writer, registerRequest, machineKey, isNoise) h.handleAuthKey(writer, registerRequest, machineKey)
return return
} }
@ -701,7 +656,6 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
// The client has registered before, but has expired or logged out // The client has registered before, but has expired or logged out
log.Trace(). log.Trace().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Str("machine_key", machineKey.ShortString()). Str("machine_key", machineKey.ShortString()).
Str("node_key", registerRequest.NodeKey.ShortString()). Str("node_key", registerRequest.NodeKey.ShortString()).
@ -718,11 +672,10 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
machineKey.String()) machineKey.String())
} }
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) respBody, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
nodeRegistrations.WithLabelValues("reauth", "web", "error", node.User.Name). nodeRegistrations.WithLabelValues("reauth", "web", "error", node.User.Name).
@ -740,14 +693,12 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Err(err). Err(err).
Msg("Failed to write response") Msg("Failed to write response")
} }
log.Trace(). log.Trace().
Caller(). Caller().
Bool("noise", isNoise).
Str("machine_key", machineKey.ShortString()). Str("machine_key", machineKey.ShortString()).
Str("node_key", registerRequest.NodeKey.ShortString()). Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()). Str("node_key_old", registerRequest.OldNodeKey.ShortString()).

View file

@ -1,61 +0,0 @@
//go:build ts2019
package hscontrol
import (
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
// RegistrationHandler handles the actual registration process of a machine
// Endpoint /machine/:mkey.
func (h *Headscale) RegistrationHandler(
writer http.ResponseWriter,
req *http.Request,
) {
vars := mux.Vars(req)
machineKeyStr, ok := vars["mkey"]
if !ok || machineKeyStr == "" {
log.Error().
Str("handler", "RegistrationHandler").
Msg("No machine ID in request")
http.Error(writer, "No machine ID in request", http.StatusBadRequest)
return
}
body, _ := io.ReadAll(req.Body)
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte("mkey:" + machineKeyStr))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse machine key")
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot parse machine key", http.StatusBadRequest)
return
}
registerRequest := tailcfg.RegisterRequest{}
err = util.DecodeAndUnmarshalNaCl(body, &registerRequest, &machineKey, h.privateKey2019)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot decode message")
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return
}
h.handleRegister(writer, req, registerRequest, machineKey, false)
}

View file

@ -39,7 +39,19 @@ func (ns *noiseServer) NoiseRegistrationHandler(
return return
} }
// Reject unsupported versions
if registerRequest.Version < MinimumCapVersion {
log.Info().
Caller().
Int("min_version", int(MinimumCapVersion)).
Int("client_version", int(registerRequest.Version)).
Msg("unsupported client connected")
http.Error(writer, "Internal error", http.StatusBadRequest)
return
}
ns.nodeKey = registerRequest.NodeKey ns.nodeKey = registerRequest.NodeKey
ns.headscale.handleRegister(writer, req, registerRequest, ns.conn.Peer(), true) ns.headscale.handleRegister(writer, req, registerRequest, ns.conn.Peer())
} }

View file

@ -198,5 +198,5 @@ func (*Suite) TestPreAuthKeyACLTags(c *check.C) {
listedPaks, err := db.ListPreAuthKeys("test8") listedPaks, err := db.ListPreAuthKeys("test8")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(listedPaks[0].Proto().AclTags, check.DeepEquals, tags) c.Assert(listedPaks[0].Proto().GetAclTags(), check.DeepEquals, tags)
} }

View file

@ -1,15 +0,0 @@
//go:build ts2019
package hscontrol
import (
"net/http"
"github.com/gorilla/mux"
)
func (h *Headscale) addLegacyHandlers(router *mux.Router) {
router.HandleFunc("/machine/{mkey}/map", h.PollNetMapHandler).
Methods(http.MethodPost)
router.HandleFunc("/machine/{mkey}", h.RegistrationHandler).Methods(http.MethodPost)
}

View file

@ -1,8 +0,0 @@
//go:build !ts2019
package hscontrol
import "github.com/gorilla/mux"
func (h *Headscale) addLegacyHandlers(router *mux.Router) {
}

View file

@ -8,7 +8,6 @@ import (
"html/template" "html/template"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -63,26 +62,6 @@ func (h *Headscale) KeyHandler(
// New Tailscale clients send a 'v' parameter to indicate the CurrentCapabilityVersion // New Tailscale clients send a 'v' parameter to indicate the CurrentCapabilityVersion
capVer, err := parseCabailityVersion(req) capVer, err := parseCabailityVersion(req)
if err != nil { if err != nil {
if errors.Is(err, ErrNoCapabilityVersion) {
log.Debug().
Str("handler", "/key").
Msg("New legacy client")
// Old clients don't send a 'v' parameter, so we send the legacy public key
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err := writer.Write(
[]byte(strings.TrimPrefix(h.privateKey2019.Public().String(), "mkey:")),
)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
return
}
log.Error(). log.Error().
Caller(). Caller().
Err(err). Err(err).
@ -101,7 +80,7 @@ func (h *Headscale) KeyHandler(
log.Debug(). log.Debug().
Str("handler", "/key"). Str("handler", "/key").
Int("v", int(capVer)). Int("cap_ver", int(capVer)).
Msg("New noise client") Msg("New noise client")
if err != nil { if err != nil {
writer.Header().Set("Content-Type", "text/plain; charset=utf-8") writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
@ -120,8 +99,7 @@ func (h *Headscale) KeyHandler(
// TS2021 (Tailscale v2 protocol) requires to have a different key // TS2021 (Tailscale v2 protocol) requires to have a different key
if capVer >= NoiseCapabilityVersion { if capVer >= NoiseCapabilityVersion {
resp := tailcfg.OverTLSPublicKeyResponse{ resp := tailcfg.OverTLSPublicKeyResponse{
LegacyPublicKey: h.privateKey2019.Public(), PublicKey: h.noisePrivateKey.Public(),
PublicKey: h.noisePrivateKey.Public(),
} }
writer.Header().Set("Content-Type", "application/json") writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK) writer.WriteHeader(http.StatusOK)

View file

@ -25,7 +25,6 @@ import (
"tailscale.com/smallzstd" "tailscale.com/smallzstd"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/dnstype" "tailscale.com/types/dnstype"
"tailscale.com/types/key"
) )
const ( const (
@ -48,10 +47,6 @@ var debugDumpMapResponsePath = envknob.String("HEADSCALE_DEBUG_DUMP_MAPRESPONSE_
// - Create a "minifier" that removes info not needed for the node // - Create a "minifier" that removes info not needed for the node
type Mapper struct { type Mapper struct {
privateKey2019 *key.MachinePrivate
isNoise bool
capVer tailcfg.CapabilityVersion
// Configuration // Configuration
// TODO(kradalby): figure out if this is the format we want this in // TODO(kradalby): figure out if this is the format we want this in
derpMap *tailcfg.DERPMap derpMap *tailcfg.DERPMap
@ -73,9 +68,6 @@ type Mapper struct {
func NewMapper( func NewMapper(
node *types.Node, node *types.Node,
peers types.Nodes, peers types.Nodes,
privateKey *key.MachinePrivate,
isNoise bool,
capVer tailcfg.CapabilityVersion,
derpMap *tailcfg.DERPMap, derpMap *tailcfg.DERPMap,
baseDomain string, baseDomain string,
dnsCfg *tailcfg.DNSConfig, dnsCfg *tailcfg.DNSConfig,
@ -84,17 +76,12 @@ func NewMapper(
) *Mapper { ) *Mapper {
log.Debug(). log.Debug().
Caller(). Caller().
Bool("noise", isNoise).
Str("node", node.Hostname). Str("node", node.Hostname).
Msg("creating new mapper") Msg("creating new mapper")
uid, _ := util.GenerateRandomStringDNSSafe(mapperIDLength) uid, _ := util.GenerateRandomStringDNSSafe(mapperIDLength)
return &Mapper{ return &Mapper{
privateKey2019: privateKey,
isNoise: isNoise,
capVer: capVer,
derpMap: derpMap, derpMap: derpMap,
baseDomain: baseDomain, baseDomain: baseDomain,
dnsCfg: dnsCfg, dnsCfg: dnsCfg,
@ -212,10 +199,11 @@ func addNextDNSMetadata(resolvers []*dnstype.Resolver, node *types.Node) {
func (m *Mapper) fullMapResponse( func (m *Mapper) fullMapResponse(
node *types.Node, node *types.Node,
pol *policy.ACLPolicy, pol *policy.ACLPolicy,
capVer tailcfg.CapabilityVersion,
) (*tailcfg.MapResponse, error) { ) (*tailcfg.MapResponse, error) {
peers := nodeMapToList(m.peers) peers := nodeMapToList(m.peers)
resp, err := m.baseWithConfigMapResponse(node, pol) resp, err := m.baseWithConfigMapResponse(node, pol, capVer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -224,7 +212,7 @@ func (m *Mapper) fullMapResponse(
resp, resp,
pol, pol,
node, node,
m.capVer, capVer,
peers, peers,
peers, peers,
m.baseDomain, m.baseDomain,
@ -247,15 +235,11 @@ func (m *Mapper) FullMapResponse(
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()
resp, err := m.fullMapResponse(node, pol) resp, err := m.fullMapResponse(node, pol, mapRequest.Version)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if m.isNoise {
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
}
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress) return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
} }
@ -267,15 +251,11 @@ func (m *Mapper) LiteMapResponse(
node *types.Node, node *types.Node,
pol *policy.ACLPolicy, pol *policy.ACLPolicy,
) ([]byte, error) { ) ([]byte, error) {
resp, err := m.baseWithConfigMapResponse(node, pol) resp, err := m.baseWithConfigMapResponse(node, pol, mapRequest.Version)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if m.isNoise {
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
}
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress) return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
} }
@ -325,7 +305,7 @@ func (m *Mapper) PeerChangedResponse(
&resp, &resp,
pol, pol,
node, node,
m.capVer, mapRequest.Version,
nodeMapToList(m.peers), nodeMapToList(m.peers),
changed, changed,
m.baseDomain, m.baseDomain,
@ -414,15 +394,8 @@ func (m *Mapper) marshalMapResponse(
var respBody []byte var respBody []byte
if compression == util.ZstdCompression { if compression == util.ZstdCompression {
respBody = zstdEncode(jsonBody) respBody = zstdEncode(jsonBody)
if !m.isNoise { // if legacy protocol
respBody = m.privateKey2019.SealTo(node.MachineKey, respBody)
}
} else { } else {
if !m.isNoise { // if legacy protocol respBody = jsonBody
respBody = m.privateKey2019.SealTo(node.MachineKey, jsonBody)
} else {
respBody = jsonBody
}
} }
data := make([]byte, reservedResponseHeaderSize) data := make([]byte, reservedResponseHeaderSize)
@ -432,32 +405,6 @@ func (m *Mapper) marshalMapResponse(
return data, nil return data, nil
} }
// MarshalResponse takes an Tailscale Response, marhsal it to JSON.
// If isNoise is set, then the JSON body will be returned
// If !isNoise and privateKey2019 is set, the JSON body will be sealed in a Nacl box.
func MarshalResponse(
resp interface{},
isNoise bool,
privateKey2019 *key.MachinePrivate,
machineKey key.MachinePublic,
) ([]byte, error) {
jsonBody, err := json.Marshal(resp)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot marshal response")
return nil, err
}
if !isNoise && privateKey2019 != nil {
return privateKey2019.SealTo(machineKey, jsonBody), nil
}
return jsonBody, nil
}
func zstdEncode(in []byte) []byte { func zstdEncode(in []byte) []byte {
encoder, ok := zstdEncoderPool.Get().(*zstd.Encoder) encoder, ok := zstdEncoderPool.Get().(*zstd.Encoder)
if !ok { if !ok {
@ -503,10 +450,11 @@ func (m *Mapper) baseMapResponse() tailcfg.MapResponse {
func (m *Mapper) baseWithConfigMapResponse( func (m *Mapper) baseWithConfigMapResponse(
node *types.Node, node *types.Node,
pol *policy.ACLPolicy, pol *policy.ACLPolicy,
capVer tailcfg.CapabilityVersion,
) (*tailcfg.MapResponse, error) { ) (*tailcfg.MapResponse, error) {
resp := m.baseMapResponse() resp := m.baseMapResponse()
tailnode, err := tailNode(node, m.capVer, pol, m.dnsCfg, m.baseDomain, m.randomClientPort) tailnode, err := tailNode(node, capVer, pol, m.dnsCfg, m.baseDomain, m.randomClientPort)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -472,9 +472,6 @@ func Test_fullMapResponse(t *testing.T) {
mappy := NewMapper( mappy := NewMapper(
tt.node, tt.node,
tt.peers, tt.peers,
nil,
false,
0,
tt.derpMap, tt.derpMap,
tt.baseDomain, tt.baseDomain,
tt.dnsConfig, tt.dnsConfig,
@ -485,6 +482,7 @@ func Test_fullMapResponse(t *testing.T) {
got, err := mappy.fullMapResponse( got, err := mappy.fullMapResponse(
tt.node, tt.node,
tt.pol, tt.pol,
0,
) )
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {

View file

@ -25,12 +25,10 @@ type UpdateNode func()
func logPollFunc( func logPollFunc(
mapRequest tailcfg.MapRequest, mapRequest tailcfg.MapRequest,
node *types.Node, node *types.Node,
isNoise bool,
) (func(string), func(error, string)) { ) (func(string), func(error, string)) {
return func(msg string) { return func(msg string) {
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Bool("readOnly", mapRequest.ReadOnly). Bool("readOnly", mapRequest.ReadOnly).
Bool("omitPeers", mapRequest.OmitPeers). Bool("omitPeers", mapRequest.OmitPeers).
Bool("stream", mapRequest.Stream). Bool("stream", mapRequest.Stream).
@ -41,7 +39,6 @@ func logPollFunc(
func(err error, msg string) { func(err error, msg string) {
log.Error(). log.Error().
Caller(). Caller().
Bool("noise", isNoise).
Bool("readOnly", mapRequest.ReadOnly). Bool("readOnly", mapRequest.ReadOnly).
Bool("omitPeers", mapRequest.OmitPeers). Bool("omitPeers", mapRequest.OmitPeers).
Bool("stream", mapRequest.Stream). Bool("stream", mapRequest.Stream).
@ -52,8 +49,8 @@ func logPollFunc(
} }
} }
// handlePoll is the common code for the legacy and Noise protocols to // handlePoll ensures the node gets the appropriate updates from either
// managed the poll loop. // polling or immediate responses.
// //
//nolint:gocyclo //nolint:gocyclo
func (h *Headscale) handlePoll( func (h *Headscale) handlePoll(
@ -61,10 +58,8 @@ func (h *Headscale) handlePoll(
ctx context.Context, ctx context.Context,
node *types.Node, node *types.Node,
mapRequest tailcfg.MapRequest, mapRequest tailcfg.MapRequest,
isNoise bool,
capVer tailcfg.CapabilityVersion,
) { ) {
logInfo, logErr := logPollFunc(mapRequest, node, isNoise) logInfo, logErr := logPollFunc(mapRequest, node)
// This is the mechanism where the node gives us inforamtion about its // This is the mechanism where the node gives us inforamtion about its
// current configuration. // current configuration.
@ -77,12 +72,12 @@ func (h *Headscale) handlePoll(
if mapRequest.OmitPeers && !mapRequest.Stream && !mapRequest.ReadOnly { if mapRequest.OmitPeers && !mapRequest.Stream && !mapRequest.ReadOnly {
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Bool("readOnly", mapRequest.ReadOnly). Bool("readOnly", mapRequest.ReadOnly).
Bool("omitPeers", mapRequest.OmitPeers). Bool("omitPeers", mapRequest.OmitPeers).
Bool("stream", mapRequest.Stream). Bool("stream", mapRequest.Stream).
Str("node_key", node.NodeKey.ShortString()). Str("node_key", node.NodeKey.ShortString()).
Str("node", node.Hostname). Str("node", node.Hostname).
Int("cap_ver", int(mapRequest.Version)).
Msg("Received endpoint update") Msg("Received endpoint update")
now := time.Now().UTC() now := time.Now().UTC()
@ -129,7 +124,7 @@ func (h *Headscale) handlePoll(
// The intended use is for clients to discover the DERP map at // The intended use is for clients to discover the DERP map at
// start-up before their first real endpoint update. // start-up before their first real endpoint update.
} else if mapRequest.OmitPeers && !mapRequest.Stream && mapRequest.ReadOnly { } else if mapRequest.OmitPeers && !mapRequest.Stream && mapRequest.ReadOnly {
h.handleLiteRequest(writer, node, mapRequest, isNoise, capVer) h.handleLiteRequest(writer, node, mapRequest)
return return
} else if mapRequest.OmitPeers && mapRequest.Stream { } else if mapRequest.OmitPeers && mapRequest.Stream {
@ -160,9 +155,6 @@ func (h *Headscale) handlePoll(
mapp := mapper.NewMapper( mapp := mapper.NewMapper(
node, node,
peers, peers,
h.privateKey2019,
isNoise,
capVer,
h.DERPMap, h.DERPMap,
h.cfg.BaseDomain, h.cfg.BaseDomain,
h.cfg.DNSConfig, h.cfg.DNSConfig,
@ -337,7 +329,6 @@ func (h *Headscale) handlePoll(
log.Info(). log.Info().
Caller(). Caller().
Bool("noise", isNoise).
Bool("readOnly", mapRequest.ReadOnly). Bool("readOnly", mapRequest.ReadOnly).
Bool("omitPeers", mapRequest.OmitPeers). Bool("omitPeers", mapRequest.OmitPeers).
Bool("stream", mapRequest.Stream). Bool("stream", mapRequest.Stream).
@ -382,19 +373,14 @@ func (h *Headscale) handleLiteRequest(
writer http.ResponseWriter, writer http.ResponseWriter,
node *types.Node, node *types.Node,
mapRequest tailcfg.MapRequest, mapRequest tailcfg.MapRequest,
isNoise bool,
capVer tailcfg.CapabilityVersion,
) { ) {
logInfo, logErr := logPollFunc(mapRequest, node, isNoise) logInfo, logErr := logPollFunc(mapRequest, node)
mapp := mapper.NewMapper( mapp := mapper.NewMapper(
node, node,
// TODO(kradalby): It might not be acceptable to send // TODO(kradalby): It might not be acceptable to send
// an empty peer list here. // an empty peer list here.
types.Nodes{}, types.Nodes{},
h.privateKey2019,
isNoise,
capVer,
h.DERPMap, h.DERPMap,
h.cfg.BaseDomain, h.cfg.BaseDomain,
h.cfg.DNSConfig, h.cfg.DNSConfig,

View file

@ -1,108 +0,0 @@
//go:build ts2019
package hscontrol
import (
"errors"
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
// PollNetMapHandler takes care of /machine/:id/map
//
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
// the clients when something in the network changes.
//
// The clients POST stuff like HostInfo and their Endpoints here, but
// only after their first request (marked with the ReadOnly field).
//
// At this moment the updates are sent in a quite horrendous way, but they kinda work.
func (h *Headscale) PollNetMapHandler(
writer http.ResponseWriter,
req *http.Request,
) {
vars := mux.Vars(req)
machineKeyStr, ok := vars["mkey"]
if !ok || machineKeyStr == "" {
log.Error().
Str("handler", "PollNetMap").
Msg("No machine key in request")
http.Error(writer, "No machine key in request", http.StatusBadRequest)
return
}
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Msg("PollNetMapHandler called")
body, _ := io.ReadAll(req.Body)
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte("mkey:" + machineKeyStr))
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Err(err).
Msg("Cannot parse client key")
http.Error(writer, "Cannot parse client key", http.StatusBadRequest)
return
}
mapRequest := tailcfg.MapRequest{}
err = util.DecodeAndUnmarshalNaCl(body, &mapRequest, &machineKey, h.privateKey2019)
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Err(err).
Msg("Cannot decode message")
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return
}
node, err := h.db.GetNodeByMachineKey(machineKey)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().
Str("handler", "PollNetMap").
Msgf("Ignoring request, cannot find node with key %s", machineKey.String())
http.Error(writer, "", http.StatusUnauthorized)
return
}
log.Error().
Str("handler", "PollNetMap").
Msgf("Failed to fetch node from the database with Machine key: %s", machineKey.String())
http.Error(writer, "", http.StatusInternalServerError)
return
}
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Str("node", node.Hostname).
Msg("A node is sending a MapRequest via legacy protocol")
capVer, err := parseCabailityVersion(req)
if err != nil && !errors.Is(err, ErrNoCapabilityVersion) {
log.Error().
Caller().
Err(err).
Msg("failed to parse capVer")
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
h.handlePoll(writer, req.Context(), node, mapRequest, false, capVer)
}

View file

@ -12,6 +12,10 @@ import (
"tailscale.com/types/key" "tailscale.com/types/key"
) )
const (
MinimumCapVersion tailcfg.CapabilityVersion = 36
)
// NoisePollNetMapHandler takes care of /machine/:id/map using the Noise protocol // NoisePollNetMapHandler takes care of /machine/:id/map using the Noise protocol
// //
// This is the busiest endpoint, as it keeps the HTTP long poll that updates // This is the busiest endpoint, as it keeps the HTTP long poll that updates
@ -47,6 +51,18 @@ func (ns *noiseServer) NoisePollNetMapHandler(
return return
} }
// Reject unsupported versions
if mapRequest.Version < MinimumCapVersion {
log.Info().
Caller().
Int("min_version", int(MinimumCapVersion)).
Int("client_version", int(mapRequest.Version)).
Msg("unsupported client connected")
http.Error(writer, "Internal error", http.StatusBadRequest)
return
}
ns.nodeKey = mapRequest.NodeKey ns.nodeKey = mapRequest.NodeKey
node, err := ns.headscale.db.GetNodeByAnyKey( node, err := ns.headscale.db.GetNodeByAnyKey(
@ -73,20 +89,8 @@ func (ns *noiseServer) NoisePollNetMapHandler(
log.Debug(). log.Debug().
Str("handler", "NoisePollNetMap"). Str("handler", "NoisePollNetMap").
Str("node", node.Hostname). Str("node", node.Hostname).
Int("cap_ver", int(mapRequest.Version)).
Msg("A node sending a MapRequest with Noise protocol") Msg("A node sending a MapRequest with Noise protocol")
capVer, err := parseCabailityVersion(req) ns.headscale.handlePoll(writer, req.Context(), node, mapRequest)
if err != nil && !errors.Is(err, ErrNoCapabilityVersion) {
log.Error().
Caller().
Err(err).
Msg("failed to parse capVer")
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
// TODO(kradalby): since we are now passing capVer, we could arguably stop passing
// isNoise, and rather have a isNoise function that takes capVer
ns.headscale.handlePoll(writer, req.Context(), node, mapRequest, true, capVer)
} }

View file

@ -40,7 +40,6 @@ func (s *Suite) ResetDB(c *check.C) {
c.Fatal(err) c.Fatal(err)
} }
cfg := types.Config{ cfg := types.Config{
PrivateKeyPath: tmpDir + "/private.key",
NoisePrivateKeyPath: tmpDir + "/noise_private.key", NoisePrivateKeyPath: tmpDir + "/noise_private.key",
DBtype: "sqlite3", DBtype: "sqlite3",
DBpath: tmpDir + "/headscale_test.db", DBpath: tmpDir + "/headscale_test.db",

View file

@ -41,7 +41,6 @@ type Config struct {
EphemeralNodeInactivityTimeout time.Duration EphemeralNodeInactivityTimeout time.Duration
NodeUpdateCheckInterval time.Duration NodeUpdateCheckInterval time.Duration
IPPrefixes []netip.Prefix IPPrefixes []netip.Prefix
PrivateKeyPath string
NoisePrivateKeyPath string NoisePrivateKeyPath string
BaseDomain string BaseDomain string
Log LogConfig Log LogConfig
@ -108,15 +107,16 @@ type OIDCConfig struct {
} }
type DERPConfig struct { type DERPConfig struct {
ServerEnabled bool ServerEnabled bool
ServerRegionID int ServerRegionID int
ServerRegionCode string ServerRegionCode string
ServerRegionName string ServerRegionName string
STUNAddr string ServerPrivateKeyPath string
URLs []url.URL STUNAddr string
Paths []string URLs []url.URL
AutoUpdate bool Paths []string
UpdateFrequency time.Duration AutoUpdate bool
UpdateFrequency time.Duration
} }
type LogTailConfig struct { type LogTailConfig struct {
@ -286,6 +286,7 @@ func GetDERPConfig() DERPConfig {
serverRegionCode := viper.GetString("derp.server.region_code") serverRegionCode := viper.GetString("derp.server.region_code")
serverRegionName := viper.GetString("derp.server.region_name") serverRegionName := viper.GetString("derp.server.region_name")
stunAddr := viper.GetString("derp.server.stun_listen_addr") stunAddr := viper.GetString("derp.server.stun_listen_addr")
privateKeyPath := util.AbsolutePathFromConfigPath(viper.GetString("derp.server.private_key_path"))
if serverEnabled && stunAddr == "" { if serverEnabled && stunAddr == "" {
log.Fatal(). log.Fatal().
@ -313,15 +314,16 @@ func GetDERPConfig() DERPConfig {
updateFrequency := viper.GetDuration("derp.update_frequency") updateFrequency := viper.GetDuration("derp.update_frequency")
return DERPConfig{ return DERPConfig{
ServerEnabled: serverEnabled, ServerEnabled: serverEnabled,
ServerRegionID: serverRegionID, ServerRegionID: serverRegionID,
ServerRegionCode: serverRegionCode, ServerRegionCode: serverRegionCode,
ServerRegionName: serverRegionName, ServerRegionName: serverRegionName,
STUNAddr: stunAddr, ServerPrivateKeyPath: privateKeyPath,
URLs: urls, STUNAddr: stunAddr,
Paths: paths, URLs: urls,
AutoUpdate: autoUpdate, Paths: paths,
UpdateFrequency: updateFrequency, AutoUpdate: autoUpdate,
UpdateFrequency: updateFrequency,
} }
} }
@ -582,9 +584,6 @@ func GetHeadscaleConfig() (*Config, error) {
DisableUpdateCheck: viper.GetBool("disable_check_updates"), DisableUpdateCheck: viper.GetBool("disable_check_updates"),
IPPrefixes: prefixes, IPPrefixes: prefixes,
PrivateKeyPath: util.AbsolutePathFromConfigPath(
viper.GetString("private_key_path"),
),
NoisePrivateKeyPath: util.AbsolutePathFromConfigPath( NoisePrivateKeyPath: util.AbsolutePathFromConfigPath(
viper.GetString("noise.private_key_path"), viper.GetString("noise.private_key_path"),
), ),

View file

@ -60,7 +60,7 @@ func TestUserCommand(t *testing.T) {
) )
assertNoErr(t, err) assertNoErr(t, err)
result := []string{listUsers[0].Name, listUsers[1].Name} result := []string{listUsers[0].GetName(), listUsers[1].GetName()}
sort.Strings(result) sort.Strings(result)
assert.Equal( assert.Equal(
@ -95,7 +95,7 @@ func TestUserCommand(t *testing.T) {
) )
assertNoErr(t, err) assertNoErr(t, err)
result = []string{listAfterRenameUsers[0].Name, listAfterRenameUsers[1].Name} result = []string{listAfterRenameUsers[0].GetName(), listAfterRenameUsers[1].GetName()}
sort.Strings(result) sort.Strings(result)
assert.Equal( assert.Equal(
@ -177,29 +177,29 @@ func TestPreAuthKeyCommand(t *testing.T) {
assert.Equal( assert.Equal(
t, t,
[]string{keys[0].Id, keys[1].Id, keys[2].Id}, []string{keys[0].GetId(), keys[1].GetId(), keys[2].GetId()},
[]string{listedPreAuthKeys[1].Id, listedPreAuthKeys[2].Id, listedPreAuthKeys[3].Id}, []string{listedPreAuthKeys[1].GetId(), listedPreAuthKeys[2].GetId(), listedPreAuthKeys[3].GetId()},
) )
assert.NotEmpty(t, listedPreAuthKeys[1].Key) assert.NotEmpty(t, listedPreAuthKeys[1].GetKey())
assert.NotEmpty(t, listedPreAuthKeys[2].Key) assert.NotEmpty(t, listedPreAuthKeys[2].GetKey())
assert.NotEmpty(t, listedPreAuthKeys[3].Key) assert.NotEmpty(t, listedPreAuthKeys[3].GetKey())
assert.True(t, listedPreAuthKeys[1].Expiration.AsTime().After(time.Now())) assert.True(t, listedPreAuthKeys[1].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedPreAuthKeys[2].Expiration.AsTime().After(time.Now())) assert.True(t, listedPreAuthKeys[2].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedPreAuthKeys[3].Expiration.AsTime().After(time.Now())) assert.True(t, listedPreAuthKeys[3].GetExpiration().AsTime().After(time.Now()))
assert.True( assert.True(
t, t,
listedPreAuthKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedPreAuthKeys[1].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
assert.True( assert.True(
t, t,
listedPreAuthKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedPreAuthKeys[2].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
assert.True( assert.True(
t, t,
listedPreAuthKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedPreAuthKeys[3].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
for index := range listedPreAuthKeys { for index := range listedPreAuthKeys {
@ -207,7 +207,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
continue continue
} }
assert.Equal(t, listedPreAuthKeys[index].AclTags, []string{"tag:test1", "tag:test2"}) assert.Equal(t, listedPreAuthKeys[index].GetAclTags(), []string{"tag:test1", "tag:test2"})
} }
// Test key expiry // Test key expiry
@ -218,7 +218,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
"--user", "--user",
user, user,
"expire", "expire",
listedPreAuthKeys[1].Key, listedPreAuthKeys[1].GetKey(),
}, },
) )
assertNoErr(t, err) assertNoErr(t, err)
@ -239,9 +239,9 @@ func TestPreAuthKeyCommand(t *testing.T) {
) )
assertNoErr(t, err) assertNoErr(t, err)
assert.True(t, listedPreAuthKeysAfterExpire[1].Expiration.AsTime().Before(time.Now())) assert.True(t, listedPreAuthKeysAfterExpire[1].GetExpiration().AsTime().Before(time.Now()))
assert.True(t, listedPreAuthKeysAfterExpire[2].Expiration.AsTime().After(time.Now())) assert.True(t, listedPreAuthKeysAfterExpire[2].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedPreAuthKeysAfterExpire[3].Expiration.AsTime().After(time.Now())) assert.True(t, listedPreAuthKeysAfterExpire[3].GetExpiration().AsTime().After(time.Now()))
} }
func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) { func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
@ -300,10 +300,10 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
// There is one key created by "scenario.CreateHeadscaleEnv" // There is one key created by "scenario.CreateHeadscaleEnv"
assert.Len(t, listedPreAuthKeys, 2) assert.Len(t, listedPreAuthKeys, 2)
assert.True(t, listedPreAuthKeys[1].Expiration.AsTime().After(time.Now())) assert.True(t, listedPreAuthKeys[1].GetExpiration().AsTime().After(time.Now()))
assert.True( assert.True(
t, t,
listedPreAuthKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Minute*70)), listedPreAuthKeys[1].GetExpiration().AsTime().Before(time.Now().Add(time.Minute*70)),
) )
} }
@ -442,9 +442,9 @@ func TestEnablingRoutes(t *testing.T) {
assert.Len(t, routes, 3) assert.Len(t, routes, 3)
for _, route := range routes { for _, route := range routes {
assert.Equal(t, route.Advertised, true) assert.Equal(t, route.GetAdvertised(), true)
assert.Equal(t, route.Enabled, false) assert.Equal(t, route.GetEnabled(), false)
assert.Equal(t, route.IsPrimary, false) assert.Equal(t, route.GetIsPrimary(), false)
} }
for _, route := range routes { for _, route := range routes {
@ -454,7 +454,7 @@ func TestEnablingRoutes(t *testing.T) {
"routes", "routes",
"enable", "enable",
"--route", "--route",
strconv.Itoa(int(route.Id)), strconv.Itoa(int(route.GetId())),
}) })
assertNoErr(t, err) assertNoErr(t, err)
} }
@ -475,12 +475,12 @@ func TestEnablingRoutes(t *testing.T) {
assert.Len(t, enablingRoutes, 3) assert.Len(t, enablingRoutes, 3)
for _, route := range enablingRoutes { for _, route := range enablingRoutes {
assert.Equal(t, route.Advertised, true) assert.Equal(t, route.GetAdvertised(), true)
assert.Equal(t, route.Enabled, true) assert.Equal(t, route.GetEnabled(), true)
assert.Equal(t, route.IsPrimary, true) assert.Equal(t, route.GetIsPrimary(), true)
} }
routeIDToBeDisabled := enablingRoutes[0].Id routeIDToBeDisabled := enablingRoutes[0].GetId()
_, err = headscale.Execute( _, err = headscale.Execute(
[]string{ []string{
@ -507,14 +507,14 @@ func TestEnablingRoutes(t *testing.T) {
assertNoErr(t, err) assertNoErr(t, err)
for _, route := range disablingRoutes { for _, route := range disablingRoutes {
assert.Equal(t, true, route.Advertised) assert.Equal(t, true, route.GetAdvertised())
if route.Id == routeIDToBeDisabled { if route.GetId() == routeIDToBeDisabled {
assert.Equal(t, route.Enabled, false) assert.Equal(t, route.GetEnabled(), false)
assert.Equal(t, route.IsPrimary, false) assert.Equal(t, route.GetIsPrimary(), false)
} else { } else {
assert.Equal(t, route.Enabled, true) assert.Equal(t, route.GetEnabled(), true)
assert.Equal(t, route.IsPrimary, true) assert.Equal(t, route.GetIsPrimary(), true)
} }
} }
} }
@ -577,43 +577,43 @@ func TestApiKeyCommand(t *testing.T) {
assert.Len(t, listedAPIKeys, 5) assert.Len(t, listedAPIKeys, 5)
assert.Equal(t, uint64(1), listedAPIKeys[0].Id) assert.Equal(t, uint64(1), listedAPIKeys[0].GetId())
assert.Equal(t, uint64(2), listedAPIKeys[1].Id) assert.Equal(t, uint64(2), listedAPIKeys[1].GetId())
assert.Equal(t, uint64(3), listedAPIKeys[2].Id) assert.Equal(t, uint64(3), listedAPIKeys[2].GetId())
assert.Equal(t, uint64(4), listedAPIKeys[3].Id) assert.Equal(t, uint64(4), listedAPIKeys[3].GetId())
assert.Equal(t, uint64(5), listedAPIKeys[4].Id) assert.Equal(t, uint64(5), listedAPIKeys[4].GetId())
assert.NotEmpty(t, listedAPIKeys[0].Prefix) assert.NotEmpty(t, listedAPIKeys[0].GetPrefix())
assert.NotEmpty(t, listedAPIKeys[1].Prefix) assert.NotEmpty(t, listedAPIKeys[1].GetPrefix())
assert.NotEmpty(t, listedAPIKeys[2].Prefix) assert.NotEmpty(t, listedAPIKeys[2].GetPrefix())
assert.NotEmpty(t, listedAPIKeys[3].Prefix) assert.NotEmpty(t, listedAPIKeys[3].GetPrefix())
assert.NotEmpty(t, listedAPIKeys[4].Prefix) assert.NotEmpty(t, listedAPIKeys[4].GetPrefix())
assert.True(t, listedAPIKeys[0].Expiration.AsTime().After(time.Now())) assert.True(t, listedAPIKeys[0].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedAPIKeys[1].Expiration.AsTime().After(time.Now())) assert.True(t, listedAPIKeys[1].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedAPIKeys[2].Expiration.AsTime().After(time.Now())) assert.True(t, listedAPIKeys[2].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedAPIKeys[3].Expiration.AsTime().After(time.Now())) assert.True(t, listedAPIKeys[3].GetExpiration().AsTime().After(time.Now()))
assert.True(t, listedAPIKeys[4].Expiration.AsTime().After(time.Now())) assert.True(t, listedAPIKeys[4].GetExpiration().AsTime().After(time.Now()))
assert.True( assert.True(
t, t,
listedAPIKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedAPIKeys[0].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
assert.True( assert.True(
t, t,
listedAPIKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedAPIKeys[1].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
assert.True( assert.True(
t, t,
listedAPIKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedAPIKeys[2].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
assert.True( assert.True(
t, t,
listedAPIKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedAPIKeys[3].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
assert.True( assert.True(
t, t,
listedAPIKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), listedAPIKeys[4].GetExpiration().AsTime().Before(time.Now().Add(time.Hour*26)),
) )
expiredPrefixes := make(map[string]bool) expiredPrefixes := make(map[string]bool)
@ -626,12 +626,12 @@ func TestApiKeyCommand(t *testing.T) {
"apikeys", "apikeys",
"expire", "expire",
"--prefix", "--prefix",
listedAPIKeys[idx].Prefix, listedAPIKeys[idx].GetPrefix(),
}, },
) )
assert.Nil(t, err) assert.Nil(t, err)
expiredPrefixes[listedAPIKeys[idx].Prefix] = true expiredPrefixes[listedAPIKeys[idx].GetPrefix()] = true
} }
var listedAfterExpireAPIKeys []v1.ApiKey var listedAfterExpireAPIKeys []v1.ApiKey
@ -648,17 +648,17 @@ func TestApiKeyCommand(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
for index := range listedAfterExpireAPIKeys { for index := range listedAfterExpireAPIKeys {
if _, ok := expiredPrefixes[listedAfterExpireAPIKeys[index].Prefix]; ok { if _, ok := expiredPrefixes[listedAfterExpireAPIKeys[index].GetPrefix()]; ok {
// Expired // Expired
assert.True( assert.True(
t, t,
listedAfterExpireAPIKeys[index].Expiration.AsTime().Before(time.Now()), listedAfterExpireAPIKeys[index].GetExpiration().AsTime().Before(time.Now()),
) )
} else { } else {
// Not expired // Not expired
assert.False( assert.False(
t, t,
listedAfterExpireAPIKeys[index].Expiration.AsTime().Before(time.Now()), listedAfterExpireAPIKeys[index].GetExpiration().AsTime().Before(time.Now()),
) )
} }
} }
@ -744,7 +744,7 @@ func TestNodeTagCommand(t *testing.T) {
) )
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, []string{"tag:test"}, node.ForcedTags) assert.Equal(t, []string{"tag:test"}, node.GetForcedTags())
// try to set a wrong tag and retrieve the error // try to set a wrong tag and retrieve the error
type errOutput struct { type errOutput struct {
@ -781,8 +781,8 @@ func TestNodeTagCommand(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
found := false found := false
for _, node := range resultMachines { for _, node := range resultMachines {
if node.ForcedTags != nil { if node.GetForcedTags() != nil {
for _, tag := range node.ForcedTags { for _, tag := range node.GetForcedTags() {
if tag == "tag:test" { if tag == "tag:test" {
found = true found = true
} }
@ -885,17 +885,17 @@ func TestNodeCommand(t *testing.T) {
assert.Len(t, listAll, 5) assert.Len(t, listAll, 5)
assert.Equal(t, uint64(1), listAll[0].Id) assert.Equal(t, uint64(1), listAll[0].GetId())
assert.Equal(t, uint64(2), listAll[1].Id) assert.Equal(t, uint64(2), listAll[1].GetId())
assert.Equal(t, uint64(3), listAll[2].Id) assert.Equal(t, uint64(3), listAll[2].GetId())
assert.Equal(t, uint64(4), listAll[3].Id) assert.Equal(t, uint64(4), listAll[3].GetId())
assert.Equal(t, uint64(5), listAll[4].Id) assert.Equal(t, uint64(5), listAll[4].GetId())
assert.Equal(t, "node-1", listAll[0].Name) assert.Equal(t, "node-1", listAll[0].GetName())
assert.Equal(t, "node-2", listAll[1].Name) assert.Equal(t, "node-2", listAll[1].GetName())
assert.Equal(t, "node-3", listAll[2].Name) assert.Equal(t, "node-3", listAll[2].GetName())
assert.Equal(t, "node-4", listAll[3].Name) assert.Equal(t, "node-4", listAll[3].GetName())
assert.Equal(t, "node-5", listAll[4].Name) assert.Equal(t, "node-5", listAll[4].GetName())
otherUserMachineKeys := []string{ otherUserMachineKeys := []string{
"mkey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", "mkey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
@ -963,11 +963,11 @@ func TestNodeCommand(t *testing.T) {
// All nodes, nodes + otherUser // All nodes, nodes + otherUser
assert.Len(t, listAllWithotherUser, 7) assert.Len(t, listAllWithotherUser, 7)
assert.Equal(t, uint64(6), listAllWithotherUser[5].Id) assert.Equal(t, uint64(6), listAllWithotherUser[5].GetId())
assert.Equal(t, uint64(7), listAllWithotherUser[6].Id) assert.Equal(t, uint64(7), listAllWithotherUser[6].GetId())
assert.Equal(t, "otherUser-node-1", listAllWithotherUser[5].Name) assert.Equal(t, "otherUser-node-1", listAllWithotherUser[5].GetName())
assert.Equal(t, "otherUser-node-2", listAllWithotherUser[6].Name) assert.Equal(t, "otherUser-node-2", listAllWithotherUser[6].GetName())
// Test list all nodes after added otherUser // Test list all nodes after added otherUser
var listOnlyotherUserMachineUser []v1.Node var listOnlyotherUserMachineUser []v1.Node
@ -988,18 +988,18 @@ func TestNodeCommand(t *testing.T) {
assert.Len(t, listOnlyotherUserMachineUser, 2) assert.Len(t, listOnlyotherUserMachineUser, 2)
assert.Equal(t, uint64(6), listOnlyotherUserMachineUser[0].Id) assert.Equal(t, uint64(6), listOnlyotherUserMachineUser[0].GetId())
assert.Equal(t, uint64(7), listOnlyotherUserMachineUser[1].Id) assert.Equal(t, uint64(7), listOnlyotherUserMachineUser[1].GetId())
assert.Equal( assert.Equal(
t, t,
"otherUser-node-1", "otherUser-node-1",
listOnlyotherUserMachineUser[0].Name, listOnlyotherUserMachineUser[0].GetName(),
) )
assert.Equal( assert.Equal(
t, t,
"otherUser-node-2", "otherUser-node-2",
listOnlyotherUserMachineUser[1].Name, listOnlyotherUserMachineUser[1].GetName(),
) )
// Delete a nodes // Delete a nodes
@ -1123,11 +1123,11 @@ func TestNodeExpireCommand(t *testing.T) {
assert.Len(t, listAll, 5) assert.Len(t, listAll, 5)
assert.True(t, listAll[0].Expiry.AsTime().IsZero()) assert.True(t, listAll[0].GetExpiry().AsTime().IsZero())
assert.True(t, listAll[1].Expiry.AsTime().IsZero()) assert.True(t, listAll[1].GetExpiry().AsTime().IsZero())
assert.True(t, listAll[2].Expiry.AsTime().IsZero()) assert.True(t, listAll[2].GetExpiry().AsTime().IsZero())
assert.True(t, listAll[3].Expiry.AsTime().IsZero()) assert.True(t, listAll[3].GetExpiry().AsTime().IsZero())
assert.True(t, listAll[4].Expiry.AsTime().IsZero()) assert.True(t, listAll[4].GetExpiry().AsTime().IsZero())
for idx := 0; idx < 3; idx++ { for idx := 0; idx < 3; idx++ {
_, err := headscale.Execute( _, err := headscale.Execute(
@ -1136,7 +1136,7 @@ func TestNodeExpireCommand(t *testing.T) {
"nodes", "nodes",
"expire", "expire",
"--identifier", "--identifier",
fmt.Sprintf("%d", listAll[idx].Id), fmt.Sprintf("%d", listAll[idx].GetId()),
}, },
) )
assert.Nil(t, err) assert.Nil(t, err)
@ -1158,11 +1158,11 @@ func TestNodeExpireCommand(t *testing.T) {
assert.Len(t, listAllAfterExpiry, 5) assert.Len(t, listAllAfterExpiry, 5)
assert.True(t, listAllAfterExpiry[0].Expiry.AsTime().Before(time.Now())) assert.True(t, listAllAfterExpiry[0].GetExpiry().AsTime().Before(time.Now()))
assert.True(t, listAllAfterExpiry[1].Expiry.AsTime().Before(time.Now())) assert.True(t, listAllAfterExpiry[1].GetExpiry().AsTime().Before(time.Now()))
assert.True(t, listAllAfterExpiry[2].Expiry.AsTime().Before(time.Now())) assert.True(t, listAllAfterExpiry[2].GetExpiry().AsTime().Before(time.Now()))
assert.True(t, listAllAfterExpiry[3].Expiry.AsTime().IsZero()) assert.True(t, listAllAfterExpiry[3].GetExpiry().AsTime().IsZero())
assert.True(t, listAllAfterExpiry[4].Expiry.AsTime().IsZero()) assert.True(t, listAllAfterExpiry[4].GetExpiry().AsTime().IsZero())
} }
func TestNodeRenameCommand(t *testing.T) { func TestNodeRenameCommand(t *testing.T) {
@ -1264,7 +1264,7 @@ func TestNodeRenameCommand(t *testing.T) {
"nodes", "nodes",
"rename", "rename",
"--identifier", "--identifier",
fmt.Sprintf("%d", listAll[idx].Id), fmt.Sprintf("%d", listAll[idx].GetId()),
fmt.Sprintf("newnode-%d", idx+1), fmt.Sprintf("newnode-%d", idx+1),
}, },
) )
@ -1300,7 +1300,7 @@ func TestNodeRenameCommand(t *testing.T) {
"nodes", "nodes",
"rename", "rename",
"--identifier", "--identifier",
fmt.Sprintf("%d", listAll[4].Id), fmt.Sprintf("%d", listAll[4].GetId()),
"testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine12345678901234567890", "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine12345678901234567890",
}, },
) )
@ -1387,11 +1387,11 @@ func TestNodeMoveCommand(t *testing.T) {
) )
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, uint64(1), node.Id) assert.Equal(t, uint64(1), node.GetId())
assert.Equal(t, "nomad-node", node.Name) assert.Equal(t, "nomad-node", node.GetName())
assert.Equal(t, node.User.Name, "old-user") assert.Equal(t, node.GetUser().GetName(), "old-user")
nodeID := fmt.Sprintf("%d", node.Id) nodeID := fmt.Sprintf("%d", node.GetId())
err = executeAndUnmarshal( err = executeAndUnmarshal(
headscale, headscale,
@ -1410,7 +1410,7 @@ func TestNodeMoveCommand(t *testing.T) {
) )
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, node.User.Name, "new-user") assert.Equal(t, node.GetUser().GetName(), "new-user")
var allNodes []v1.Node var allNodes []v1.Node
err = executeAndUnmarshal( err = executeAndUnmarshal(
@ -1428,9 +1428,9 @@ func TestNodeMoveCommand(t *testing.T) {
assert.Len(t, allNodes, 1) assert.Len(t, allNodes, 1)
assert.Equal(t, allNodes[0].Id, node.Id) assert.Equal(t, allNodes[0].GetId(), node.GetId())
assert.Equal(t, allNodes[0].User, node.User) assert.Equal(t, allNodes[0].GetUser(), node.GetUser())
assert.Equal(t, allNodes[0].User.Name, "new-user") assert.Equal(t, allNodes[0].GetUser().GetName(), "new-user")
moveToNonExistingNSResult, err := headscale.Execute( moveToNonExistingNSResult, err := headscale.Execute(
[]string{ []string{
@ -1452,7 +1452,7 @@ func TestNodeMoveCommand(t *testing.T) {
moveToNonExistingNSResult, moveToNonExistingNSResult,
"user not found", "user not found",
) )
assert.Equal(t, node.User.Name, "new-user") assert.Equal(t, node.GetUser().GetName(), "new-user")
err = executeAndUnmarshal( err = executeAndUnmarshal(
headscale, headscale,
@ -1471,7 +1471,7 @@ func TestNodeMoveCommand(t *testing.T) {
) )
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, node.User.Name, "old-user") assert.Equal(t, node.GetUser().GetName(), "old-user")
err = executeAndUnmarshal( err = executeAndUnmarshal(
headscale, headscale,
@ -1490,5 +1490,5 @@ func TestNodeMoveCommand(t *testing.T) {
) )
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, node.User.Name, "old-user") assert.Equal(t, node.GetUser().GetName(), "old-user")
} }

View file

@ -43,6 +43,7 @@ func TestDERPServerScenario(t *testing.T) {
headscaleConfig["HEADSCALE_DERP_SERVER_REGION_CODE"] = "headscale" headscaleConfig["HEADSCALE_DERP_SERVER_REGION_CODE"] = "headscale"
headscaleConfig["HEADSCALE_DERP_SERVER_REGION_NAME"] = "Headscale Embedded DERP" headscaleConfig["HEADSCALE_DERP_SERVER_REGION_NAME"] = "Headscale Embedded DERP"
headscaleConfig["HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR"] = "0.0.0.0:3478" headscaleConfig["HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR"] = "0.0.0.0:3478"
headscaleConfig["HEADSCALE_DERP_SERVER_PRIVATE_KEY_PATH"] = "/tmp/derp.key"
err = scenario.CreateHeadscaleEnv( err = scenario.CreateHeadscaleEnv(
spec, spec,

View file

@ -530,10 +530,10 @@ func TestExpireNode(t *testing.T) {
peerPublicKey := strings.TrimPrefix(peerStatus.PublicKey.String(), "nodekey:") peerPublicKey := strings.TrimPrefix(peerStatus.PublicKey.String(), "nodekey:")
assert.NotEqual(t, node.NodeKey, peerPublicKey) assert.NotEqual(t, node.GetNodeKey(), peerPublicKey)
} }
if client.Hostname() != node.Name { if client.Hostname() != node.GetName() {
// Assert that we have the original count - self - expired node // Assert that we have the original count - self - expired node
assert.Len(t, status.Peers(), len(MustTestVersions)-2) assert.Len(t, status.Peers(), len(MustTestVersions)-2)
} }

View file

@ -26,7 +26,6 @@ run_tests() {
--volume "$PWD"/control_logs:/tmp/control \ --volume "$PWD"/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go test ./... \
-tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
-parallel 1 \ -parallel 1 \

View file

@ -22,6 +22,17 @@ const (
scenarioHashLength = 6 scenarioHashLength = 6
) )
func enabledVersions(vs map[string]bool) []string {
var ret []string
for version, enabled := range vs {
if enabled {
ret = append(ret, version)
}
}
return ret
}
var ( var (
errNoHeadscaleAvailable = errors.New("no headscale available") errNoHeadscaleAvailable = errors.New("no headscale available")
errNoUserAvailable = errors.New("no user available") errNoUserAvailable = errors.New("no user available")
@ -29,29 +40,30 @@ var (
// Tailscale started adding TS2021 support in CapabilityVersion>=28 (v1.24.0), but // Tailscale started adding TS2021 support in CapabilityVersion>=28 (v1.24.0), but
// proper support in Headscale was only added for CapabilityVersion>=39 clients (v1.30.0). // proper support in Headscale was only added for CapabilityVersion>=39 clients (v1.30.0).
tailscaleVersions2021 = []string{ tailscaleVersions2021 = map[string]bool{
"head", "head": true,
"unstable", "unstable": true,
"1.50", "1.52": true, // CapVer:
"1.48", "1.50": true, // CapVer: 74
"1.46", "1.48": true, // CapVer: 68
"1.44", "1.46": true, // CapVer: 65
"1.42", "1.44": true, // CapVer: 63
"1.40", "1.42": true, // CapVer: 61
"1.38", "1.40": true, // CapVer: 61
"1.36", "1.38": true, // CapVer: 58
"1.34", "1.36": true, // CapVer: 56
"1.32", "1.34": true, // CapVer: 51
"1.30", "1.32": true, // Oldest supported version, CapVer: 46
"1.30": false,
} }
tailscaleVersions2019 = []string{ tailscaleVersions2019 = map[string]bool{
"1.28", "1.28": false,
"1.26", "1.26": false,
"1.24", // Tailscale SSH "1.24": false, // Tailscale SSH
"1.22", "1.22": false,
"1.20", "1.20": false,
"1.18", "1.18": false,
} }
// tailscaleVersionsUnavailable = []string{ // tailscaleVersionsUnavailable = []string{
@ -72,8 +84,8 @@ var (
// The rest of the version represents Tailscale versions that can be // The rest of the version represents Tailscale versions that can be
// found in Tailscale's apt repository. // found in Tailscale's apt repository.
AllVersions = append( AllVersions = append(
tailscaleVersions2021, enabledVersions(tailscaleVersions2021),
tailscaleVersions2019..., enabledVersions(tailscaleVersions2019)...,
) )
// MustTestVersions is the minimum set of versions we should test. // MustTestVersions is the minimum set of versions we should test.
@ -83,8 +95,8 @@ var (
// - Two latest versions // - Two latest versions
// - Two oldest versions. // - Two oldest versions.
MustTestVersions = append( MustTestVersions = append(
tailscaleVersions2021[0:4], AllVersions[0:4],
tailscaleVersions2019[len(tailscaleVersions2019)-2:]..., AllVersions[len(AllVersions)-2:]...,
) )
) )