Skip to content

Commit

Permalink
initial capver packet tracking version (#2391)
Browse files Browse the repository at this point in the history
* initial capver packet tracking version

Signed-off-by: Kristoffer Dalby <[email protected]>

* Log the minimum version as client version, not only capver

Signed-off-by: Kristoffer Dalby <[email protected]>

* remove old versions

Signed-off-by: Kristoffer Dalby <[email protected]>

* use capver for integration tests

Signed-off-by: Kristoffer Dalby <[email protected]>

* changelog

Signed-off-by: Kristoffer Dalby <[email protected]>

* patch through m and n key

Signed-off-by: Kristoffer Dalby <[email protected]>

---------

Signed-off-by: Kristoffer Dalby <[email protected]>
  • Loading branch information
kradalby authored Jan 30, 2025
1 parent cd3b8e6 commit e172c29
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 68 deletions.
32 changes: 19 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- `oidc.map_legacy_users` is now `false` by default
[#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)

Expand All @@ -24,8 +26,8 @@
[#2367](https://github.com/juanfont/headscale/pull/2367)
- Relax username validation to allow emails
[#2364](https://github.com/juanfont/headscale/pull/2364)
- Remove invalid routes and add stronger constraints for routes to avoid API panic
[#2371](https://github.com/juanfont/headscale/pull/2371)
- Remove invalid routes and add stronger constraints for routes to avoid API
panic [#2371](https://github.com/juanfont/headscale/pull/2371)
- Fix panic when `derp.update_frequency` is 0
[#2368](https://github.com/juanfont/headscale/pull/2368)

Expand Down Expand Up @@ -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.

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
secured.
legacy (existing) OIDC accounts _need to be migrated_ to be properly secured.

#### What do I need to do to migrate?

Expand All @@ -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
`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 will be ignored, meaning that the users will get new accounts instead
of being migrated.
emails will be ignored, meaning that the users will get new accounts instead of
being migrated.

After this exception is ensured, make all users log into Headscale with their
account, and Headscale will automatically update the account record. This will
Expand Down Expand Up @@ -175,7 +176,8 @@ This will also affect the way you
- 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
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.

### Changes
Expand All @@ -197,9 +199,12 @@ This will also affect the way you
- CLI for managing users now accepts `--identifier` in addition to `--name`,
usage of `--identifier` is recommended
[#2261](https://github.com/juanfont/headscale/pull/2261)
- Add `dns.extra_records_path` configuration option [#2262](https://github.com/juanfont/headscale/issues/2262)
- 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)
- Add `dns.extra_records_path` configuration option
[#2262](https://github.com/juanfont/headscale/issues/2262)
- 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)

Expand Down Expand Up @@ -730,8 +735,8 @@ behaviour.
- All machines can communicate with all machines by default
- Tags should now work correctly and adding a host to Headscale should now
reload the rules.
- The documentation have a [fictional example](./docs/ref/acls.md) that should cover
some use cases of the ACLs features
- The documentation have a [fictional example](./docs/ref/acls.md) that should
cover some use cases of the ACLs features

### Features

Expand All @@ -749,7 +754,8 @@ behaviour.

- Add IPv6 support to the prefix assigned to namespaces
- 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)
- OpenID Connect users will be mapped per namespaces
- Each user will get its own namespace, created if it does not exist
Expand Down
6 changes: 6 additions & 0 deletions hscontrol/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
grpcRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/juanfont/headscale"
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/derp"
derpServer "github.com/juanfont/headscale/hscontrol/derp/server"
Expand Down Expand Up @@ -560,6 +561,11 @@ func (h *Headscale) Serve() error {
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
h.DERPMap = derp.GetDERPMap(h.cfg.DERP)
h.mapper = mapper.NewMapper(h.db, h.cfg, h.DERPMap, h.nodeNotifier, h.polMan)
Expand Down
92 changes: 92 additions & 0 deletions hscontrol/capver/capver.go
Original file line number Diff line number Diff line change
@@ -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 changes: 54 additions & 0 deletions hscontrol/capver/capver_generated.go
Original file line number Diff line number Diff line change
@@ -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 changes: 53 additions & 0 deletions hscontrol/capver/capver_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
Loading

0 comments on commit e172c29

Please sign in to comment.