Compare commits

..

2 Commits

Author SHA1 Message Date
Kristoffer Dalby
94048a96e7 Run on correct change
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-04 10:32:57 +01:00
Kristoffer Dalby
a617edadf5 Add experimental kradalby gh runner
Remove old v2 runner in favour of self-hosted

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-04 10:32:22 +01:00
43 changed files with 230 additions and 658 deletions

View File

@@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -9,7 +9,7 @@ jobs:
add-contributors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Delete upstream contributor branch
# Allow continue on failure to account for when the
# upstream branch is deleted or does not exist.

View File

@@ -7,13 +7,13 @@ jobs:
golangci-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-18.04 # due to CGO we need to user an older version
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v3
uses: actions/setup-go@v2
with:
go-version: 1.19.0
@@ -37,7 +37,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Docker Buildx
@@ -100,7 +100,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Docker Buildx
@@ -166,7 +166,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Docker Buildx

View File

@@ -16,7 +16,7 @@ jobs:
APP_ID: ${{ secrets.RENOVATEBOT_APP_ID }}
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2.0.0
- name: Self-hosted Renovate
uses: renovatebot/github-action@v31.81.3

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 2
@@ -18,7 +18,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 2
@@ -18,7 +18,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 2
@@ -18,7 +18,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -13,7 +13,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -7,13 +7,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix

View File

@@ -1,8 +1,6 @@
---
run:
timeout: 10m
build-tags:
- ts2019
issues:
skip-dirs:
@@ -28,7 +26,6 @@ linters:
- ireturn
- execinquery
- exhaustruct
- nolintlint
# We should strive to enable these:
- wrapcheck
@@ -36,9 +33,6 @@ linters:
- makezero
- maintidx
# Limits the methods of an interface to 10. We have more in integration tests
- interfacebloat
# We might want to enable this, but it might be a lot of work
- cyclop
- nestif

View File

@@ -20,8 +20,6 @@ builds:
- -mod=readonly
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
tags:
- ts2019
- id: darwin-arm64
main: ./cmd/headscale/headscale.go
@@ -36,8 +34,6 @@ builds:
- -mod=readonly
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
tags:
- ts2019
- id: linux-amd64
mod_timestamp: "{{ .CommitTimestamp }}"
@@ -50,8 +46,6 @@ builds:
main: ./cmd/headscale/headscale.go
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
tags:
- ts2019
- id: linux-arm64
mod_timestamp: "{{ .CommitTimestamp }}"
@@ -64,8 +58,6 @@ builds:
main: ./cmd/headscale/headscale.go
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
tags:
- ts2019
archives:
- id: golang-cross

View File

@@ -24,7 +24,6 @@
- Remove `ip_prefix` configuration option and warning [#899](https://github.com/juanfont/headscale/pull/899)
- Add `dns_config.override_local_dns` option [#905](https://github.com/juanfont/headscale/pull/905)
- Fix some DNS config issues [#660](https://github.com/juanfont/headscale/issues/660)
- Make it possible to disable TS2019 with build flag [#928](https://github.com/juanfont/headscale/pull/928)
## 0.16.4 (2022-08-21)

View File

@@ -9,7 +9,7 @@ RUN go mod download
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 test -e /go/bin/headscale

View File

@@ -9,7 +9,7 @@ RUN go mod download
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
# Debug image

View File

@@ -10,8 +10,6 @@ ifeq ($(filter $(GOOS), openbsd netbsd soloaris plan9), )
else
endif
TAGS = -tags ts2019
# GO_SOURCES = $(wildcard *.go)
# PROTO_SOURCES = $(wildcard **/*.proto)
GO_SOURCES = $(call rwildcard,,*.go)
@@ -19,12 +17,12 @@ PROTO_SOURCES = $(call rwildcard,,*.proto)
build:
nix build
GOOS=$(GOOS) CGO_ENABLED=0 go build -trimpath $(pieflags) -mod=readonly -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go
dev: lint test build
test:
@go test $(TAGS) -short -coverprofile=coverage.out ./...
@go test -short -coverprofile=coverage.out ./...
test_integration: test_integration_cli test_integration_derp test_integration_oidc test_integration_v2_general
@@ -36,7 +34,7 @@ test_integration_cli:
-v ~/.cache/hs-integration-go:/go \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test $(TAGS) -failfast -timeout 30m -count=1 -run IntegrationCLI ./...
go test -failfast -timeout 30m -count=1 -run IntegrationCLI ./...
test_integration_derp:
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
@@ -46,7 +44,7 @@ test_integration_derp:
-v ~/.cache/hs-integration-go:/go \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test $(TAGS) -failfast -timeout 30m -count=1 -run IntegrationDERP ./...
go test -failfast -timeout 30m -count=1 -run IntegrationDERP ./...
test_integration_oidc:
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
@@ -56,7 +54,7 @@ test_integration_oidc:
-v ~/.cache/hs-integration-go:/go \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test $(TAGS) -failfast -timeout 30m -count=1 -run IntegrationOIDC ./...
go test -failfast -timeout 30m -count=1 -run IntegrationOIDC ./...
test_integration_v2_general:
docker run \
@@ -66,7 +64,7 @@ test_integration_v2_general:
-v $$PWD:$$PWD -w $$PWD/integration \
-v /var/run/docker.sock:/var/run/docker.sock \
golang:1 \
go test $(TAGS) -failfast ./... -timeout 60m -parallel 6
go test ./... -timeout 60m -parallel 6
coverprofile_func:
go tool cover -func=coverage.out

View File

@@ -424,13 +424,6 @@ make build
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/CNLHC>
<img src=https://avatars.githubusercontent.com/u/21005146?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=LiuHanCheng/>
<br />
<sub style="font-size:14px"><b>LiuHanCheng</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/pvinis>
<img src=https://avatars.githubusercontent.com/u/100233?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Pavlos Vinieratos/>
@@ -446,7 +439,7 @@ make build
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/ratsclub>
<a href=https://github.com/vtrf>
<img src=https://avatars.githubusercontent.com/u/25647735?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Victor Freire/>
<br />
<sub style="font-size:14px"><b>Victor Freire</b></sub>
@@ -466,8 +459,6 @@ make build
<sub style="font-size:14px"><b>thomas</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/aberoham>
<img src=https://avatars.githubusercontent.com/u/586805?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Abraham Ingersoll/>
@@ -475,13 +466,8 @@ make build
<sub style="font-size:14px"><b>Abraham Ingersoll</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/puzpuzpuz>
<img src=https://avatars.githubusercontent.com/u/37772591?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Andrei Pechkurov/>
<br />
<sub style="font-size:14px"><b>Andrei Pechkurov</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/apognu>
<img src=https://avatars.githubusercontent.com/u/3017182?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Antoine POPINEAU/>
@@ -510,8 +496,6 @@ make build
<sub style="font-size:14px"><b>Bryan Stenson</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/yangchuansheng>
<img src=https://avatars.githubusercontent.com/u/15308462?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt= Carson Yang/>
@@ -526,6 +510,8 @@ make build
<sub style="font-size:14px"><b>kundel</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/fkr>
<img src=https://avatars.githubusercontent.com/u/51063?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Felix Kronlage-Dammers/>
@@ -554,8 +540,6 @@ make build
<sub style="font-size:14px"><b>Jim Tittsler</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/ShadowJonathan>
<img src=https://avatars.githubusercontent.com/u/22740616?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Jonathan de Jong/>
@@ -570,6 +554,8 @@ make build
<sub style="font-size:14px"><b>Pierre Carru</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/Donran>
<img src=https://avatars.githubusercontent.com/u/4838348?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Pontus N/>
@@ -593,13 +579,11 @@ make build
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/renovate-bot>
<img src=https://avatars.githubusercontent.com/u/25180681?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Mend Renovate/>
<img src=https://avatars.githubusercontent.com/u/25180681?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=WhiteSource Renovate/>
<br />
<sub style="font-size:14px"><b>Mend Renovate</b></sub>
<sub style="font-size:14px"><b>WhiteSource Renovate</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/ryanfowler>
<img src=https://avatars.githubusercontent.com/u/2668821?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Ryan Fowler/>
@@ -614,6 +598,8 @@ make build
<sub style="font-size:14px"><b>Shaanan Cohney</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/stefanvanburen>
<img src=https://avatars.githubusercontent.com/u/622527?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Stefan VanBuren/>
@@ -642,8 +628,6 @@ make build
<sub style="font-size:14px"><b>Teteros</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/gitter-badger>
<img src=https://avatars.githubusercontent.com/u/8518239?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=The Gitter Badger/>
@@ -658,6 +642,8 @@ make build
<sub style="font-size:14px"><b>Tianon Gravi</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/thetillhoff>
<img src=https://avatars.githubusercontent.com/u/25052289?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Till Hoffmann/>
@@ -686,8 +672,6 @@ make build
<sub style="font-size:14px"><b>Yujie Xia</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/zekker6>
<img src=https://avatars.githubusercontent.com/u/1367798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Zakhar Bessarab/>
@@ -702,6 +686,8 @@ make build
<sub style="font-size:14px"><b>Zhiyuan Zheng</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/Bpazy>
<img src=https://avatars.githubusercontent.com/u/9838749?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Ziyuan Han/>
@@ -730,8 +716,6 @@ make build
<sub style="font-size:14px"><b>ignoramous</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/lion24>
<img src=https://avatars.githubusercontent.com/u/1382102?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=sharkonet/>
@@ -746,6 +730,8 @@ make build
<sub style="font-size:14px"><b>pernila</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/phpmalik>
<img src=https://avatars.githubusercontent.com/u/26834645?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=phpmalik/>

17
app.go
View File

@@ -51,6 +51,12 @@ const (
errUnsupportedLetsEncryptChallengeType = Error(
"unknown value for Lets Encrypt challenge type",
)
ErrFailedPrivateKey = Error("failed to read or create private key")
ErrFailedNoisePrivateKey = Error(
"failed to read or create Noise protocol private key",
)
ErrSamePrivateKeys = Error("private key and noise private key are the same")
)
const (
@@ -125,17 +131,17 @@ func LookupTLSClientAuthMode(mode string) (tls.ClientAuthType, bool) {
func NewHeadscale(cfg *Config) (*Headscale, error) {
privateKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read or create private key: %w", err)
return nil, ErrFailedPrivateKey
}
// TS2021 requires to have a different key from the legacy protocol.
noisePrivateKey, err := readOrCreatePrivateKey(cfg.NoisePrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read or create Noise protocol private key: %w", err)
return nil, ErrFailedNoisePrivateKey
}
if privateKey.Equal(*noisePrivateKey) {
return nil, fmt.Errorf("private key and noise private key are the same: %w", err)
return nil, ErrSamePrivateKeys
}
var dbString string
@@ -448,8 +454,9 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet)
router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet)
router.HandleFunc("/register/{nkey}", h.RegisterWebAPI).Methods(http.MethodGet)
h.addLegacyHandlers(router)
router.HandleFunc("/machine/{mkey}/map", h.PollNetMapHandler).
Methods(http.MethodPost)
router.HandleFunc("/machine/{mkey}", h.RegistrationHandler).Methods(http.MethodPost)
router.HandleFunc("/oidc/register/{nkey}", h.RegisterOIDC).Methods(http.MethodGet)
router.HandleFunc("/oidc/callback", h.OIDCCallback).Methods(http.MethodGet)
router.HandleFunc("/apple", h.AppleConfigMessage).Methods(http.MethodGet)

View File

@@ -3,7 +3,6 @@ package cli
import (
"fmt"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
@@ -11,7 +10,8 @@ import (
)
const (
errPreAuthKeyMalformed = Error("key is malformed. expected 64 hex characters with `nodekey` prefix")
keyLength = 64
errPreAuthKeyTooShort = Error("key too short, must be 64 hexadecimal characters")
)
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
@@ -87,8 +87,8 @@ var createNodeCmd = &cobra.Command{
return
}
if !headscale.NodePublicKeyRegex.Match([]byte(machineKey)) {
err = errPreAuthKeyMalformed
if len(machineKey) != keyLength {
err = errPreAuthKeyTooShort
ErrorOutput(
err,
fmt.Sprintf("Error: %s", err),

View File

@@ -134,9 +134,7 @@ var registerNodeCmd = &cobra.Command{
return
}
SuccessOutput(
response.Machine,
fmt.Sprintf("Machine %s registered", response.Machine.GivenName), output)
SuccessOutput(response.Machine, "Machine register", output)
},
}

View File

@@ -35,13 +35,14 @@ grpc_listen_addr: 0.0.0.0:50443
# are doing.
grpc_allow_insecure: false
# Private key used to encrypt the traffic between headscale
# Private key used encrypt the traffic between headscale
# and Tailscale clients.
# The private key file will be autogenerated if it's missing.
# The private key file which will be
# autogenerated if it's missing
private_key_path: /var/lib/headscale/private.key
# The Noise section includes specific configuration for the
# TS2021 Noise protocol
# TS2021 Noise procotol
noise:
# The Noise private key is used to encrypt the
# traffic between headscale and Tailscale clients when
@@ -77,7 +78,7 @@ derp:
region_code: "headscale"
region_name: "Headscale Embedded DERP"
# Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
# Listens in UDP at the configured address for STUN connections to help on NAT traversal.
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
#
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
@@ -111,9 +112,9 @@ disable_check_updates: false
# Time before an inactive ephemeral node is deleted?
ephemeral_node_inactivity_timeout: 30m
# Period to check for node updates within the tailnet. A value too low will severely affect
# Period to check for node updates in the tailnet. A value too low will severily affect
# CPU consumption of Headscale. A value too high (over 60s) will cause problems
# for the nodes, as they won't get updates or keep alive messages frequently enough.
# to the nodes, as they won't get updates or keep alive messages in time.
# In case of doubts, do not touch the default 10s.
node_update_check_interval: 10s

View File

@@ -26,8 +26,6 @@
version = headscaleVersion;
src = pkgs.lib.cleanSource self;
tags = ["ts2019"];
# Only run unit tests when testing a build
checkFlags = ["-short"];
@@ -137,7 +135,7 @@
buildInputs = devDeps;
shellHook = ''
export GOFLAGS=-tags="ts2019"
export GOFLAGS=-tags="integration,integration_general,integration_oidc,integration_cli,integration_derp"
'';
};

View File

@@ -1,4 +1,4 @@
// nolint
//nolint
package headscale
import (
@@ -12,7 +12,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
type headscaleV1APIServer struct { // v1.HeadscaleServiceServer
@@ -497,14 +496,9 @@ func (api headscaleV1APIServer) DebugCreateMachine(
HostInfo: HostInfo(hostinfo),
}
nodeKey := key.NodePublic{}
err = nodeKey.UnmarshalText([]byte(request.GetKey()))
if err != nil {
log.Panic().Msg("can not add machine for debug. invalid node key")
}
api.h.registrationCache.Set(
NodePublicKeyStripPrefix(nodeKey),
request.GetKey(),
newMachine,
registerCacheExpiration,
)

View File

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

View File

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

View File

@@ -1,192 +0,0 @@
package integration
import (
"context"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strings"
"testing"
)
var errParseAuthPage = errors.New("failed to parse auth page")
type AuthWebFlowScenario struct {
*Scenario
}
func TestAuthWebFlowAuthenticationPingAll(t *testing.T) {
IntegrationSkip(t)
baseScenario, err := NewScenario()
if err != nil {
t.Errorf("failed to create scenario: %s", err)
}
scenario := AuthWebFlowScenario{
Scenario: baseScenario,
}
spec := map[string]int{
"namespace1": len(TailscaleVersions),
"namespace2": len(TailscaleVersions),
}
err = scenario.CreateHeadscaleEnv(spec)
if err != nil {
t.Errorf("failed to create headscale environment: %s", err)
}
allClients, err := scenario.ListTailscaleClients()
if err != nil {
t.Errorf("failed to get clients: %s", err)
}
allIps, err := scenario.ListTailscaleClientsIPs()
if err != nil {
t.Errorf("failed to get clients: %s", err)
}
err = scenario.WaitForTailscaleSync()
if err != nil {
t.Errorf("failed wait for tailscale clients to be in sync: %s", err)
}
success := 0
for _, client := range allClients {
for _, ip := range allIps {
err := client.Ping(ip.String())
if err != nil {
t.Errorf("failed to ping %s from %s: %s", ip, client.Hostname(), err)
} else {
success++
}
}
}
t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps))
err = scenario.Shutdown()
if err != nil {
t.Errorf("failed to tear down scenario: %s", err)
}
}
func (s *AuthWebFlowScenario) CreateHeadscaleEnv(namespaces map[string]int) error {
err := s.StartHeadscale()
if err != nil {
return err
}
err = s.Headscale().WaitForReady()
if err != nil {
return err
}
for namespaceName, clientCount := range namespaces {
log.Printf("creating namespace %s with %d clients", namespaceName, clientCount)
err = s.CreateNamespace(namespaceName)
if err != nil {
return err
}
err = s.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount)
if err != nil {
return err
}
err = s.runTailscaleUp(namespaceName, s.Headscale().GetEndpoint())
if err != nil {
return err
}
}
return nil
}
func (s *AuthWebFlowScenario) runTailscaleUp(
namespaceStr, loginServer string,
) error {
log.Printf("running tailscale up for namespace %s", namespaceStr)
if namespace, ok := s.namespaces[namespaceStr]; ok {
for _, client := range namespace.Clients {
namespace.joinWaitGroup.Add(1)
go func(c TailscaleClient) {
defer namespace.joinWaitGroup.Done()
// TODO(juanfont): error handle this
loginURL, err := c.UpWithLoginURL(loginServer)
if err != nil {
log.Printf("failed to run tailscale up: %s", err)
}
err = s.runHeadscaleRegister(namespaceStr, loginURL)
if err != nil {
log.Printf("failed to register client: %s", err)
}
err = c.WaitForReady()
if err != nil {
log.Printf("error waiting for client %s to be ready: %s", c.Hostname(), err)
}
}(client)
}
namespace.joinWaitGroup.Wait()
return nil
}
return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable)
}
func (s *AuthWebFlowScenario) runHeadscaleRegister(namespaceStr string, loginURL *url.URL) error {
log.Printf("loginURL: %s", loginURL)
loginURL.Host = fmt.Sprintf("%s:8080", s.Headscale().GetIP())
loginURL.Scheme = "http"
httpClient := &http.Client{}
ctx := context.Background()
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil)
resp, err := httpClient.Do(req)
if err != nil {
return err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()
// see api.go HTML template
codeSep := strings.Split(string(body), "</code>")
if len(codeSep) != 2 {
return errParseAuthPage
}
keySep := strings.Split(codeSep[0], "key ")
if len(keySep) != 2 {
return errParseAuthPage
}
key := keySep[1]
log.Printf("registering node %s", key)
if headscale, ok := s.controlServers["headscale"]; ok {
_, err = headscale.Execute([]string{"headscale", "-n", namespaceStr, "nodes", "register", "--key", key})
if err != nil {
log.Printf("failed to register node: %s", err)
return err
}
return nil
}
return fmt.Errorf("failed to find headscale: %w", errNoHeadscaleAvailable)
}

View File

@@ -168,7 +168,7 @@ func TestTaildrop(t *testing.T) {
for _, client := range allClients {
command := []string{"touch", fmt.Sprintf("/tmp/file_from_%s", client.Hostname())}
if _, _, err := client.Execute(command); err != nil {
if _, err := client.Execute(command); err != nil {
t.Errorf("failed to create taildrop file on %s, err: %s", client.Hostname(), err)
}
@@ -193,7 +193,7 @@ func TestTaildrop(t *testing.T) {
client.Hostname(),
peer.Hostname(),
)
_, _, err := client.Execute(command)
_, err := client.Execute(command)
return err
})
@@ -214,7 +214,7 @@ func TestTaildrop(t *testing.T) {
"get",
"/tmp/",
}
if _, _, err := client.Execute(command); err != nil {
if _, err := client.Execute(command); err != nil {
t.Errorf("failed to get taildrop file on %s, err: %s", client.Hostname(), err)
}
@@ -234,7 +234,7 @@ func TestTaildrop(t *testing.T) {
peer.Hostname(),
)
result, _, err := client.Execute(command)
result, err := client.Execute(command)
if err != nil {
t.Errorf("failed to execute command to ls taildrop: %s", err)
}
@@ -306,7 +306,7 @@ func TestResolveMagicDNS(t *testing.T) {
"tailscale",
"ip", peerFQDN,
}
result, _, err := client.Execute(command)
result, err := client.Execute(command)
if err != nil {
t.Errorf(
"failed to execute resolve/ip command %s from %s: %s",

View File

@@ -179,7 +179,9 @@ func (t *HeadscaleInContainer) GetIP() string {
}
func (t *HeadscaleInContainer) GetPort() string {
return fmt.Sprintf("%d", t.port)
portProto := fmt.Sprintf("%d/tcp", t.port)
return t.container.GetPort(portProto)
}
func (t *HeadscaleInContainer) GetHealthEndpoint() string {

View File

@@ -258,13 +258,7 @@ func (s *Scenario) RunTailscaleUp(
// TODO(kradalby): error handle this
_ = c.Up(loginServer, authKey)
}(client)
err := client.WaitForReady()
if err != nil {
log.Printf("error waiting for client %s to be ready: %s", client.Hostname(), err)
}
}
namespace.joinWaitGroup.Wait()
return nil

View File

@@ -2,7 +2,6 @@ package integration
import (
"net/netip"
"net/url"
"tailscale.com/ipn/ipnstate"
)
@@ -11,13 +10,11 @@ type TailscaleClient interface {
Hostname() string
Shutdown() error
Version() string
Execute(command []string) (string, string, error)
Execute(command []string) (string, error)
Up(loginServer, authKey string) error
UpWithLoginURL(loginServer string) (*url.URL, error)
IPs() ([]netip.Addr, error)
FQDN() (string, error)
Status() (*ipnstate.Status, error)
WaitForReady() error
WaitForPeers(expected int) error
Ping(hostnameOrIP string) error
}

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"log"
"net/netip"
"net/url"
"strings"
"github.com/cenkalti/backoff/v4"
@@ -23,11 +22,9 @@ const (
)
var (
errTailscalePingFailed = errors.New("ping failed")
errTailscaleNotLoggedIn = errors.New("tailscale not logged in")
errTailscaleWrongPeerCount = errors.New("wrong peer count")
errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
errTailscaleNotConnected = errors.New("tailscale not connected")
errTailscalePingFailed = errors.New("ping failed")
errTailscaleNotLoggedIn = errors.New("tailscale not logged in")
errTailscaleWrongPeerCount = errors.New("wrong peer count")
)
type TailscaleInContainer struct {
@@ -113,7 +110,7 @@ func (t *TailscaleInContainer) Version() string {
func (t *TailscaleInContainer) Execute(
command []string,
) (string, string, error) {
) (string, error) {
log.Println("command", command)
log.Printf("running command for %s\n", t.hostname)
stdout, stderr, err := dockertestutil.ExecuteCommand(
@@ -129,13 +126,13 @@ func (t *TailscaleInContainer) Execute(
}
if strings.Contains(stderr, "NeedsLogin") {
return stdout, stderr, errTailscaleNotLoggedIn
return "", errTailscaleNotLoggedIn
}
return stdout, stderr, err
return "", err
}
return stdout, stderr, nil
return stdout, nil
}
func (t *TailscaleInContainer) Up(
@@ -152,45 +149,13 @@ func (t *TailscaleInContainer) Up(
t.hostname,
}
if _, _, err := t.Execute(command); err != nil {
if _, err := t.Execute(command); err != nil {
return fmt.Errorf("failed to join tailscale client: %w", err)
}
return nil
}
func (t *TailscaleInContainer) UpWithLoginURL(
loginServer string,
) (*url.URL, error) {
command := []string{
"tailscale",
"up",
"-login-server",
loginServer,
"--hostname",
t.hostname,
}
_, stderr, err := t.Execute(command)
if errors.Is(err, errTailscaleNotLoggedIn) {
return nil, errTailscaleCannotUpWithoutAuthkey
}
urlStr := strings.ReplaceAll(stderr, "\nTo authenticate, visit:\n\n\t", "")
urlStr = strings.TrimSpace(urlStr)
// parse URL
loginURL, err := url.Parse(urlStr)
if err != nil {
log.Printf("Could not parse login URL: %s", err)
log.Printf("Original join command result: %s", stderr)
return nil, err
}
return loginURL, nil
}
func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) {
if t.ips != nil && len(t.ips) != 0 {
return t.ips, nil
@@ -203,7 +168,7 @@ func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) {
"ip",
}
result, _, err := t.Execute(command)
result, err := t.Execute(command)
if err != nil {
return []netip.Addr{}, fmt.Errorf("failed to join tailscale client: %w", err)
}
@@ -230,7 +195,7 @@ func (t *TailscaleInContainer) Status() (*ipnstate.Status, error) {
"--json",
}
result, _, err := t.Execute(command)
result, err := t.Execute(command)
if err != nil {
return nil, fmt.Errorf("failed to execute tailscale status command: %w", err)
}
@@ -257,21 +222,6 @@ func (t *TailscaleInContainer) FQDN() (string, error) {
return status.Self.DNSName, nil
}
func (t *TailscaleInContainer) WaitForReady() error {
return t.pool.Retry(func() error {
status, err := t.Status()
if err != nil {
return fmt.Errorf("failed to fetch tailscale status: %w", err)
}
if status.CurrentTailnet != nil {
return nil
}
return errTailscaleNotConnected
})
}
func (t *TailscaleInContainer) WaitForPeers(expected int) error {
return t.pool.Retry(func() error {
status, err := t.Status()
@@ -298,7 +248,7 @@ func (t *TailscaleInContainer) Ping(hostnameOrIP string) error {
hostnameOrIP,
}
result, _, err := t.Execute(command)
result, err := t.Execute(command)
if err != nil {
log.Printf(
"failed to run ping command from %s to %s, err: %s",

View File

@@ -1,4 +1,4 @@
// nolint
//nolint
package headscale
import (
@@ -558,8 +558,8 @@ func (s *IntegrationCLITestSuite) TestNodeTagCommand() {
assert.Nil(s.T(), err)
machineKeys := []string{
"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -691,11 +691,11 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
// Randomly generated machine keys
machineKeys := []string{
"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
"nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
"nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
"9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
"8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
"cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -779,8 +779,8 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
assert.Equal(s.T(), "machine-5", listAll[4].Name)
otherNamespaceMachineKeys := []string{
"nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
"nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
"b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
"dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
}
otherNamespaceMachines := make([]*v1.Machine, len(otherNamespaceMachineKeys))
assert.Nil(s.T(), err)
@@ -950,11 +950,11 @@ func (s *IntegrationCLITestSuite) TestNodeExpireCommand() {
// Randomly generated machine keys
machineKeys := []string{
"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
"nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
"nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
"9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
"8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
"cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -1077,11 +1077,11 @@ func (s *IntegrationCLITestSuite) TestNodeRenameCommand() {
// Randomly generated machine keys
machineKeys := []string{
"nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
"nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
"nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
"cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
"8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
"f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
"6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
"9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -1248,7 +1248,7 @@ func (s *IntegrationCLITestSuite) TestRouteCommand() {
assert.Nil(s.T(), err)
// Randomly generated machine keys
machineKey := "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe"
machineKey := "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe"
_, _, err = ExecuteCommand(
&s.headscale,
@@ -1588,7 +1588,7 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() {
assert.Nil(s.T(), err)
// Randomly generated machine key
machineKey := "nodekey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
machineKey := "688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
_, _, err = ExecuteCommand(
&s.headscale,

View File

@@ -1,4 +1,4 @@
// nolint
//nolint
package headscale
import (

View File

@@ -839,13 +839,7 @@ func (h *Headscale) RegisterMachineFromAuthCallback(
namespaceName string,
registrationMethod string,
) (*Machine, error) {
nodeKey := key.NodePublic{}
err := nodeKey.UnmarshalText([]byte(nodeKeyStr))
if err != nil {
return nil, err
}
if machineInterface, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(nodeKey)); ok {
if machineInterface, ok := h.registrationCache.Get(nodeKeyStr); ok {
if registrationMachine, ok := machineInterface.(Machine); ok {
namespace, err := h.GetNamespace(namespaceName)
if err != nil {

View File

@@ -604,8 +604,10 @@ func (h *Headscale) registerMachineForOIDCCallback(
namespace *Namespace,
nodeKey *key.NodePublic,
) error {
nodeKeyStr := NodePublicKeyStripPrefix(*nodeKey)
if _, err := h.RegisterMachineFromAuthCallback(
nodeKey.String(),
nodeKeyStr,
namespace.Name,
RegisterMethodOIDC,
); err != nil {

View File

@@ -2,7 +2,6 @@ package headscale
import (
"bytes"
_ "embed"
"html/template"
"net/http"
textTemplate "text/template"
@@ -12,18 +11,51 @@ import (
"github.com/rs/zerolog/log"
)
//go:embed templates/apple.html
var appleTemplate string
//go:embed templates/windows.html
var windowsTemplate string
// WindowsConfigMessage shows a simple message in the browser for how to configure the Windows Tailscale client.
func (h *Headscale) WindowsConfigMessage(
writer http.ResponseWriter,
req *http.Request,
) {
winTemplate := template.Must(template.New("windows").Parse(windowsTemplate))
winTemplate := template.Must(template.New("windows").Parse(`
<html>
<body>
<h1>headscale</h1>
<h2>Windows registry configuration</h2>
<p>
This page provides Windows registry information for the official Windows Tailscale client.
<p>
<p>
The registry file will configure Tailscale to use <code>{{.URL}}</code> as its control server.
<p>
<h3>Caution</h3>
<p>You should always download and inspect the registry file before installing it:</p>
<pre><code>curl {{.URL}}/windows/tailscale.reg</code></pre>
<h2>Installation</h2>
<p>Headscale can be set to the default server by running the registry file:</p>
<p>
<a href="/windows/tailscale.reg" download="tailscale.reg">Windows registry file</a>
</p>
<ol>
<li>Download the registry file, then run it</li>
<li>Follow the prompts</li>
<li>Install and run the official windows Tailscale client</li>
<li>When the installation has finished, start Tailscale, and log in by clicking the icon in the system tray</li>
</ol>
<p>Or</p>
<p>Open command prompt with Administrator rights. Issue the following commands to add the required registry entries:</p>
<pre>
<code>REG ADD "HKLM\Software\Tailscale IPN" /v UnattendedMode /t REG_SZ /d always
REG ADD "HKLM\Software\Tailscale IPN" /v LoginURL /t REG_SZ /d "{{.URL}}"</code></pre>
<p>
Restart Tailscale and log in.
<p>
</body>
</html>
`))
config := map[string]interface{}{
"URL": h.cfg.ServerURL,
}
@@ -104,7 +136,55 @@ func (h *Headscale) AppleConfigMessage(
writer http.ResponseWriter,
req *http.Request,
) {
appleTemplate := template.Must(template.New("apple").Parse(appleTemplate))
appleTemplate := template.Must(template.New("apple").Parse(`
<html>
<body>
<h1>headscale</h1>
<h2>Apple configuration profiles</h2>
<p>
This page provides <a href="https://support.apple.com/guide/mdm/mdm-overview-mdmbf9e668/web">configuration profiles</a> for the official Tailscale clients for <a href="https://apps.apple.com/us/app/tailscale/id1470499037?ls=1">iOS</a> and <a href="https://apps.apple.com/ca/app/tailscale/id1475387142?mt=12">macOS</a>.
</p>
<p>
The profiles will configure Tailscale.app to use <code>{{.URL}}</code> as its control server.
</p>
<h3>Caution</h3>
<p>You should always download and inspect the profile before installing it:</p>
<!--
<pre><code>curl {{.URL}}/apple/ios</code></pre>
-->
<pre><code>curl {{.URL}}/apple/macos</code></pre>
<h2>Profiles</h2>
<!--
<h3>iOS</h3>
<p>
<a href="/apple/ios" download="headscale_ios.mobileconfig">iOS profile</a>
</p>
-->
<h3>macOS</h3>
<p>Headscale can be set to the default server by installing a Headscale configuration profile:</p>
<p>
<a href="/apple/macos" download="headscale_macos.mobileconfig">macOS profile</a>
</p>
<ol>
<li>Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed</li>
<li>Open System Preferences and go to "Profiles"</li>
<li>Find and install the Headscale profile</li>
<li>Restart Tailscale.app and log in</li>
</ol>
<p>Or</p>
<p>Use your terminal to configure the default setting for Tailscale by issuing:</p>
<code>defaults write io.tailscale.ipn.macos ControlURL {{.URL}}</code>
<p>Restart Tailscale.app and log in.</p>
</body>
</html>`))
config := map[string]interface{}{
"URL": h.cfg.ServerURL,
@@ -202,33 +282,24 @@ func (h *Headscale) ApplePlatformConfig(
}
var payload bytes.Buffer
handleMacError := func(ierr error) {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(ierr).
Msg("Could not render Apple macOS template")
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusInternalServerError)
_, err := writer.Write([]byte("Could not render Apple macOS template"))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
switch platform {
case "macos-standlone":
if err := macosStandloneTemplate.Execute(&payload, platformConfig); err != nil {
handleMacError(err)
case "macos":
if err := macosTemplate.Execute(&payload, platformConfig); err != nil {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(err).
Msg("Could not render Apple macOS template")
return
}
case "macos-app-store":
if err := macosAppStoreTemplate.Execute(&payload, platformConfig); err != nil {
handleMacError(err)
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusInternalServerError)
_, err := writer.Write([]byte("Could not render Apple macOS template"))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
return
}
@@ -373,7 +444,7 @@ var iosTemplate = textTemplate.Must(textTemplate.New("iosTemplate").Parse(`
</dict>
`))
var macosAppStoreTemplate = template.Must(template.New("macosTemplate").Parse(`
var macosTemplate = template.Must(template.New("macosTemplate").Parse(`
<dict>
<key>PayloadType</key>
<string>io.tailscale.ipn.macos</string>
@@ -385,23 +456,7 @@ var macosAppStoreTemplate = template.Must(template.New("macosTemplate").Parse(`
<integer>1</integer>
<key>PayloadEnabled</key>
<true/>
<key>ControlURL</key>
<string>{{.URL}}</string>
</dict>
`))
var macosStandloneTemplate = template.Must(template.New("macosStandloneTemplate").Parse(`
<dict>
<key>PayloadType</key>
<string>io.tailscale.ipn.macsys</string>
<key>PayloadUUID</key>
<string>{{.UUID}}</string>
<key>PayloadIdentifier</key>
<string>com.github.juanfont.headscale</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true/>
<key>ControlURL</key>
<string>{{.URL}}</string>
</dict>

View File

@@ -435,10 +435,6 @@ func (h *Headscale) handleAuthKeyCommon(
resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser()
// Provide LoginName when registering with pre-auth key
// Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName*
resp.Login = *pak.Namespace.toLogin()
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
@@ -494,12 +490,12 @@ func (h *Headscale) handleNewMachineCommon(
resp.AuthURL = fmt.Sprintf(
"%s/oidc/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
registerRequest.NodeKey,
NodePublicKeyStripPrefix(registerRequest.NodeKey),
)
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
registerRequest.NodeKey)
NodePublicKeyStripPrefix(registerRequest.NodeKey))
}
respBody, err := h.marshalResponse(resp, machineKey)
@@ -730,7 +726,7 @@ func (h *Headscale) handleMachineExpiredCommon(
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
registerRequest.NodeKey)
NodePublicKeyStripPrefix(registerRequest.NodeKey))
}
respBody, err := h.marshalResponse(resp, machineKey)

View File

@@ -451,7 +451,7 @@ func (h *Headscale) pollNetMapStream(
Time("last_successful_update", lastUpdate).
Time("last_state_change", h.getLastStateChange(machine.Namespace)).
Msgf("There has been updates since the last successful update to %s", machine.Hostname)
data, err := h.getMapResponseData(mapRequest, machine, isNoise)
data, err := h.getMapResponseData(mapRequest, machine, false)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
@@ -622,7 +622,7 @@ func (h *Headscale) scheduledPollWorker(
defer closeChanWithLog(
keepAliveChan,
fmt.Sprint(ctx.Value(machineNameContextKey)),
"keepAliveChan",
"updateChan",
)
for {

View File

@@ -1,5 +1,3 @@
//go:build ts2019
package headscale
import (

View File

@@ -1,5 +1,3 @@
//go:build ts2019
package headscale
import (

View File

@@ -1,102 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>headscale</h1>
<h2>Apple configuration profiles</h2>
<p>
This page provides
<a href="https://support.apple.com/guide/mdm/mdm-overview-mdmbf9e668/web">
configuration profiles
</a>
for the official Tailscale clients for
</p>
<ul>
<li>
<a href="https://apps.apple.com/us/app/tailscale/id1470499037?ls=1"
>iOS</a
>
</li>
<li>
<a href="https://apps.apple.com/ca/app/tailscale/id1475387142?mt=12"
>macOS - AppStore Client</a
>.
</li>
<li>
<a href="https://pkgs.tailscale.com/stable/#macos"
>macOS - Standalone Client</a
>.
</li>
</ul>
<p>
The profiles will configure Tailscale.app to use <code>{{.URL}}</code> as
its control server.
</p>
<h3>Caution</h3>
<p>
You should always download and inspect the profile before installing it:
</p>
<!--
<pre><code>curl {{.URL}}/apple/ios</code></pre>
-->
<pre><code>curl {{.URL}}/apple/macos</code></pre>
<h2>Profiles</h2>
<!--
<h3>iOS</h3>
<p>
<a href="/apple/ios" download="headscale_ios.mobileconfig">iOS profile</a>
</p>
-->
<h3>macOS</h3>
<p>
Headscale can be set to the default server by installing a Headscale
configuration profile:
</p>
<p>
<a href="/apple/macos-app-store" download="headscale_macos.mobileconfig"
>macOS AppStore profile</a
>
<a href="/apple/macos-standalone" download="headscale_macos.mobileconfig"
>macOS Standalone profile</a
>
</p>
<ol>
<li>
Download the profile, then open it. When it has been opened, there
should be a notification that a profile can be installed
</li>
<li>Open System Preferences and go to "Profiles"</li>
<li>Find and install the Headscale profile</li>
<li>Restart Tailscale.app and log in</li>
</ol>
<p>Or</p>
<p>
Use your terminal to configure the default setting for Tailscale by
issuing:
</p>
<ul>
<li>
for app store client:
<code>defaults write io.tailscale.ipn.macos ControlURL {{.URL}}</code>
</li>
<li>
for standlone client:
<code>defaults write io.tailscale.ipn.macsys ControlURL {{.URL}}</code>
</li>
</ul>
<p>Restart Tailscale.app and log in.</p>
</body>
</html>

View File

@@ -1,64 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>headscale</h1>
<h2>Windows registry configuration</h2>
<p>
This page provides Windows registry information for the official Windows
Tailscale client.
</p>
<p></p>
<p>
The registry file will configure Tailscale to use <code>{{.URL}}</code> as
its control server.
</p>
<p></p>
<h3>Caution</h3>
<p>
You should always download and inspect the registry file before installing
it:
</p>
<pre><code>curl {{.URL}}/windows/tailscale.reg</code></pre>
<h2>Installation</h2>
<p>
Headscale can be set to the default server by running the registry file:
</p>
<p>
<a href="/windows/tailscale.reg" download="tailscale.reg"
>Windows registry file</a
>
</p>
<ol>
<li>Download the registry file, then run it</li>
<li>Follow the prompts</li>
<li>Install and run the official windows Tailscale client</li>
<li>
When the installation has finished, start Tailscale, and log in by
clicking the icon in the system tray
</li>
</ol>
<p>Or</p>
<p>
Open command prompt with Administrator rights. Issue the following
commands to add the required registry entries:
</p>
<pre>
<code>REG ADD "HKLM\Software\Tailscale IPN" /v UnattendedMode /t REG_SZ /d always
REG ADD "HKLM\Software\Tailscale IPN" /v LoginURL /t REG_SZ /d "{{.URL}}"</code></pre>
<p>Restart Tailscale and log in.</p>
<p></p>
</body>
</html>

View File

@@ -347,7 +347,7 @@ func IsStringInSlice(slice []string, str string) bool {
}
func AbsolutePathFromConfigPath(path string) string {
// If a relative path is provided, prefix it with the directory where
// If a relative path is provided, prefix it with the the directory where
// the config file was found.
if (path != "") && !strings.HasPrefix(path, string(os.PathSeparator)) {
dir, _ := filepath.Split(viper.ConfigFileUsed())