Remove support for non-noise clients (pre-1.32) (#1611)
This commit is contained in:
parent
b918aa03fc
commit
a59aab2081
72 changed files with 319 additions and 679 deletions
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,6 @@ var createNodeCmd = &cobra.Command{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
SuccessOutput(response.Node, "Node created", output)
|
SuccessOutput(response.GetNode(), "Node created", output)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()).
|
||||||
|
|
|
@ -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, ®isterRequest, &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)
|
|
||||||
}
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
//go:build !ts2019
|
|
||||||
|
|
||||||
package hscontrol
|
|
||||||
|
|
||||||
import "github.com/gorilla/mux"
|
|
||||||
|
|
||||||
func (h *Headscale) addLegacyHandlers(router *mux.Router) {
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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:]...,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue