mirror of
https://github.com/juanfont/headscale.git
synced 2025-02-08 10:18:01 +09:00
initial capver packet tracking version (#2391)
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=386 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm GOOS=linux GOARM=5) (push) Has been cancelled
Build / build-cross (GOARCH=arm GOOS=linux GOARM=6) (push) Has been cancelled
Build / build-cross (GOARCH=arm GOOS=linux GOARM=7) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Tests / test (push) Has been cancelled
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=386 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm GOOS=linux GOARM=5) (push) Has been cancelled
Build / build-cross (GOARCH=arm GOOS=linux GOARM=6) (push) Has been cancelled
Build / build-cross (GOARCH=arm GOOS=linux GOARM=7) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Tests / test (push) Has been cancelled
* initial capver packet tracking version Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * Log the minimum version as client version, not only capver Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * remove old versions Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * use capver for integration tests Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * changelog Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * patch through m and n key Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
cd3b8e68ff
commit
e172c29360
8 changed files with 397 additions and 68 deletions
32
CHANGELOG.md
32
CHANGELOG.md
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
- `oidc.map_legacy_users` is now `false` by default
|
- `oidc.map_legacy_users` is now `false` by default
|
||||||
[#2350](https://github.com/juanfont/headscale/pull/2350)
|
[#2350](https://github.com/juanfont/headscale/pull/2350)
|
||||||
|
- Print Tailscale version instead of capability versions for outdated nodes
|
||||||
|
[#2391](https://github.com/juanfont/headscale/pull/2391)
|
||||||
|
|
||||||
## 0.24.2 (2025-01-30)
|
## 0.24.2 (2025-01-30)
|
||||||
|
|
||||||
|
@ -24,8 +26,8 @@
|
||||||
[#2367](https://github.com/juanfont/headscale/pull/2367)
|
[#2367](https://github.com/juanfont/headscale/pull/2367)
|
||||||
- Relax username validation to allow emails
|
- Relax username validation to allow emails
|
||||||
[#2364](https://github.com/juanfont/headscale/pull/2364)
|
[#2364](https://github.com/juanfont/headscale/pull/2364)
|
||||||
- Remove invalid routes and add stronger constraints for routes to avoid API panic
|
- Remove invalid routes and add stronger constraints for routes to avoid API
|
||||||
[#2371](https://github.com/juanfont/headscale/pull/2371)
|
panic [#2371](https://github.com/juanfont/headscale/pull/2371)
|
||||||
- Fix panic when `derp.update_frequency` is 0
|
- Fix panic when `derp.update_frequency` is 0
|
||||||
[#2368](https://github.com/juanfont/headscale/pull/2368)
|
[#2368](https://github.com/juanfont/headscale/pull/2368)
|
||||||
|
|
||||||
|
@ -60,8 +62,7 @@ and have it populate to Headscale automatically the next time they log in.
|
||||||
However, this may affect the way you reference users in policies.
|
However, this may affect the way you reference users in policies.
|
||||||
|
|
||||||
Headscale v0.23.0 and earlier never recorded the `iss` and `sub` fields, so all
|
Headscale v0.23.0 and earlier never recorded the `iss` and `sub` fields, so all
|
||||||
legacy (existing) OIDC accounts _need to be migrated_ to be properly
|
legacy (existing) OIDC accounts _need to be migrated_ to be properly secured.
|
||||||
secured.
|
|
||||||
|
|
||||||
#### What do I need to do to migrate?
|
#### What do I need to do to migrate?
|
||||||
|
|
||||||
|
@ -73,8 +74,8 @@ The migration will mostly be done automatically, with one exception. If your
|
||||||
OIDC does not provide an `email_verified` claim, Headscale will ignore the
|
OIDC does not provide an `email_verified` claim, Headscale will ignore the
|
||||||
`email`. This means that either the administrator will have to mark the user
|
`email`. This means that either the administrator will have to mark the user
|
||||||
emails as verified, or ensure the users verify their emails. Any unverified
|
emails as verified, or ensure the users verify their emails. Any unverified
|
||||||
emails will be ignored, meaning that the users will get new accounts instead
|
emails will be ignored, meaning that the users will get new accounts instead of
|
||||||
of being migrated.
|
being migrated.
|
||||||
|
|
||||||
After this exception is ensured, make all users log into Headscale with their
|
After this exception is ensured, make all users log into Headscale with their
|
||||||
account, and Headscale will automatically update the account record. This will
|
account, and Headscale will automatically update the account record. This will
|
||||||
|
@ -175,7 +176,8 @@ This will also affect the way you
|
||||||
- User gRPC/API [#2261](https://github.com/juanfont/headscale/pull/2261):
|
- User gRPC/API [#2261](https://github.com/juanfont/headscale/pull/2261):
|
||||||
- If you depend on a Headscale Web UI, you should wait with this update until
|
- If you depend on a Headscale Web UI, you should wait with this update until
|
||||||
the UI have been updated to match the new API.
|
the UI have been updated to match the new API.
|
||||||
- `GET /api/v1/user/{name}` and `GetUser` have been removed in favour of `ListUsers` with an ID parameter
|
- `GET /api/v1/user/{name}` and `GetUser` have been removed in favour of
|
||||||
|
`ListUsers` with an ID parameter
|
||||||
- `RenameUser` and `DeleteUser` now require an ID instead of a name.
|
- `RenameUser` and `DeleteUser` now require an ID instead of a name.
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
@ -197,9 +199,12 @@ This will also affect the way you
|
||||||
- CLI for managing users now accepts `--identifier` in addition to `--name`,
|
- CLI for managing users now accepts `--identifier` in addition to `--name`,
|
||||||
usage of `--identifier` is recommended
|
usage of `--identifier` is recommended
|
||||||
[#2261](https://github.com/juanfont/headscale/pull/2261)
|
[#2261](https://github.com/juanfont/headscale/pull/2261)
|
||||||
- Add `dns.extra_records_path` configuration option [#2262](https://github.com/juanfont/headscale/issues/2262)
|
- Add `dns.extra_records_path` configuration option
|
||||||
- Support client verify for DERP [#2046](https://github.com/juanfont/headscale/pull/2046)
|
[#2262](https://github.com/juanfont/headscale/issues/2262)
|
||||||
- Add PKCE Verifier for OIDC [#2314](https://github.com/juanfont/headscale/pull/2314)
|
- Support client verify for DERP
|
||||||
|
[#2046](https://github.com/juanfont/headscale/pull/2046)
|
||||||
|
- Add PKCE Verifier for OIDC
|
||||||
|
[#2314](https://github.com/juanfont/headscale/pull/2314)
|
||||||
|
|
||||||
## 0.23.0 (2024-09-18)
|
## 0.23.0 (2024-09-18)
|
||||||
|
|
||||||
|
@ -730,8 +735,8 @@ behaviour.
|
||||||
- All machines can communicate with all machines by default
|
- All machines can communicate with all machines by default
|
||||||
- Tags should now work correctly and adding a host to Headscale should now
|
- Tags should now work correctly and adding a host to Headscale should now
|
||||||
reload the rules.
|
reload the rules.
|
||||||
- The documentation have a [fictional example](./docs/ref/acls.md) that should cover
|
- The documentation have a [fictional example](./docs/ref/acls.md) that should
|
||||||
some use cases of the ACLs features
|
cover some use cases of the ACLs features
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
@ -749,7 +754,8 @@ behaviour.
|
||||||
|
|
||||||
- Add IPv6 support to the prefix assigned to namespaces
|
- Add IPv6 support to the prefix assigned to namespaces
|
||||||
- Add API Key support
|
- Add API Key support
|
||||||
- Enable remote control of `headscale` via CLI [docs](./docs/ref/remote-cli.md)
|
- Enable remote control of `headscale` via CLI
|
||||||
|
[docs](./docs/ref/remote-cli.md)
|
||||||
- Enable HTTP API (beta, subject to change)
|
- Enable HTTP API (beta, subject to change)
|
||||||
- OpenID Connect users will be mapped per namespaces
|
- OpenID Connect users will be mapped per namespaces
|
||||||
- Each user will get its own namespace, created if it does not exist
|
- Each user will get its own namespace, created if it does not exist
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
grpcRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
grpcRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
"github.com/juanfont/headscale"
|
"github.com/juanfont/headscale"
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/capver"
|
||||||
"github.com/juanfont/headscale/hscontrol/db"
|
"github.com/juanfont/headscale/hscontrol/db"
|
||||||
"github.com/juanfont/headscale/hscontrol/derp"
|
"github.com/juanfont/headscale/hscontrol/derp"
|
||||||
derpServer "github.com/juanfont/headscale/hscontrol/derp/server"
|
derpServer "github.com/juanfont/headscale/hscontrol/derp/server"
|
||||||
|
@ -560,6 +561,11 @@ func (h *Headscale) Serve() error {
|
||||||
spew.Dump(h.cfg)
|
spew.Dump(h.cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info().
|
||||||
|
Caller().
|
||||||
|
Str("minimum_version", capver.TailscaleVersion(MinimumCapVersion)).
|
||||||
|
Msg("Clients with a lower minimum version will be rejected")
|
||||||
|
|
||||||
// Fetch an initial DERP Map before we start serving
|
// Fetch an initial DERP Map before we start serving
|
||||||
h.DERPMap = derp.GetDERPMap(h.cfg.DERP)
|
h.DERPMap = derp.GetDERPMap(h.cfg.DERP)
|
||||||
h.mapper = mapper.NewMapper(h.db, h.cfg, h.DERPMap, h.nodeNotifier, h.polMan)
|
h.mapper = mapper.NewMapper(h.db, h.cfg, h.DERPMap, h.nodeNotifier, h.polMan)
|
||||||
|
|
92
hscontrol/capver/capver.go
Normal file
92
hscontrol/capver/capver.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package capver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
xmaps "golang.org/x/exp/maps"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/util/set"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tailscaleVersSorted() []string {
|
||||||
|
vers := xmaps.Keys(tailscaleToCapVer)
|
||||||
|
sort.Strings(vers)
|
||||||
|
return vers
|
||||||
|
}
|
||||||
|
|
||||||
|
func capVersSorted() []tailcfg.CapabilityVersion {
|
||||||
|
capVers := xmaps.Keys(capVerToTailscaleVer)
|
||||||
|
sort.Slice(capVers, func(i, j int) bool {
|
||||||
|
return capVers[i] < capVers[j]
|
||||||
|
})
|
||||||
|
return capVers
|
||||||
|
}
|
||||||
|
|
||||||
|
// TailscaleVersion returns the Tailscale version for the given CapabilityVersion.
|
||||||
|
func TailscaleVersion(ver tailcfg.CapabilityVersion) string {
|
||||||
|
return capVerToTailscaleVer[ver]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CapabilityVersion returns the CapabilityVersion for the given Tailscale version.
|
||||||
|
func CapabilityVersion(ver string) tailcfg.CapabilityVersion {
|
||||||
|
if !strings.HasPrefix(ver, "v") {
|
||||||
|
ver = "v" + ver
|
||||||
|
}
|
||||||
|
return tailscaleToCapVer[ver]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TailscaleLatest returns the n latest Tailscale versions.
|
||||||
|
func TailscaleLatest(n int) []string {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tsSorted := tailscaleVersSorted()
|
||||||
|
|
||||||
|
if n > len(tsSorted) {
|
||||||
|
return tsSorted
|
||||||
|
}
|
||||||
|
|
||||||
|
return tsSorted[len(tsSorted)-n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TailscaleLatestMajorMinor returns the n latest Tailscale versions (e.g. 1.80).
|
||||||
|
func TailscaleLatestMajorMinor(n int, stripV bool) []string {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
majors := set.Set[string]{}
|
||||||
|
for _, vers := range tailscaleVersSorted() {
|
||||||
|
if stripV {
|
||||||
|
vers = strings.TrimPrefix(vers, "v")
|
||||||
|
}
|
||||||
|
v := strings.Split(vers, ".")
|
||||||
|
majors.Add(v[0] + "." + v[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
majorSl := majors.Slice()
|
||||||
|
sort.Strings(majorSl)
|
||||||
|
|
||||||
|
if n > len(majorSl) {
|
||||||
|
return majorSl
|
||||||
|
}
|
||||||
|
|
||||||
|
return majorSl[len(majorSl)-n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CapVerLatest returns the n latest CapabilityVersions.
|
||||||
|
func CapVerLatest(n int) []tailcfg.CapabilityVersion {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s := capVersSorted()
|
||||||
|
|
||||||
|
if n > len(s) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return s[len(s)-n:]
|
||||||
|
}
|
54
hscontrol/capver/capver_generated.go
Normal file
54
hscontrol/capver/capver_generated.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package capver
|
||||||
|
|
||||||
|
//Generated DO NOT EDIT
|
||||||
|
|
||||||
|
import "tailscale.com/tailcfg"
|
||||||
|
|
||||||
|
var tailscaleToCapVer = map[string]tailcfg.CapabilityVersion{
|
||||||
|
"v1.44.3": 63,
|
||||||
|
"v1.56.1": 82,
|
||||||
|
"v1.58.0": 85,
|
||||||
|
"v1.58.1": 85,
|
||||||
|
"v1.58.2": 85,
|
||||||
|
"v1.60.0": 87,
|
||||||
|
"v1.60.1": 87,
|
||||||
|
"v1.62.0": 88,
|
||||||
|
"v1.62.1": 88,
|
||||||
|
"v1.64.0": 90,
|
||||||
|
"v1.64.1": 90,
|
||||||
|
"v1.64.2": 90,
|
||||||
|
"v1.66.0": 95,
|
||||||
|
"v1.66.1": 95,
|
||||||
|
"v1.66.2": 95,
|
||||||
|
"v1.66.3": 95,
|
||||||
|
"v1.66.4": 95,
|
||||||
|
"v1.68.0": 97,
|
||||||
|
"v1.68.1": 97,
|
||||||
|
"v1.68.2": 97,
|
||||||
|
"v1.70.0": 102,
|
||||||
|
"v1.72.0": 104,
|
||||||
|
"v1.72.1": 104,
|
||||||
|
"v1.74.0": 106,
|
||||||
|
"v1.74.1": 106,
|
||||||
|
"v1.76.0": 106,
|
||||||
|
"v1.76.1": 106,
|
||||||
|
"v1.76.6": 106,
|
||||||
|
"v1.78.0": 109,
|
||||||
|
"v1.78.1": 109,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var capVerToTailscaleVer = map[tailcfg.CapabilityVersion]string{
|
||||||
|
63: "v1.44.3",
|
||||||
|
82: "v1.56.1",
|
||||||
|
85: "v1.58.0",
|
||||||
|
87: "v1.60.0",
|
||||||
|
88: "v1.62.0",
|
||||||
|
90: "v1.64.0",
|
||||||
|
95: "v1.66.0",
|
||||||
|
97: "v1.68.0",
|
||||||
|
102: "v1.70.0",
|
||||||
|
104: "v1.72.0",
|
||||||
|
106: "v1.74.0",
|
||||||
|
109: "v1.78.0",
|
||||||
|
}
|
53
hscontrol/capver/capver_test.go
Normal file
53
hscontrol/capver/capver_test.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package capver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTailscaleLatestMajorMinor(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
n int
|
||||||
|
stripV bool
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{3, false, []string{"v1.74", "v1.76", "v1.78"}},
|
||||||
|
{2, true, []string{"1.76", "1.78"}},
|
||||||
|
{0, false, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
output := TailscaleLatestMajorMinor(test.n, test.stripV)
|
||||||
|
if diff := cmp.Diff(output, test.expected); diff != "" {
|
||||||
|
t.Errorf("TailscaleLatestMajorMinor(%d, %v) mismatch (-want +got):\n%s", test.n, test.stripV, diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCapVerMinimumTailscaleVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input tailcfg.CapabilityVersion
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{85, "v1.58.0"},
|
||||||
|
{90, "v1.64.0"},
|
||||||
|
{95, "v1.66.0"},
|
||||||
|
{106, "v1.74.0"},
|
||||||
|
{109, "v1.78.0"},
|
||||||
|
{9001, ""}, // Test case for a version higher than any in the map
|
||||||
|
{60, ""}, // Test case for a version lower than any in the map
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
output := TailscaleVersion(test.input)
|
||||||
|
if output != test.expected {
|
||||||
|
t.Errorf("CapVerFromTailscaleVersion(%d) = %s; want %s", test.input, output, test.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
157
hscontrol/capver/gen/main.go
Normal file
157
hscontrol/capver/gen/main.go
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
//go:generate go run main.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
xmaps "golang.org/x/exp/maps"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
releasesURL = "https://api.github.com/repos/tailscale/tailscale/releases"
|
||||||
|
rawFileURL = "https://github.com/tailscale/tailscale/raw/refs/tags/%s/tailcfg/tailcfg.go"
|
||||||
|
outputFile = "../capver_generated.go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Release struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCapabilityVersions() (map[string]tailcfg.CapabilityVersion, error) {
|
||||||
|
// Fetch the releases
|
||||||
|
resp, err := http.Get(releasesURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error fetching releases: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading response body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var releases []Release
|
||||||
|
err = json.Unmarshal(body, &releases)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular expression to find the CurrentCapabilityVersion line
|
||||||
|
re := regexp.MustCompile(`const CurrentCapabilityVersion CapabilityVersion = (\d+)`)
|
||||||
|
|
||||||
|
versions := make(map[string]tailcfg.CapabilityVersion)
|
||||||
|
|
||||||
|
for _, release := range releases {
|
||||||
|
version := strings.TrimSpace(release.Name)
|
||||||
|
if !strings.HasPrefix(version, "v") {
|
||||||
|
version = "v" + version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the raw Go file
|
||||||
|
rawURL := fmt.Sprintf(rawFileURL, version)
|
||||||
|
resp, err := http.Get(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error fetching raw file for version %s: %v\n", version, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading raw file for version %s: %v\n", version, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the CurrentCapabilityVersion
|
||||||
|
matches := re.FindStringSubmatch(string(body))
|
||||||
|
if len(matches) > 1 {
|
||||||
|
capabilityVersionStr := matches[1]
|
||||||
|
capabilityVersion, _ := strconv.Atoi(capabilityVersionStr)
|
||||||
|
versions[version] = tailcfg.CapabilityVersion(capabilityVersion)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Version: %s, CurrentCapabilityVersion not found\n", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeCapabilityVersionsToFile(versions map[string]tailcfg.CapabilityVersion) error {
|
||||||
|
// Open the output file
|
||||||
|
file, err := os.Create(outputFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating file: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Write the package declaration and variable
|
||||||
|
file.WriteString("package capver\n\n")
|
||||||
|
file.WriteString("//Generated DO NOT EDIT\n\n")
|
||||||
|
file.WriteString(`import "tailscale.com/tailcfg"`)
|
||||||
|
file.WriteString("\n\n")
|
||||||
|
file.WriteString("var tailscaleToCapVer = map[string]tailcfg.CapabilityVersion{\n")
|
||||||
|
|
||||||
|
sortedVersions := xmaps.Keys(versions)
|
||||||
|
sort.Strings(sortedVersions)
|
||||||
|
for _, version := range sortedVersions {
|
||||||
|
file.WriteString(fmt.Sprintf("\t\"%s\": %d,\n", version, versions[version]))
|
||||||
|
}
|
||||||
|
file.WriteString("}\n")
|
||||||
|
|
||||||
|
file.WriteString("\n\n")
|
||||||
|
file.WriteString("var capVerToTailscaleVer = map[tailcfg.CapabilityVersion]string{\n")
|
||||||
|
|
||||||
|
capVarToTailscaleVer := make(map[tailcfg.CapabilityVersion]string)
|
||||||
|
for _, v := range sortedVersions {
|
||||||
|
cap := versions[v]
|
||||||
|
log.Printf("cap for v: %d, %s", cap, v)
|
||||||
|
|
||||||
|
// If it is already set, skip and continue,
|
||||||
|
// we only want the first tailscale vsion per
|
||||||
|
// capability vsion.
|
||||||
|
if _, ok := capVarToTailscaleVer[cap]; ok {
|
||||||
|
log.Printf("Skipping %d, %s", cap, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("Storing %d, %s", cap, v)
|
||||||
|
capVarToTailscaleVer[cap] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
capsSorted := xmaps.Keys(capVarToTailscaleVer)
|
||||||
|
sort.Slice(capsSorted, func(i, j int) bool {
|
||||||
|
return capsSorted[i] < capsSorted[j]
|
||||||
|
})
|
||||||
|
for _, capVer := range capsSorted {
|
||||||
|
file.WriteString(fmt.Sprintf("\t%d:\t\t\"%s\",\n", capVer, capVarToTailscaleVer[capVer]))
|
||||||
|
}
|
||||||
|
file.WriteString("}\n")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
versions, err := getCapabilityVersions()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeCapabilityVersionsToFile(versions)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error writing to file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Capability versions written to", outputFile)
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/capver"
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
|
@ -155,10 +156,19 @@ func isSupportedVersion(version tailcfg.CapabilityVersion) bool {
|
||||||
return version >= MinimumCapVersion
|
return version >= MinimumCapVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
func rejectUnsupported(writer http.ResponseWriter, version tailcfg.CapabilityVersion) bool {
|
func rejectUnsupported(writer http.ResponseWriter, version tailcfg.CapabilityVersion, mkey key.MachinePublic, nkey key.NodePublic) bool {
|
||||||
// Reject unsupported versions
|
// Reject unsupported versions
|
||||||
if !isSupportedVersion(version) {
|
if !isSupportedVersion(version) {
|
||||||
httpError(writer, nil, "unsupported client version", http.StatusBadRequest)
|
log.Error().
|
||||||
|
Caller().
|
||||||
|
Int("minimum_cap_ver", int(MinimumCapVersion)).
|
||||||
|
Int("client_cap_ver", int(version)).
|
||||||
|
Str("minimum_version", capver.TailscaleVersion(MinimumCapVersion)).
|
||||||
|
Str("client_version", capver.TailscaleVersion(version)).
|
||||||
|
Str("node_key", nkey.ShortString()).
|
||||||
|
Str("machine_key", mkey.ShortString()).
|
||||||
|
Msg("unsupported client connected")
|
||||||
|
http.Error(writer, "unsupported client version", http.StatusBadRequest)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -188,7 +198,7 @@ func (ns *noiseServer) NoisePollNetMapHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject unsupported versions
|
// Reject unsupported versions
|
||||||
if rejectUnsupported(writer, mapRequest.Version) {
|
if rejectUnsupported(writer, mapRequest.Version, ns.machineKey, mapRequest.NodeKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +243,7 @@ func (ns *noiseServer) NoiseRegistrationHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject unsupported versions
|
// Reject unsupported versions
|
||||||
if rejectUnsupported(writer, registerRequest.Version) {
|
if rejectUnsupported(writer, registerRequest.Version, ns.machineKey, registerRequest.NodeKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/capver"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"github.com/juanfont/headscale/integration/dockertestutil"
|
"github.com/juanfont/headscale/integration/dockertestutil"
|
||||||
"github.com/juanfont/headscale/integration/dsic"
|
"github.com/juanfont/headscale/integration/dsic"
|
||||||
|
@ -51,53 +52,6 @@ var (
|
||||||
errNoUserAvailable = errors.New("no user available")
|
errNoUserAvailable = errors.New("no user available")
|
||||||
errNoClientFound = errors.New("client not found")
|
errNoClientFound = errors.New("client not found")
|
||||||
|
|
||||||
// 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).
|
|
||||||
tailscaleVersions2021 = map[string]bool{
|
|
||||||
"head": true,
|
|
||||||
"unstable": true,
|
|
||||||
"1.74": true, // CapVer: 106
|
|
||||||
"1.72": true, // CapVer: 104
|
|
||||||
"1.70": true, // CapVer: 102
|
|
||||||
"1.68": true, // CapVer: 97
|
|
||||||
"1.66": true, // CapVer: 95
|
|
||||||
"1.64": true, // CapVer: 90
|
|
||||||
"1.62": true, // CapVer: 88
|
|
||||||
"1.60": true, // CapVer: 87
|
|
||||||
"1.58": true, // CapVer: 85
|
|
||||||
"1.56": true, // Oldest supported version, CapVer: 82
|
|
||||||
"1.54": false, // CapVer: 79
|
|
||||||
"1.52": false, // CapVer: 79
|
|
||||||
"1.50": false, // CapVer: 74
|
|
||||||
"1.48": false, // CapVer: 68
|
|
||||||
"1.46": false, // CapVer: 65
|
|
||||||
"1.44": false, // CapVer: 63
|
|
||||||
"1.42": false, // CapVer: 61
|
|
||||||
"1.40": false, // CapVer: 61
|
|
||||||
"1.38": false, // CapVer: 58
|
|
||||||
"1.36": false, // CapVer: 56
|
|
||||||
"1.34": false, // CapVer: 51
|
|
||||||
"1.32": false, // CapVer: 46
|
|
||||||
"1.30": false,
|
|
||||||
}
|
|
||||||
|
|
||||||
tailscaleVersions2019 = map[string]bool{
|
|
||||||
"1.28": false,
|
|
||||||
"1.26": false,
|
|
||||||
"1.24": false, // Tailscale SSH
|
|
||||||
"1.22": false,
|
|
||||||
"1.20": false,
|
|
||||||
"1.18": false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// tailscaleVersionsUnavailable = []string{
|
|
||||||
// // These versions seem to fail when fetching from apt.
|
|
||||||
// "1.14.6",
|
|
||||||
// "1.12.4",
|
|
||||||
// "1.10.2",
|
|
||||||
// "1.8.7",
|
|
||||||
// }.
|
|
||||||
|
|
||||||
// AllVersions represents a list of Tailscale versions the suite
|
// AllVersions represents a list of Tailscale versions the suite
|
||||||
// uses to test compatibility with the ControlServer.
|
// uses to test compatibility with the ControlServer.
|
||||||
//
|
//
|
||||||
|
@ -107,10 +61,7 @@ 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([]string{"head", "unstable"}, capver.TailscaleLatestMajorMinor(10, true)...)
|
||||||
enabledVersions(tailscaleVersions2021),
|
|
||||||
enabledVersions(tailscaleVersions2019)...,
|
|
||||||
)
|
|
||||||
|
|
||||||
// MustTestVersions is the minimum set of versions we should test.
|
// MustTestVersions is the minimum set of versions we should test.
|
||||||
// At the moment, this is arbitrarily chosen as:
|
// At the moment, this is arbitrarily chosen as:
|
||||||
|
|
Loading…
Reference in a new issue