mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Use tailscale key types instead of strings (#1609)
* upgrade tailscale Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * make Node object use actualy tailscale key types This commit changes the Node struct to have both a field for strings to store the keys in the database and a dedicated Key for each type of key. The keys are populated and stored with Gorm hooks to ensure the data is stored in the db. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * use key types throughout the code Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * make sure machinekey is concistently used Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * use machine key in auth url Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * fix web register Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * use key type in notifier Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * fix relogin with webauth Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
		
							parent
							
								
									c0fd06e3f5
								
							
						
					
					
						commit
						ed4e19996b
					
				@ -4,10 +4,10 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
						v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
				
			||||||
	"github.com/juanfont/headscale/hscontrol/util"
 | 
					 | 
				
			||||||
	"github.com/rs/zerolog/log"
 | 
						"github.com/rs/zerolog/log"
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
	"google.golang.org/grpc/status"
 | 
						"google.golang.org/grpc/status"
 | 
				
			||||||
 | 
						"tailscale.com/types/key"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@ -93,11 +93,13 @@ var createNodeCmd = &cobra.Command{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !util.NodePublicKeyRegex.Match([]byte(machineKey)) {
 | 
					
 | 
				
			||||||
			err = errPreAuthKeyMalformed
 | 
							var mkey key.MachinePublic
 | 
				
			||||||
 | 
							err = mkey.UnmarshalText([]byte(machineKey))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
			ErrorOutput(
 | 
								ErrorOutput(
 | 
				
			||||||
				err,
 | 
									err,
 | 
				
			||||||
				fmt.Sprintf("Error: %s", err),
 | 
									fmt.Sprintf("Failed to parse machine key from flag: %s", err),
 | 
				
			||||||
				output,
 | 
									output,
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          # When updating go.mod or go.sum, a new sha will need to be calculated,
 | 
					          # When updating go.mod or go.sum, a new sha will need to be calculated,
 | 
				
			||||||
          # update this if you have a mismatch after doing a change to thos files.
 | 
					          # update this if you have a mismatch after doing a change to thos files.
 | 
				
			||||||
          vendorSha256 = "sha256-Q6eySc8lXYhkWka7Y+qOM6viv7QhdjFZDX8PttaLfr4=";
 | 
					          vendorSha256 = "sha256-SYb2LCCZT/p1UHwB1b0IHPfk6Sxh3UYkR4r+KjkL4F8=";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"];
 | 
					          ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"];
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										67
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								go.mod
									
									
									
									
									
								
							@ -16,14 +16,14 @@ require (
 | 
				
			|||||||
	github.com/gorilla/mux v1.8.0
 | 
						github.com/gorilla/mux v1.8.0
 | 
				
			||||||
	github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
 | 
						github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
 | 
				
			||||||
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
 | 
						github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
 | 
				
			||||||
	github.com/klauspost/compress v1.16.7
 | 
						github.com/klauspost/compress v1.17.0
 | 
				
			||||||
	github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282
 | 
						github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282
 | 
				
			||||||
	github.com/ory/dockertest/v3 v3.9.1
 | 
						github.com/ory/dockertest/v3 v3.9.1
 | 
				
			||||||
	github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
						github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
				
			||||||
	github.com/philip-bui/grpc-zerolog v1.0.1
 | 
						github.com/philip-bui/grpc-zerolog v1.0.1
 | 
				
			||||||
	github.com/pkg/profile v1.7.0
 | 
						github.com/pkg/profile v1.7.0
 | 
				
			||||||
	github.com/prometheus/client_golang v1.15.1
 | 
						github.com/prometheus/client_golang v1.17.0
 | 
				
			||||||
	github.com/prometheus/common v0.42.0
 | 
						github.com/prometheus/common v0.44.0
 | 
				
			||||||
	github.com/pterm/pterm v0.12.58
 | 
						github.com/pterm/pterm v0.12.58
 | 
				
			||||||
	github.com/puzpuzpuz/xsync/v2 v2.4.0
 | 
						github.com/puzpuzpuz/xsync/v2 v2.4.0
 | 
				
			||||||
	github.com/rs/zerolog v1.29.0
 | 
						github.com/rs/zerolog v1.29.0
 | 
				
			||||||
@ -33,19 +33,19 @@ require (
 | 
				
			|||||||
	github.com/stretchr/testify v1.8.4
 | 
						github.com/stretchr/testify v1.8.4
 | 
				
			||||||
	github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
 | 
						github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
 | 
				
			||||||
	github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
 | 
						github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
 | 
				
			||||||
	go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516
 | 
						go4.org/netipx v0.0.0-20230824141953-6213f710f925
 | 
				
			||||||
	golang.org/x/crypto v0.12.0
 | 
						golang.org/x/crypto v0.14.0
 | 
				
			||||||
	golang.org/x/net v0.14.0
 | 
						golang.org/x/net v0.17.0
 | 
				
			||||||
	golang.org/x/oauth2 v0.7.0
 | 
						golang.org/x/oauth2 v0.12.0
 | 
				
			||||||
	golang.org/x/sync v0.2.0
 | 
						golang.org/x/sync v0.3.0
 | 
				
			||||||
	google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
 | 
						google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
 | 
				
			||||||
	google.golang.org/grpc v1.55.0
 | 
						google.golang.org/grpc v1.55.0
 | 
				
			||||||
	google.golang.org/protobuf v1.30.0
 | 
						google.golang.org/protobuf v1.31.0
 | 
				
			||||||
	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
 | 
						gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1
 | 
						gopkg.in/yaml.v3 v3.0.1
 | 
				
			||||||
	gorm.io/driver/postgres v1.4.8
 | 
						gorm.io/driver/postgres v1.4.8
 | 
				
			||||||
	gorm.io/gorm v1.24.6
 | 
						gorm.io/gorm v1.24.6
 | 
				
			||||||
	tailscale.com v1.50.0
 | 
						tailscale.com v1.54.0
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
@ -56,22 +56,22 @@ require (
 | 
				
			|||||||
	github.com/Microsoft/go-winio v0.6.1 // indirect
 | 
						github.com/Microsoft/go-winio v0.6.1 // indirect
 | 
				
			||||||
	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
 | 
						github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
 | 
				
			||||||
	github.com/akutz/memconn v0.1.0 // indirect
 | 
						github.com/akutz/memconn v0.1.0 // indirect
 | 
				
			||||||
	github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
 | 
						github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
 | 
				
			||||||
	github.com/beorn7/perks v1.0.1 // indirect
 | 
						github.com/beorn7/perks v1.0.1 // indirect
 | 
				
			||||||
	github.com/cenkalti/backoff/v4 v4.2.0 // indirect
 | 
						github.com/cenkalti/backoff/v4 v4.2.0 // indirect
 | 
				
			||||||
	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 | 
						github.com/cespare/xxhash/v2 v2.2.0 // indirect
 | 
				
			||||||
	github.com/containerd/console v1.0.3 // indirect
 | 
						github.com/containerd/console v1.0.3 // indirect
 | 
				
			||||||
	github.com/containerd/continuity v0.3.0 // indirect
 | 
						github.com/containerd/continuity v0.3.0 // indirect
 | 
				
			||||||
	github.com/coreos/go-iptables v0.6.0 // indirect
 | 
						github.com/coreos/go-iptables v0.7.0 // indirect
 | 
				
			||||||
	github.com/dblohm7/wingoes v0.0.0-20230821191801-fc76608aecf0 // indirect
 | 
						github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077 // indirect
 | 
				
			||||||
	github.com/docker/cli v23.0.5+incompatible // indirect
 | 
						github.com/docker/cli v24.0.6+incompatible // indirect
 | 
				
			||||||
	github.com/docker/docker v24.0.4+incompatible // indirect
 | 
						github.com/docker/docker v24.0.7+incompatible // indirect
 | 
				
			||||||
	github.com/docker/go-connections v0.4.0 // indirect
 | 
						github.com/docker/go-connections v0.4.0 // indirect
 | 
				
			||||||
	github.com/docker/go-units v0.5.0 // indirect
 | 
						github.com/docker/go-units v0.5.0 // indirect
 | 
				
			||||||
	github.com/dustin/go-humanize v1.0.1 // indirect
 | 
						github.com/dustin/go-humanize v1.0.1 // indirect
 | 
				
			||||||
	github.com/felixge/fgprof v0.9.3 // indirect
 | 
						github.com/felixge/fgprof v0.9.3 // indirect
 | 
				
			||||||
	github.com/fsnotify/fsnotify v1.6.0 // indirect
 | 
						github.com/fsnotify/fsnotify v1.6.0 // indirect
 | 
				
			||||||
	github.com/fxamacker/cbor/v2 v2.4.0 // indirect
 | 
						github.com/fxamacker/cbor/v2 v2.5.0 // indirect
 | 
				
			||||||
	github.com/glebarez/go-sqlite v1.20.3 // indirect
 | 
						github.com/glebarez/go-sqlite v1.20.3 // indirect
 | 
				
			||||||
	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
 | 
						github.com/go-jose/go-jose/v3 v3.0.0 // indirect
 | 
				
			||||||
	github.com/gogo/protobuf v1.3.2 // indirect
 | 
						github.com/gogo/protobuf v1.3.2 // indirect
 | 
				
			||||||
@ -83,7 +83,7 @@ require (
 | 
				
			|||||||
	github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c // indirect
 | 
						github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c // indirect
 | 
				
			||||||
	github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
 | 
						github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
 | 
				
			||||||
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 | 
						github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 | 
				
			||||||
	github.com/google/uuid v1.3.0 // indirect
 | 
						github.com/google/uuid v1.3.1 // indirect
 | 
				
			||||||
	github.com/gookit/color v1.5.3 // indirect
 | 
						github.com/gookit/color v1.5.3 // indirect
 | 
				
			||||||
	github.com/hashicorp/go-version v1.6.0 // indirect
 | 
						github.com/hashicorp/go-version v1.6.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/hcl v1.0.0 // indirect
 | 
						github.com/hashicorp/hcl v1.0.0 // indirect
 | 
				
			||||||
@ -96,7 +96,7 @@ require (
 | 
				
			|||||||
	github.com/jinzhu/inflection v1.0.0 // indirect
 | 
						github.com/jinzhu/inflection v1.0.0 // indirect
 | 
				
			||||||
	github.com/jinzhu/now v1.1.5 // indirect
 | 
						github.com/jinzhu/now v1.1.5 // indirect
 | 
				
			||||||
	github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
 | 
						github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
 | 
				
			||||||
	github.com/jsimonetti/rtnetlink v1.3.2 // indirect
 | 
						github.com/jsimonetti/rtnetlink v1.3.5 // indirect
 | 
				
			||||||
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 | 
						github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 | 
				
			||||||
	github.com/kr/pretty v0.3.1 // indirect
 | 
						github.com/kr/pretty v0.3.1 // indirect
 | 
				
			||||||
	github.com/kr/text v0.2.0 // indirect
 | 
						github.com/kr/text v0.2.0 // indirect
 | 
				
			||||||
@ -104,33 +104,34 @@ require (
 | 
				
			|||||||
	github.com/lithammer/fuzzysearch v1.1.5 // indirect
 | 
						github.com/lithammer/fuzzysearch v1.1.5 // indirect
 | 
				
			||||||
	github.com/magiconair/properties v1.8.7 // indirect
 | 
						github.com/magiconair/properties v1.8.7 // indirect
 | 
				
			||||||
	github.com/mattn/go-colorable v0.1.13 // indirect
 | 
						github.com/mattn/go-colorable v0.1.13 // indirect
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.18 // indirect
 | 
						github.com/mattn/go-isatty v0.0.19 // indirect
 | 
				
			||||||
	github.com/mattn/go-runewidth v0.0.14 // indirect
 | 
						github.com/mattn/go-runewidth v0.0.14 // indirect
 | 
				
			||||||
	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 | 
						github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 | 
				
			||||||
	github.com/mdlayher/netlink v1.7.2 // indirect
 | 
						github.com/mdlayher/netlink v1.7.2 // indirect
 | 
				
			||||||
	github.com/mdlayher/socket v0.4.1 // indirect
 | 
						github.com/mdlayher/socket v0.5.0 // indirect
 | 
				
			||||||
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 | 
						github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 | 
				
			||||||
	github.com/miekg/dns v1.1.55 // indirect
 | 
						github.com/miekg/dns v1.1.56 // indirect
 | 
				
			||||||
	github.com/mitchellh/go-ps v1.0.0 // indirect
 | 
						github.com/mitchellh/go-ps v1.0.0 // indirect
 | 
				
			||||||
	github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
						github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
				
			||||||
	github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
 | 
						github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
 | 
				
			||||||
	github.com/opencontainers/go-digest v1.0.0 // indirect
 | 
						github.com/opencontainers/go-digest v1.0.0 // indirect
 | 
				
			||||||
	github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
 | 
						github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
 | 
				
			||||||
	github.com/opencontainers/runc v1.1.4 // indirect
 | 
						github.com/opencontainers/runc v1.1.4 // indirect
 | 
				
			||||||
	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
 | 
						github.com/pelletier/go-toml/v2 v2.0.8 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/prometheus/client_model v0.4.0 // indirect
 | 
						github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
 | 
				
			||||||
	github.com/prometheus/procfs v0.9.0 // indirect
 | 
						github.com/prometheus/procfs v0.12.0 // indirect
 | 
				
			||||||
	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
				
			||||||
	github.com/rivo/uniseg v0.4.4 // indirect
 | 
						github.com/rivo/uniseg v0.4.4 // indirect
 | 
				
			||||||
	github.com/rogpeppe/go-internal v1.10.0 // indirect
 | 
						github.com/rogpeppe/go-internal v1.11.0 // indirect
 | 
				
			||||||
	github.com/sirupsen/logrus v1.9.0 // indirect
 | 
						github.com/sirupsen/logrus v1.9.3 // indirect
 | 
				
			||||||
	github.com/spf13/afero v1.9.5 // indirect
 | 
						github.com/spf13/afero v1.9.5 // indirect
 | 
				
			||||||
	github.com/spf13/cast v1.5.1 // indirect
 | 
						github.com/spf13/cast v1.5.1 // indirect
 | 
				
			||||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
						github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
				
			||||||
	github.com/spf13/pflag v1.0.5 // indirect
 | 
						github.com/spf13/pflag v1.0.5 // indirect
 | 
				
			||||||
	github.com/subosito/gotenv v1.4.2 // indirect
 | 
						github.com/subosito/gotenv v1.4.2 // indirect
 | 
				
			||||||
 | 
						github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
 | 
				
			||||||
	github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
 | 
						github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
 | 
				
			||||||
	github.com/vishvananda/netlink v1.2.1-beta.2 // indirect
 | 
						github.com/vishvananda/netlink v1.2.1-beta.2 // indirect
 | 
				
			||||||
	github.com/vishvananda/netns v0.0.4 // indirect
 | 
						github.com/vishvananda/netns v0.0.4 // indirect
 | 
				
			||||||
@ -140,15 +141,15 @@ require (
 | 
				
			|||||||
	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 | 
						github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 | 
				
			||||||
	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
 | 
						github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
 | 
				
			||||||
	go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
 | 
						go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
 | 
						golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
 | 
				
			||||||
	golang.org/x/mod v0.11.0 // indirect
 | 
						golang.org/x/mod v0.12.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.11.0 // indirect
 | 
						golang.org/x/sys v0.13.0 // indirect
 | 
				
			||||||
	golang.org/x/term v0.11.0 // indirect
 | 
						golang.org/x/term v0.13.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.12.0 // indirect
 | 
						golang.org/x/text v0.13.0 // indirect
 | 
				
			||||||
	golang.org/x/time v0.3.0 // indirect
 | 
						golang.org/x/time v0.3.0 // indirect
 | 
				
			||||||
	golang.org/x/tools v0.9.1 // indirect
 | 
						golang.org/x/tools v0.13.0 // indirect
 | 
				
			||||||
	golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
 | 
						golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
 | 
				
			||||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
						google.golang.org/appengine v1.6.8 // indirect
 | 
				
			||||||
	gopkg.in/ini.v1 v1.67.0 // indirect
 | 
						gopkg.in/ini.v1 v1.67.0 // indirect
 | 
				
			||||||
	gopkg.in/square/go-jose.v2 v2.6.0 // indirect
 | 
						gopkg.in/square/go-jose.v2 v2.6.0 // indirect
 | 
				
			||||||
	gopkg.in/yaml.v2 v2.4.0 // indirect
 | 
						gopkg.in/yaml.v2 v2.4.0 // indirect
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										142
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								go.sum
									
									
									
									
									
								
							@ -70,8 +70,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV
 | 
				
			|||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
 | 
					github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
 | 
				
			||||||
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
 | 
					github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
 | 
				
			||||||
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
 | 
					github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
 | 
				
			||||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
 | 
					github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
 | 
				
			||||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
 | 
					github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
 | 
				
			||||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
 | 
					github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
 | 
				
			||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 | 
					github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 | 
				
			||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
					github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
				
			||||||
@ -86,8 +86,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 | 
				
			|||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
					github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
				
			||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
					github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
				
			||||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
 | 
					github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
 | 
				
			||||||
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
 | 
					github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
 | 
				
			||||||
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
 | 
					github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
 | 
				
			||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
					github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
				
			||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
					github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
				
			||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
					github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
				
			||||||
@ -96,8 +96,8 @@ github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARu
 | 
				
			|||||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 | 
					github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 | 
				
			||||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
 | 
					github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
 | 
				
			||||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
 | 
					github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
 | 
				
			||||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
 | 
					github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
 | 
				
			||||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
 | 
					github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
 | 
				
			||||||
github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgEHw=
 | 
					github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgEHw=
 | 
				
			||||||
github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM=
 | 
					github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM=
 | 
				
			||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 | 
					github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 | 
				
			||||||
@ -112,14 +112,14 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG
 | 
				
			|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/dblohm7/wingoes v0.0.0-20230821191801-fc76608aecf0 h1:/dgKwHVTI0J+A0zd/BHOF2CTn1deN0735cJrb+w2hbE=
 | 
					github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077 h1:WphxHslVftszsr0oZOHPaOjpmN/BsgNYF+gW/hxZXXc=
 | 
				
			||||||
github.com/dblohm7/wingoes v0.0.0-20230821191801-fc76608aecf0/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
 | 
					github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
 | 
				
			||||||
github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
 | 
					github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
 | 
				
			||||||
github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 | 
					github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 | 
				
			||||||
github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
 | 
					github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
 | 
				
			||||||
github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
					github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
				
			||||||
github.com/docker/docker v24.0.4+incompatible h1:s/LVDftw9hjblvqIeTiGYXBCD95nOEEl7qRsRrIOuQI=
 | 
					github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
 | 
				
			||||||
github.com/docker/docker v24.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
					github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
				
			||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
					github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
				
			||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
					github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
				
			||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
					github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
				
			||||||
@ -142,8 +142,8 @@ github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx
 | 
				
			|||||||
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 | 
					github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 | 
				
			||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 | 
					github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 | 
				
			||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 | 
					github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 | 
				
			||||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
 | 
					github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
 | 
				
			||||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
 | 
					github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
					github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
					github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
				
			||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 | 
					github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 | 
				
			||||||
@ -258,8 +258,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
 | 
				
			|||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 | 
					github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 | 
				
			||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 | 
					github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 | 
				
			||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
					github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
 | 
				
			||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
					github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
					github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
				
			||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 | 
					github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 | 
				
			||||||
@ -306,8 +306,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
				
			|||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
					github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
				
			||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
 | 
					github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
 | 
				
			||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
 | 
					github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
 | 
				
			||||||
github.com/jsimonetti/rtnetlink v1.3.2 h1:dcn0uWkfxycEEyNy0IGfx3GrhQ38LH7odjxAghimsVI=
 | 
					github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA=
 | 
				
			||||||
github.com/jsimonetti/rtnetlink v1.3.2/go.mod h1:BBu4jZCpTjP6Gk0/wfrO8qcqymnN3g0hoFqObRmUo6U=
 | 
					github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00=
 | 
				
			||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
					github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
				
			||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
					github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
				
			||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 | 
					github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 | 
				
			||||||
@ -318,8 +318,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
 | 
				
			|||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
					github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
				
			||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
					github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
				
			||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 | 
					github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 | 
				
			||||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
 | 
					github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
 | 
				
			||||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 | 
					github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 | 
				
			||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 | 
					github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 | 
				
			||||||
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
 | 
					github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
 | 
				
			||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
 | 
					github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
 | 
				
			||||||
@ -353,8 +353,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
 | 
				
			|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
					github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
					github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
 | 
					github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
					github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
					github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
 | 
					github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
					github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
				
			||||||
@ -362,13 +362,13 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
 | 
				
			|||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 | 
					github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 | 
				
			||||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
 | 
					github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
 | 
				
			||||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
 | 
					github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
 | 
				
			||||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
 | 
					github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
 | 
				
			||||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
 | 
					github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
					github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 | 
					github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
					github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
				
			||||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
 | 
					github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
 | 
				
			||||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
 | 
					github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
 | 
				
			||||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
 | 
					github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
 | 
				
			||||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
 | 
					github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
					github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
				
			||||||
@ -387,8 +387,8 @@ github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282 h1:TQMyrpijt
 | 
				
			|||||||
github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282/go.mod h1:rW25Kyd08Wdn3UVn0YBsDTSvReu0jqpmJKzxITPSjks=
 | 
					github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282/go.mod h1:rW25Kyd08Wdn3UVn0YBsDTSvReu0jqpmJKzxITPSjks=
 | 
				
			||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 | 
					github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 | 
				
			||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 | 
					github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 | 
				
			||||||
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
 | 
					github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
 | 
				
			||||||
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
 | 
					github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
 | 
				
			||||||
github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
 | 
					github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
 | 
				
			||||||
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
 | 
					github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
 | 
				
			||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
					github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
				
			||||||
@ -411,15 +411,15 @@ github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDj
 | 
				
			|||||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 | 
					github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
 | 
					github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
 | 
				
			||||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
 | 
					github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
 | 
				
			||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
					github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
				
			||||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
 | 
					github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
 | 
				
			||||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
 | 
					github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
 | 
				
			||||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
 | 
					github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
 | 
				
			||||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
 | 
					github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
 | 
				
			||||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
 | 
					github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
 | 
				
			||||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
 | 
					github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
 | 
				
			||||||
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
 | 
					github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
 | 
				
			||||||
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
 | 
					github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
 | 
				
			||||||
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
 | 
					github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
 | 
				
			||||||
@ -440,8 +440,8 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
 | 
				
			|||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
					github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 | 
					github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 | 
					github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 | 
					github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 | 
				
			||||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 | 
					github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 | 
				
			||||||
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
 | 
					github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
 | 
				
			||||||
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
 | 
					github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
 | 
				
			||||||
@ -456,8 +456,8 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF
 | 
				
			|||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
					github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
				
			||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
					github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
				
			||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
					github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 | 
					github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
					github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
				
			||||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
 | 
					github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
 | 
				
			||||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
 | 
					github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
 | 
				
			||||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
 | 
					github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
 | 
				
			||||||
@ -490,6 +490,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
 | 
				
			|||||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
 | 
					github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
 | 
				
			||||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
 | 
					github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
 | 
				
			||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 | 
					github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 | 
				
			||||||
 | 
					github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
 | 
				
			||||||
 | 
					github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
 | 
				
			||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
 | 
					github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
 | 
				
			||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
 | 
					github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
 | 
				
			||||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk=
 | 
					github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk=
 | 
				
			||||||
@ -537,8 +539,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
 | 
				
			|||||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
 | 
					go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
 | 
				
			||||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
 | 
					go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
 | 
				
			||||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
 | 
					go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
 | 
				
			||||||
go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 h1:X66ZEoMN2SuaoI/dfZVYobB6E5zjZyyHUMWlCA7MgGE=
 | 
					go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
 | 
				
			||||||
go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
 | 
					go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
@ -550,8 +552,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 | 
				
			|||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
					golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
					golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
				
			||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
 | 
					golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
 | 
				
			||||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
 | 
					golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
 | 
				
			||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 | 
					golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
					golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
				
			||||||
@ -562,8 +564,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 | 
				
			|||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 | 
					golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 | 
					golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 | 
					golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
 | 
					golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
 | 
					golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
					golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
					golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
					golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
				
			||||||
@ -588,8 +590,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			|||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
					golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
				
			||||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
 | 
					golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
 | 
				
			||||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
					golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
@ -626,8 +628,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 | 
				
			|||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 | 
					golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 | 
				
			||||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 | 
					golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 | 
				
			||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 | 
					golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 | 
				
			||||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
 | 
					golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 | 
				
			||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 | 
					golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
@ -638,8 +640,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ
 | 
				
			|||||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
					golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
					golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
 | 
					golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
 | 
				
			||||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
 | 
					golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
 | 
				
			||||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
 | 
					golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
@ -652,8 +654,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 | 
				
			|||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
 | 
					golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
 | 
				
			||||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
@ -720,8 +722,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			|||||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
 | 
					golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
 | 
				
			||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
 | 
					golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
 | 
				
			||||||
@ -729,8 +731,8 @@ golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuX
 | 
				
			|||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
					golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
				
			||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
 | 
					golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
 | 
				
			||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 | 
					golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 | 
				
			||||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
 | 
					golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 | 
				
			||||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
 | 
					golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 | 
				
			||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
@ -739,10 +741,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			|||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
					golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
				
			||||||
 | 
					golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 | 
				
			||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
					golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
				
			||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
					golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
				
			||||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
 | 
					golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 | 
				
			||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 | 
					golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 | 
				
			||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
@ -799,8 +802,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
 | 
				
			|||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
				
			||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 | 
					golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 | 
				
			||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
					golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
				
			||||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
 | 
					golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
 | 
				
			||||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
 | 
					golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
@ -832,8 +835,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 | 
				
			|||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
					google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
				
			||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 | 
					 | 
				
			||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
 | 
					google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
 | 
				
			||||||
 | 
					google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
					google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
@ -905,8 +909,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
 | 
				
			|||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
					google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
				
			||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
					google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
				
			||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
					google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
				
			||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
 | 
					google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
 | 
				
			||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
					google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
@ -955,7 +959,7 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
 | 
				
			|||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
					rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
				
			||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 | 
					rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 | 
				
			||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 | 
					rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 | 
				
			||||||
software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
 | 
					software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8=
 | 
				
			||||||
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
 | 
					software.sslmate.com/src/go-pkcs12 v0.2.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
 | 
				
			||||||
tailscale.com v1.50.0 h1:98infw8rznkdntRgcnlrAC5JuZfDH0bqKLyg8ZKfwMk=
 | 
					tailscale.com v1.54.0 h1:Dri5BTKkHYpl+/t8ofY+tyvoTDbH/FpP7iB4B0cAQOY=
 | 
				
			||||||
tailscale.com v1.50.0/go.mod h1:lBw7+Mw2d7rea3kefGjYWN8IJkB5dyaakMNMOinNGDo=
 | 
					tailscale.com v1.54.0/go.mod h1:MnLFoCRwzFWr3qtkSW2nZdQpK7wQRZEk1KtcEGAuZYw=
 | 
				
			||||||
 | 
				
			|||||||
@ -449,10 +449,10 @@ 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/{nkey}", h.RegisterWebAPI).Methods(http.MethodGet)
 | 
						router.HandleFunc("/register/{mkey}", h.RegisterWebAPI).Methods(http.MethodGet)
 | 
				
			||||||
	h.addLegacyHandlers(router)
 | 
						h.addLegacyHandlers(router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	router.HandleFunc("/oidc/register/{nkey}", 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)
 | 
				
			||||||
	router.HandleFunc("/apple", h.AppleConfigMessage).Methods(http.MethodGet)
 | 
						router.HandleFunc("/apple", h.AppleConfigMessage).Methods(http.MethodGet)
 | 
				
			||||||
	router.HandleFunc("/apple/{platform}", h.ApplePlatformConfig).
 | 
						router.HandleFunc("/apple/{platform}", h.ApplePlatformConfig).
 | 
				
			||||||
 | 
				
			|||||||
@ -45,7 +45,7 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		// is that the client will hammer headscale with requests until it gets a
 | 
							// is that the client will hammer headscale with requests until it gets a
 | 
				
			||||||
		// successful RegisterResponse.
 | 
							// successful RegisterResponse.
 | 
				
			||||||
		if registerRequest.Followup != "" {
 | 
							if registerRequest.Followup != "" {
 | 
				
			||||||
			if _, ok := h.registrationCache.Get(registerRequest.NodeKey.String()); ok {
 | 
								if _, ok := h.registrationCache.Get(machineKey.String()); ok {
 | 
				
			||||||
				log.Debug().
 | 
									log.Debug().
 | 
				
			||||||
					Caller().
 | 
										Caller().
 | 
				
			||||||
					Str("node", registerRequest.Hostinfo.Hostname).
 | 
										Str("node", registerRequest.Hostinfo.Hostname).
 | 
				
			||||||
@ -78,7 +78,7 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
			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(
 | 
				
			||||||
			machineKey.String(),
 | 
								machineKey,
 | 
				
			||||||
			registerRequest.Hostinfo.Hostname,
 | 
								registerRequest.Hostinfo.Hostname,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@ -97,10 +97,10 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		// We create the node and then keep it around until a callback
 | 
							// We create the node and then keep it around until a callback
 | 
				
			||||||
		// happens
 | 
							// happens
 | 
				
			||||||
		newNode := types.Node{
 | 
							newNode := types.Node{
 | 
				
			||||||
			MachineKey: machineKey.String(),
 | 
								MachineKey: machineKey,
 | 
				
			||||||
			Hostname:   registerRequest.Hostinfo.Hostname,
 | 
								Hostname:   registerRequest.Hostinfo.Hostname,
 | 
				
			||||||
			GivenName:  givenName,
 | 
								GivenName:  givenName,
 | 
				
			||||||
			NodeKey:    registerRequest.NodeKey.String(),
 | 
								NodeKey:    registerRequest.NodeKey,
 | 
				
			||||||
			LastSeen:   &now,
 | 
								LastSeen:   &now,
 | 
				
			||||||
			Expiry:     &time.Time{},
 | 
								Expiry:     &time.Time{},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -116,7 +116,7 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		h.registrationCache.Set(
 | 
							h.registrationCache.Set(
 | 
				
			||||||
			newNode.NodeKey,
 | 
								machineKey.String(),
 | 
				
			||||||
			newNode,
 | 
								newNode,
 | 
				
			||||||
			registerCacheExpiration,
 | 
								registerCacheExpiration,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
@ -134,11 +134,7 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		// (juan): For a while we had a bug where we were not storing the MachineKey for the nodes using the TS2021,
 | 
							// (juan): For a while we had a bug where we were not storing the MachineKey for the nodes using the TS2021,
 | 
				
			||||||
		// due to a misunderstanding of the protocol https://github.com/juanfont/headscale/issues/1054
 | 
							// due to a misunderstanding of the protocol https://github.com/juanfont/headscale/issues/1054
 | 
				
			||||||
		// So if we have a not valid MachineKey (but we were able to fetch the node with the NodeKeys), we update it.
 | 
							// So if we have a not valid MachineKey (but we were able to fetch the node with the NodeKeys), we update it.
 | 
				
			||||||
		var storedMachineKey key.MachinePublic
 | 
							if err != nil || node.MachineKey.IsZero() {
 | 
				
			||||||
		err = storedMachineKey.UnmarshalText(
 | 
					 | 
				
			||||||
			[]byte(node.MachineKey),
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
		if err != nil || storedMachineKey.IsZero() {
 | 
					 | 
				
			||||||
			if err := h.db.NodeSetMachineKey(node, machineKey); err != nil {
 | 
								if err := h.db.NodeSetMachineKey(node, machineKey); err != nil {
 | 
				
			||||||
				log.Error().
 | 
									log.Error().
 | 
				
			||||||
					Caller().
 | 
										Caller().
 | 
				
			||||||
@ -156,7 +152,7 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		// - Trying to log out (sending a expiry in the past)
 | 
							// - Trying to log out (sending a expiry in the past)
 | 
				
			||||||
		// - A valid, registered node, looking for /map
 | 
							// - A valid, registered node, looking for /map
 | 
				
			||||||
		// - Expired node wanting to reauthenticate
 | 
							// - Expired node wanting to reauthenticate
 | 
				
			||||||
		if node.NodeKey == registerRequest.NodeKey.String() {
 | 
							if node.NodeKey.String() == registerRequest.NodeKey.String() {
 | 
				
			||||||
			// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
 | 
								// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
 | 
				
			||||||
			//   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() &&
 | 
				
			||||||
@ -176,7 +172,7 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
 | 
							// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
 | 
				
			||||||
		if node.NodeKey == registerRequest.OldNodeKey.String() &&
 | 
							if node.NodeKey.String() == registerRequest.OldNodeKey.String() &&
 | 
				
			||||||
			!node.IsExpired() {
 | 
								!node.IsExpired() {
 | 
				
			||||||
			h.handleNodeKeyRefresh(
 | 
								h.handleNodeKeyRefresh(
 | 
				
			||||||
				writer,
 | 
									writer,
 | 
				
			||||||
@ -207,9 +203,9 @@ func (h *Headscale) handleRegister(
 | 
				
			|||||||
		// we need to make sure the NodeKey matches the one in the request
 | 
							// we need to make sure the NodeKey matches the one in the request
 | 
				
			||||||
		// TODO(juan): What happens when using fast user switching between two
 | 
							// TODO(juan): What happens when using fast user switching between two
 | 
				
			||||||
		// headscale-managed tailnets?
 | 
							// headscale-managed tailnets?
 | 
				
			||||||
		node.NodeKey = registerRequest.NodeKey.String()
 | 
							node.NodeKey = registerRequest.NodeKey
 | 
				
			||||||
		h.registrationCache.Set(
 | 
							h.registrationCache.Set(
 | 
				
			||||||
			registerRequest.NodeKey.String(),
 | 
								machineKey.String(),
 | 
				
			||||||
			*node,
 | 
								*node,
 | 
				
			||||||
			registerCacheExpiration,
 | 
								registerCacheExpiration,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
@ -294,7 +290,7 @@ func (h *Headscale) handleAuthKey(
 | 
				
			|||||||
		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")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodeKey := registerRequest.NodeKey.String()
 | 
						nodeKey := registerRequest.NodeKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// retrieve node information if it exist
 | 
						// retrieve node information if it exist
 | 
				
			||||||
	// The error is not important, because if it does not
 | 
						// The error is not important, because if it does not
 | 
				
			||||||
@ -342,7 +338,7 @@ func (h *Headscale) handleAuthKey(
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		now := time.Now().UTC()
 | 
							now := time.Now().UTC()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		givenName, err := h.db.GenerateGivenName(machineKey.String(), registerRequest.Hostinfo.Hostname)
 | 
							givenName, err := h.db.GenerateGivenName(machineKey, registerRequest.Hostinfo.Hostname)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error().
 | 
								log.Error().
 | 
				
			||||||
				Caller().
 | 
									Caller().
 | 
				
			||||||
@ -359,7 +355,7 @@ func (h *Headscale) handleAuthKey(
 | 
				
			|||||||
			Hostname:       registerRequest.Hostinfo.Hostname,
 | 
								Hostname:       registerRequest.Hostinfo.Hostname,
 | 
				
			||||||
			GivenName:      givenName,
 | 
								GivenName:      givenName,
 | 
				
			||||||
			UserID:         pak.User.ID,
 | 
								UserID:         pak.User.ID,
 | 
				
			||||||
			MachineKey:     machineKey.String(),
 | 
								MachineKey:     machineKey,
 | 
				
			||||||
			RegisterMethod: util.RegisterMethodAuthKey,
 | 
								RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
			Expiry:         ®isterRequest.Expiry,
 | 
								Expiry:         ®isterRequest.Expiry,
 | 
				
			||||||
			NodeKey:        nodeKey,
 | 
								NodeKey:        nodeKey,
 | 
				
			||||||
@ -460,12 +456,12 @@ func (h *Headscale) handleNewNode(
 | 
				
			|||||||
		resp.AuthURL = fmt.Sprintf(
 | 
							resp.AuthURL = fmt.Sprintf(
 | 
				
			||||||
			"%s/oidc/register/%s",
 | 
								"%s/oidc/register/%s",
 | 
				
			||||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
								strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
				
			||||||
			registerRequest.NodeKey,
 | 
								machineKey.String(),
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		resp.AuthURL = fmt.Sprintf("%s/register/%s",
 | 
							resp.AuthURL = fmt.Sprintf("%s/register/%s",
 | 
				
			||||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
								strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
				
			||||||
			registerRequest.NodeKey)
 | 
								machineKey.String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
 | 
						respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
 | 
				
			||||||
@ -715,11 +711,11 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
 | 
				
			|||||||
	if h.oauth2Config != nil {
 | 
						if h.oauth2Config != nil {
 | 
				
			||||||
		resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
 | 
							resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
 | 
				
			||||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
								strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
				
			||||||
			registerRequest.NodeKey)
 | 
								machineKey.String())
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		resp.AuthURL = fmt.Sprintf("%s/register/%s",
 | 
							resp.AuthURL = fmt.Sprintf("%s/register/%s",
 | 
				
			||||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
								strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
				
			||||||
			registerRequest.NodeKey)
 | 
								machineKey.String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
 | 
						respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ package db
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/netip"
 | 
						"net/netip"
 | 
				
			||||||
@ -99,7 +100,7 @@ func NewHeadscaleDatabase(
 | 
				
			|||||||
	// node was registered.
 | 
						// node was registered.
 | 
				
			||||||
	_ = dbConn.Migrator().RenameColumn(&types.Node{}, "nickname", "given_name")
 | 
						_ = dbConn.Migrator().RenameColumn(&types.Node{}, "nickname", "given_name")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If the MacNodehine table has a column for registered,
 | 
						// If the Node table has a column for registered,
 | 
				
			||||||
	// find all occourences of "false" and drop them. Then
 | 
						// find all occourences of "false" and drop them. Then
 | 
				
			||||||
	// remove the column.
 | 
						// remove the column.
 | 
				
			||||||
	if dbConn.Migrator().HasColumn(&types.Node{}, "registered") {
 | 
						if dbConn.Migrator().HasColumn(&types.Node{}, "registered") {
 | 
				
			||||||
@ -114,13 +115,13 @@ func NewHeadscaleDatabase(
 | 
				
			|||||||
		for _, node := range nodes {
 | 
							for _, node := range nodes {
 | 
				
			||||||
			log.Info().
 | 
								log.Info().
 | 
				
			||||||
				Str("node", node.Hostname).
 | 
									Str("node", node.Hostname).
 | 
				
			||||||
				Str("machine_key", node.MachineKey).
 | 
									Str("machine_key", node.MachineKey.ShortString()).
 | 
				
			||||||
				Msg("Deleting unregistered node")
 | 
									Msg("Deleting unregistered node")
 | 
				
			||||||
			if err := dbConn.Delete(&types.Node{}, node.ID).Error; err != nil {
 | 
								if err := dbConn.Delete(&types.Node{}, node.ID).Error; err != nil {
 | 
				
			||||||
				log.Error().
 | 
									log.Error().
 | 
				
			||||||
					Err(err).
 | 
										Err(err).
 | 
				
			||||||
					Str("node", node.Hostname).
 | 
										Str("node", node.Hostname).
 | 
				
			||||||
					Str("machine_key", node.MachineKey).
 | 
										Str("machine_key", node.MachineKey.ShortString()).
 | 
				
			||||||
					Msg("Error deleting unregistered node")
 | 
										Msg("Error deleting unregistered node")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -136,6 +137,50 @@ func NewHeadscaleDatabase(
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = dbConn.AutoMigrate(&types.Node{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ensure all keys have correct prefixes
 | 
				
			||||||
 | 
						// https://github.com/tailscale/tailscale/blob/main/types/key/node.go#L35
 | 
				
			||||||
 | 
						type result struct {
 | 
				
			||||||
 | 
							ID         uint64
 | 
				
			||||||
 | 
							MachineKey string
 | 
				
			||||||
 | 
							NodeKey    string
 | 
				
			||||||
 | 
							DiscoKey   string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var results []result
 | 
				
			||||||
 | 
						err = db.db.Raw("SELECT id, node_key, machine_key, disco_key FROM nodes").Find(&results).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, node := range results {
 | 
				
			||||||
 | 
							mKey := node.MachineKey
 | 
				
			||||||
 | 
							if !strings.HasPrefix(node.MachineKey, "mkey:") {
 | 
				
			||||||
 | 
								mKey = "mkey:" + node.MachineKey
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							nKey := node.NodeKey
 | 
				
			||||||
 | 
							if !strings.HasPrefix(node.NodeKey, "nodekey:") {
 | 
				
			||||||
 | 
								nKey = "nodekey:" + node.NodeKey
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dKey := node.DiscoKey
 | 
				
			||||||
 | 
							if !strings.HasPrefix(node.DiscoKey, "discokey:") {
 | 
				
			||||||
 | 
								dKey = "discokey:" + node.DiscoKey
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := db.db.Exec("UPDATE nodes SET machine_key = @mKey, node_key = @nKey, disco_key = @dKey WHERE ID = @id",
 | 
				
			||||||
 | 
								sql.Named("mKey", mKey),
 | 
				
			||||||
 | 
								sql.Named("nKey", nKey),
 | 
				
			||||||
 | 
								sql.Named("dKey", dKey),
 | 
				
			||||||
 | 
								sql.Named("id", node.ID)).Error
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if dbConn.Migrator().HasColumn(&types.Node{}, "enabled_routes") {
 | 
						if dbConn.Migrator().HasColumn(&types.Node{}, "enabled_routes") {
 | 
				
			||||||
		log.Info().Msgf("Database has legacy enabled_routes column in node, migrating...")
 | 
							log.Info().Msgf("Database has legacy enabled_routes column in node, migrating...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -195,11 +240,6 @@ func NewHeadscaleDatabase(
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = dbConn.AutoMigrate(&types.Node{})
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if dbConn.Migrator().HasColumn(&types.Node{}, "given_name") {
 | 
						if dbConn.Migrator().HasColumn(&types.Node{}, "given_name") {
 | 
				
			||||||
		nodes := types.Nodes{}
 | 
							nodes := types.Nodes{}
 | 
				
			||||||
		if err := dbConn.Find(&nodes).Error; err != nil {
 | 
							if err := dbConn.Find(&nodes).Error; err != nil {
 | 
				
			||||||
@ -253,27 +293,6 @@ func NewHeadscaleDatabase(
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Ensure all keys have correct prefixes
 | 
					 | 
				
			||||||
	// https://github.com/tailscale/tailscale/blob/main/types/key/node.go#L35
 | 
					 | 
				
			||||||
	nodes := types.Nodes{}
 | 
					 | 
				
			||||||
	if err := dbConn.Find(&nodes).Error; err != nil {
 | 
					 | 
				
			||||||
		log.Error().Err(err).Msg("Error accessing db")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, node := range nodes {
 | 
					 | 
				
			||||||
		if !strings.HasPrefix(node.DiscoKey, "discokey:") {
 | 
					 | 
				
			||||||
			node.DiscoKey = "discokey:" + node.DiscoKey
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !strings.HasPrefix(node.NodeKey, "nodekey:") {
 | 
					 | 
				
			||||||
			node.NodeKey = "nodekey:" + node.NodeKey
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !strings.HasPrefix(node.MachineKey, "mkey:") {
 | 
					 | 
				
			||||||
			node.MachineKey = "mkey:" + node.MachineKey
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO(kradalby): is this needed?
 | 
						// TODO(kradalby): is this needed?
 | 
				
			||||||
	err = db.setValue("db_version", dbVersion)
 | 
						err = db.setValue("db_version", dbVersion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -55,7 +55,7 @@ func (hsdb *HSDatabase) listPeers(node *types.Node) (types.Nodes, error) {
 | 
				
			|||||||
		Preload("User").
 | 
							Preload("User").
 | 
				
			||||||
		Preload("Routes").
 | 
							Preload("Routes").
 | 
				
			||||||
		Where("node_key <> ?",
 | 
							Where("node_key <> ?",
 | 
				
			||||||
			node.NodeKey).Find(&nodes).Error; err != nil {
 | 
								node.NodeKey.String()).Find(&nodes).Error; err != nil {
 | 
				
			||||||
		return types.Nodes{}, err
 | 
							return types.Nodes{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -268,7 +268,7 @@ func (hsdb *HSDatabase) SetTags(
 | 
				
			|||||||
	hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
						hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
				
			||||||
		Type:    types.StatePeerChanged,
 | 
							Type:    types.StatePeerChanged,
 | 
				
			||||||
		Changed: types.Nodes{node},
 | 
							Changed: types.Nodes{node},
 | 
				
			||||||
	}, node.MachineKey)
 | 
						}, node.MachineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -304,7 +304,7 @@ func (hsdb *HSDatabase) RenameNode(node *types.Node, newName string) error {
 | 
				
			|||||||
	hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
						hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
				
			||||||
		Type:    types.StatePeerChanged,
 | 
							Type:    types.StatePeerChanged,
 | 
				
			||||||
		Changed: types.Nodes{node},
 | 
							Changed: types.Nodes{node},
 | 
				
			||||||
	}, node.MachineKey)
 | 
						}, node.MachineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -330,7 +330,7 @@ func (hsdb *HSDatabase) nodeSetExpiry(node *types.Node, expiry time.Time) error
 | 
				
			|||||||
	hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
						hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
				
			||||||
		Type:    types.StatePeerChanged,
 | 
							Type:    types.StatePeerChanged,
 | 
				
			||||||
		Changed: types.Nodes{node},
 | 
							Changed: types.Nodes{node},
 | 
				
			||||||
	}, node.MachineKey)
 | 
						}, node.MachineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -376,7 +376,7 @@ func (hsdb *HSDatabase) UpdateLastSeen(node *types.Node) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (hsdb *HSDatabase) RegisterNodeFromAuthCallback(
 | 
					func (hsdb *HSDatabase) RegisterNodeFromAuthCallback(
 | 
				
			||||||
	cache *cache.Cache,
 | 
						cache *cache.Cache,
 | 
				
			||||||
	nodeKeyStr string,
 | 
						mkey key.MachinePublic,
 | 
				
			||||||
	userName string,
 | 
						userName string,
 | 
				
			||||||
	nodeExpiry *time.Time,
 | 
						nodeExpiry *time.Time,
 | 
				
			||||||
	registrationMethod string,
 | 
						registrationMethod string,
 | 
				
			||||||
@ -384,20 +384,14 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback(
 | 
				
			|||||||
	hsdb.mu.Lock()
 | 
						hsdb.mu.Lock()
 | 
				
			||||||
	defer hsdb.mu.Unlock()
 | 
						defer hsdb.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodeKey := key.NodePublic{}
 | 
					 | 
				
			||||||
	err := nodeKey.UnmarshalText([]byte(nodeKeyStr))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.Debug().
 | 
						log.Debug().
 | 
				
			||||||
		Str("nodeKey", nodeKey.ShortString()).
 | 
							Str("machine_key", mkey.ShortString()).
 | 
				
			||||||
		Str("userName", userName).
 | 
							Str("userName", userName).
 | 
				
			||||||
		Str("registrationMethod", registrationMethod).
 | 
							Str("registrationMethod", registrationMethod).
 | 
				
			||||||
		Str("expiresAt", fmt.Sprintf("%v", nodeExpiry)).
 | 
							Str("expiresAt", fmt.Sprintf("%v", nodeExpiry)).
 | 
				
			||||||
		Msg("Registering node from API/CLI or auth callback")
 | 
							Msg("Registering node from API/CLI or auth callback")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if nodeInterface, ok := cache.Get(nodeKey.String()); ok {
 | 
						if nodeInterface, ok := cache.Get(mkey.String()); ok {
 | 
				
			||||||
		if registrationNode, ok := nodeInterface.(types.Node); ok {
 | 
							if registrationNode, ok := nodeInterface.(types.Node); ok {
 | 
				
			||||||
			user, err := hsdb.getUser(userName)
 | 
								user, err := hsdb.getUser(userName)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
@ -425,7 +419,7 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback(
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				cache.Delete(nodeKeyStr)
 | 
									cache.Delete(mkey.String())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return node, err
 | 
								return node, err
 | 
				
			||||||
@ -448,8 +442,8 @@ func (hsdb *HSDatabase) RegisterNode(node types.Node) (*types.Node, error) {
 | 
				
			|||||||
func (hsdb *HSDatabase) registerNode(node types.Node) (*types.Node, error) {
 | 
					func (hsdb *HSDatabase) registerNode(node types.Node) (*types.Node, error) {
 | 
				
			||||||
	log.Debug().
 | 
						log.Debug().
 | 
				
			||||||
		Str("node", node.Hostname).
 | 
							Str("node", node.Hostname).
 | 
				
			||||||
		Str("machine_key", node.MachineKey).
 | 
							Str("machine_key", node.MachineKey.ShortString()).
 | 
				
			||||||
		Str("node_key", node.NodeKey).
 | 
							Str("node_key", node.NodeKey.ShortString()).
 | 
				
			||||||
		Str("user", node.User.Name).
 | 
							Str("user", node.User.Name).
 | 
				
			||||||
		Msg("Registering node")
 | 
							Msg("Registering node")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -464,8 +458,8 @@ func (hsdb *HSDatabase) registerNode(node types.Node) (*types.Node, error) {
 | 
				
			|||||||
		log.Trace().
 | 
							log.Trace().
 | 
				
			||||||
			Caller().
 | 
								Caller().
 | 
				
			||||||
			Str("node", node.Hostname).
 | 
								Str("node", node.Hostname).
 | 
				
			||||||
			Str("machine_key", node.MachineKey).
 | 
								Str("machine_key", node.MachineKey.ShortString()).
 | 
				
			||||||
			Str("node_key", node.NodeKey).
 | 
								Str("node_key", node.NodeKey.ShortString()).
 | 
				
			||||||
			Str("user", node.User.Name).
 | 
								Str("user", node.User.Name).
 | 
				
			||||||
			Msg("Node authorized again")
 | 
								Msg("Node authorized again")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -507,7 +501,7 @@ func (hsdb *HSDatabase) NodeSetNodeKey(node *types.Node, nodeKey key.NodePublic)
 | 
				
			|||||||
	defer hsdb.mu.Unlock()
 | 
						defer hsdb.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := hsdb.db.Model(node).Updates(types.Node{
 | 
						if err := hsdb.db.Model(node).Updates(types.Node{
 | 
				
			||||||
		NodeKey: nodeKey.String(),
 | 
							NodeKey: nodeKey,
 | 
				
			||||||
	}).Error; err != nil {
 | 
						}).Error; err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -524,7 +518,7 @@ func (hsdb *HSDatabase) NodeSetMachineKey(
 | 
				
			|||||||
	defer hsdb.mu.Unlock()
 | 
						defer hsdb.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := hsdb.db.Model(node).Updates(types.Node{
 | 
						if err := hsdb.db.Model(node).Updates(types.Node{
 | 
				
			||||||
		MachineKey: machineKey.String(),
 | 
							MachineKey: machineKey,
 | 
				
			||||||
	}).Error; err != nil {
 | 
						}).Error; err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -703,7 +697,7 @@ func (hsdb *HSDatabase) enableRoutes(node *types.Node, routeStrs ...string) erro
 | 
				
			|||||||
	hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
						hsdb.notifier.NotifyWithIgnore(types.StateUpdate{
 | 
				
			||||||
		Type:    types.StatePeerChanged,
 | 
							Type:    types.StatePeerChanged,
 | 
				
			||||||
		Changed: types.Nodes{node},
 | 
							Changed: types.Nodes{node},
 | 
				
			||||||
	}, node.MachineKey)
 | 
						}, node.MachineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -734,7 +728,7 @@ func generateGivenName(suppliedName string, randomSuffix bool) (string, error) {
 | 
				
			|||||||
	return normalizedHostname, nil
 | 
						return normalizedHostname, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (hsdb *HSDatabase) GenerateGivenName(machineKey string, suppliedName string) (string, error) {
 | 
					func (hsdb *HSDatabase) GenerateGivenName(mkey key.MachinePublic, suppliedName string) (string, error) {
 | 
				
			||||||
	hsdb.mu.RLock()
 | 
						hsdb.mu.RLock()
 | 
				
			||||||
	defer hsdb.mu.RUnlock()
 | 
						defer hsdb.mu.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -749,8 +743,14 @@ func (hsdb *HSDatabase) GenerateGivenName(machineKey string, suppliedName string
 | 
				
			|||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, node := range nodes {
 | 
						var nodeFound *types.Node
 | 
				
			||||||
		if node.MachineKey != machineKey && node.GivenName == givenName {
 | 
						for idx, node := range nodes {
 | 
				
			||||||
 | 
							if node.GivenName == givenName {
 | 
				
			||||||
 | 
								nodeFound = nodes[idx]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if nodeFound != nil && nodeFound.MachineKey.String() != mkey.String() {
 | 
				
			||||||
		postfixedName, err := generateGivenName(suppliedName, true)
 | 
							postfixedName, err := generateGivenName(suppliedName, true)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
@ -758,7 +758,6 @@ func (hsdb *HSDatabase) GenerateGivenName(machineKey string, suppliedName string
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		givenName = postfixedName
 | 
							givenName = postfixedName
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return givenName, nil
 | 
						return givenName, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -25,11 +25,13 @@ func (s *Suite) TestGetNode(c *check.C) {
 | 
				
			|||||||
	_, err = db.GetNode("test", "testnode")
 | 
						_, err = db.GetNode("test", "testnode")
 | 
				
			||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "foo",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode",
 | 
							Hostname:       "testnode",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -51,11 +53,13 @@ func (s *Suite) TestGetNodeByID(c *check.C) {
 | 
				
			|||||||
	_, err = db.GetNodeByID(0)
 | 
						_, err = db.GetNodeByID(0)
 | 
				
			||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node := types.Node{
 | 
						node := types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "foo",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode",
 | 
							Hostname:       "testnode",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -82,9 +86,8 @@ func (s *Suite) TestGetNodeByNodeKey(c *check.C) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := types.Node{
 | 
						node := types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     machineKey.Public().String(),
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        nodeKey.Public().String(),
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode",
 | 
							Hostname:       "testnode",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -113,9 +116,8 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := types.Node{
 | 
						node := types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     machineKey.Public().String(),
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        nodeKey.Public().String(),
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode",
 | 
							Hostname:       "testnode",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -130,11 +132,14 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) {
 | 
				
			|||||||
func (s *Suite) TestHardDeleteNode(c *check.C) {
 | 
					func (s *Suite) TestHardDeleteNode(c *check.C) {
 | 
				
			||||||
	user, err := db.CreateUser("test")
 | 
						user, err := db.CreateUser("test")
 | 
				
			||||||
	c.Assert(err, check.IsNil)
 | 
						c.Assert(err, check.IsNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node := types.Node{
 | 
						node := types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "foo",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode3",
 | 
							Hostname:       "testnode3",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -160,11 +165,13 @@ func (s *Suite) TestListPeers(c *check.C) {
 | 
				
			|||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for index := 0; index <= 10; index++ {
 | 
						for index := 0; index <= 10; index++ {
 | 
				
			||||||
 | 
							nodeKey := key.NewNode()
 | 
				
			||||||
 | 
							machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		node := types.Node{
 | 
							node := types.Node{
 | 
				
			||||||
			ID:             uint64(index),
 | 
								ID:             uint64(index),
 | 
				
			||||||
			MachineKey:     "foo" + strconv.Itoa(index),
 | 
								MachineKey:     machineKey.Public(),
 | 
				
			||||||
			NodeKey:        "bar" + strconv.Itoa(index),
 | 
								NodeKey:        nodeKey.Public(),
 | 
				
			||||||
			DiscoKey:       "faa" + strconv.Itoa(index),
 | 
					 | 
				
			||||||
			Hostname:       "testnode" + strconv.Itoa(index),
 | 
								Hostname:       "testnode" + strconv.Itoa(index),
 | 
				
			||||||
			UserID:         user.ID,
 | 
								UserID:         user.ID,
 | 
				
			||||||
			RegisterMethod: util.RegisterMethodAuthKey,
 | 
								RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -205,11 +212,13 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
 | 
				
			|||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for index := 0; index <= 10; index++ {
 | 
						for index := 0; index <= 10; index++ {
 | 
				
			||||||
 | 
							nodeKey := key.NewNode()
 | 
				
			||||||
 | 
							machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		node := types.Node{
 | 
							node := types.Node{
 | 
				
			||||||
			ID:         uint64(index),
 | 
								ID:         uint64(index),
 | 
				
			||||||
			MachineKey: "foo" + strconv.Itoa(index),
 | 
								MachineKey: machineKey.Public(),
 | 
				
			||||||
			NodeKey:    "bar" + strconv.Itoa(index),
 | 
								NodeKey:    nodeKey.Public(),
 | 
				
			||||||
			DiscoKey:   "faa" + strconv.Itoa(index),
 | 
					 | 
				
			||||||
			IPAddresses: types.NodeAddresses{
 | 
								IPAddresses: types.NodeAddresses{
 | 
				
			||||||
				netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
 | 
									netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
@ -288,11 +297,13 @@ func (s *Suite) TestExpireNode(c *check.C) {
 | 
				
			|||||||
	_, err = db.GetNode("test", "testnode")
 | 
						_, err = db.GetNode("test", "testnode")
 | 
				
			||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "foo",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode",
 | 
							Hostname:       "testnode",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -345,11 +356,15 @@ func (s *Suite) TestGenerateGivenName(c *check.C) {
 | 
				
			|||||||
	_, err = db.GetNode("user-1", "testnode")
 | 
						_, err = db.GetNode("user-1", "testnode")
 | 
				
			||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						machineKey2 := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "node-key-1",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        "node-key-1",
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "disco-key-1",
 | 
					 | 
				
			||||||
		Hostname:       "hostname-1",
 | 
							Hostname:       "hostname-1",
 | 
				
			||||||
		GivenName:      "hostname-1",
 | 
							GivenName:      "hostname-1",
 | 
				
			||||||
		UserID:         user1.ID,
 | 
							UserID:         user1.ID,
 | 
				
			||||||
@ -358,25 +373,20 @@ func (s *Suite) TestGenerateGivenName(c *check.C) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	db.db.Save(node)
 | 
						db.db.Save(node)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	givenName, err := db.GenerateGivenName("node-key-2", "hostname-2")
 | 
						givenName, err := db.GenerateGivenName(machineKey2.Public(), "hostname-2")
 | 
				
			||||||
	comment := check.Commentf("Same user, unique nodes, unique hostnames, no conflict")
 | 
						comment := check.Commentf("Same user, unique nodes, unique hostnames, no conflict")
 | 
				
			||||||
	c.Assert(err, check.IsNil, comment)
 | 
						c.Assert(err, check.IsNil, comment)
 | 
				
			||||||
	c.Assert(givenName, check.Equals, "hostname-2", comment)
 | 
						c.Assert(givenName, check.Equals, "hostname-2", comment)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	givenName, err = db.GenerateGivenName("node-key-1", "hostname-1")
 | 
						givenName, err = db.GenerateGivenName(machineKey.Public(), "hostname-1")
 | 
				
			||||||
	comment = check.Commentf("Same user, same node, same hostname, no conflict")
 | 
						comment = check.Commentf("Same user, same node, same hostname, no conflict")
 | 
				
			||||||
	c.Assert(err, check.IsNil, comment)
 | 
						c.Assert(err, check.IsNil, comment)
 | 
				
			||||||
	c.Assert(givenName, check.Equals, "hostname-1", comment)
 | 
						c.Assert(givenName, check.Equals, "hostname-1", comment)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	givenName, err = db.GenerateGivenName("node-key-2", "hostname-1")
 | 
						givenName, err = db.GenerateGivenName(machineKey2.Public(), "hostname-1")
 | 
				
			||||||
	comment = check.Commentf("Same user, unique nodes, same hostname, conflict")
 | 
						comment = check.Commentf("Same user, unique nodes, same hostname, conflict")
 | 
				
			||||||
	c.Assert(err, check.IsNil, comment)
 | 
						c.Assert(err, check.IsNil, comment)
 | 
				
			||||||
	c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", NodeGivenNameHashLength), comment)
 | 
						c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", NodeGivenNameHashLength), comment)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	givenName, err = db.GenerateGivenName("node-key-2", "hostname-1")
 | 
					 | 
				
			||||||
	comment = check.Commentf("Unique users, unique nodes, same hostname, conflict")
 | 
					 | 
				
			||||||
	c.Assert(err, check.IsNil, comment)
 | 
					 | 
				
			||||||
	c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", NodeGivenNameHashLength), comment)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *Suite) TestSetTags(c *check.C) {
 | 
					func (s *Suite) TestSetTags(c *check.C) {
 | 
				
			||||||
@ -389,11 +399,13 @@ func (s *Suite) TestSetTags(c *check.C) {
 | 
				
			|||||||
	_, err = db.GetNode("test", "testnode")
 | 
						_, err = db.GetNode("test", "testnode")
 | 
				
			||||||
	c.Assert(err, check.NotNil)
 | 
						c.Assert(err, check.NotNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "foo",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "testnode",
 | 
							Hostname:       "testnode",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
@ -565,6 +577,7 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) {
 | 
				
			|||||||
	c.Assert(err, check.IsNil)
 | 
						c.Assert(err, check.IsNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodeKey := key.NewNode()
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
						machineKey := key.NewMachine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultRouteV4 := netip.MustParsePrefix("0.0.0.0/0")
 | 
						defaultRouteV4 := netip.MustParsePrefix("0.0.0.0/0")
 | 
				
			||||||
	defaultRouteV6 := netip.MustParsePrefix("::/0")
 | 
						defaultRouteV6 := netip.MustParsePrefix("::/0")
 | 
				
			||||||
@ -574,9 +587,8 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := types.Node{
 | 
						node := types.Node{
 | 
				
			||||||
		ID:             0,
 | 
							ID:             0,
 | 
				
			||||||
		MachineKey:     "foo",
 | 
							MachineKey:     machineKey.Public(),
 | 
				
			||||||
		NodeKey:        nodeKey.Public().String(),
 | 
							NodeKey:        nodeKey.Public(),
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
					 | 
				
			||||||
		Hostname:       "test",
 | 
							Hostname:       "test",
 | 
				
			||||||
		UserID:         user.ID,
 | 
							UserID:         user.ID,
 | 
				
			||||||
		RegisterMethod: util.RegisterMethodAuthKey,
 | 
							RegisterMethod: util.RegisterMethodAuthKey,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package db
 | 
					package db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
	"net/netip"
 | 
						"net/netip"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
@ -27,19 +28,22 @@ func (s *Suite) SetUpTest(c *check.C) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *Suite) TearDownTest(c *check.C) {
 | 
					func (s *Suite) TearDownTest(c *check.C) {
 | 
				
			||||||
	os.RemoveAll(tmpDir)
 | 
						// os.RemoveAll(tmpDir)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *Suite) ResetDB(c *check.C) {
 | 
					func (s *Suite) ResetDB(c *check.C) {
 | 
				
			||||||
	if len(tmpDir) != 0 {
 | 
						// if len(tmpDir) != 0 {
 | 
				
			||||||
		os.RemoveAll(tmpDir)
 | 
						// 	os.RemoveAll(tmpDir)
 | 
				
			||||||
	}
 | 
						// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	tmpDir, err = os.MkdirTemp("", "autoygg-client-test")
 | 
						tmpDir, err = os.MkdirTemp("", "headscale-db-test-*")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		c.Fatal(err)
 | 
							c.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Printf("database path: %s", tmpDir+"/headscale_test.db")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db, err = NewHeadscaleDatabase(
 | 
						db, err = NewHeadscaleDatabase(
 | 
				
			||||||
		"sqlite3",
 | 
							"sqlite3",
 | 
				
			||||||
		tmpDir+"/headscale_test.db",
 | 
							tmpDir+"/headscale_test.db",
 | 
				
			||||||
 | 
				
			|||||||
@ -172,12 +172,18 @@ func (api headscaleV1APIServer) RegisterNode(
 | 
				
			|||||||
) (*v1.RegisterNodeResponse, error) {
 | 
					) (*v1.RegisterNodeResponse, error) {
 | 
				
			||||||
	log.Trace().
 | 
						log.Trace().
 | 
				
			||||||
		Str("user", request.GetUser()).
 | 
							Str("user", request.GetUser()).
 | 
				
			||||||
		Str("node_key", request.GetKey()).
 | 
							Str("machine_key", request.GetKey()).
 | 
				
			||||||
		Msg("Registering node")
 | 
							Msg("Registering node")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mkey key.MachinePublic
 | 
				
			||||||
 | 
						err := mkey.UnmarshalText([]byte(request.GetKey()))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	node, err := api.h.db.RegisterNodeFromAuthCallback(
 | 
						node, err := api.h.db.RegisterNodeFromAuthCallback(
 | 
				
			||||||
		api.h.registrationCache,
 | 
							api.h.registrationCache,
 | 
				
			||||||
		request.GetKey(),
 | 
							mkey,
 | 
				
			||||||
		request.GetUser(),
 | 
							request.GetUser(),
 | 
				
			||||||
		nil,
 | 
							nil,
 | 
				
			||||||
		util.RegisterMethodCLI,
 | 
							util.RegisterMethodCLI,
 | 
				
			||||||
@ -521,13 +527,22 @@ func (api headscaleV1APIServer) DebugCreateNode(
 | 
				
			|||||||
		Hostname:    "DebugTestNode",
 | 
							Hostname:    "DebugTestNode",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	givenName, err := api.h.db.GenerateGivenName(request.GetKey(), request.GetName())
 | 
						var mkey key.MachinePublic
 | 
				
			||||||
 | 
						err = mkey.UnmarshalText([]byte(request.GetKey()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						givenName, err := api.h.db.GenerateGivenName(mkey, request.GetName())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodeKey := key.NewNode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newNode := types.Node{
 | 
						newNode := types.Node{
 | 
				
			||||||
		MachineKey: request.GetKey(),
 | 
							MachineKey: mkey,
 | 
				
			||||||
 | 
							NodeKey:    nodeKey.Public(),
 | 
				
			||||||
		Hostname:   request.GetName(),
 | 
							Hostname:   request.GetName(),
 | 
				
			||||||
		GivenName:  givenName,
 | 
							GivenName:  givenName,
 | 
				
			||||||
		User:       *user,
 | 
							User:       *user,
 | 
				
			||||||
@ -538,14 +553,12 @@ func (api headscaleV1APIServer) DebugCreateNode(
 | 
				
			|||||||
		HostInfo: types.HostInfo(hostinfo),
 | 
							HostInfo: types.HostInfo(hostinfo),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodeKey := key.NodePublic{}
 | 
						log.Debug().
 | 
				
			||||||
	err = nodeKey.UnmarshalText([]byte(request.GetKey()))
 | 
							Str("machine_key", mkey.ShortString()).
 | 
				
			||||||
	if err != nil {
 | 
							Msg("adding debug machine via CLI, appending to registration cache")
 | 
				
			||||||
		log.Panic().Msg("can not add node for debug. invalid node key")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.h.registrationCache.Set(
 | 
						api.h.registrationCache.Set(
 | 
				
			||||||
		nodeKey.String(),
 | 
							mkey.String(),
 | 
				
			||||||
		newNode,
 | 
							newNode,
 | 
				
			||||||
		registerCacheExpiration,
 | 
							registerCacheExpiration,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,6 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/mux"
 | 
						"github.com/gorilla/mux"
 | 
				
			||||||
	"github.com/juanfont/headscale/hscontrol/util"
 | 
					 | 
				
			||||||
	"github.com/rs/zerolog/log"
 | 
						"github.com/rs/zerolog/log"
 | 
				
			||||||
	"tailscale.com/tailcfg"
 | 
						"tailscale.com/tailcfg"
 | 
				
			||||||
	"tailscale.com/types/key"
 | 
						"tailscale.com/types/key"
 | 
				
			||||||
@ -207,33 +206,16 @@ func (h *Headscale) RegisterWebAPI(
 | 
				
			|||||||
	req *http.Request,
 | 
						req *http.Request,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
	vars := mux.Vars(req)
 | 
						vars := mux.Vars(req)
 | 
				
			||||||
	nodeKeyStr, ok := vars["nkey"]
 | 
						machineKeyStr := vars["mkey"]
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !util.NodePublicKeyRegex.Match([]byte(nodeKeyStr)) {
 | 
					 | 
				
			||||||
		log.Warn().Str("node_key", nodeKeyStr).Msg("Invalid node key passed to registration url")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
					 | 
				
			||||||
		writer.WriteHeader(http.StatusUnauthorized)
 | 
					 | 
				
			||||||
		_, err := writer.Write([]byte("Unauthorized"))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error().
 | 
					 | 
				
			||||||
				Caller().
 | 
					 | 
				
			||||||
				Err(err).
 | 
					 | 
				
			||||||
				Msg("Failed to write response")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We need to make sure we dont open for XSS style injections, if the parameter that
 | 
						// We need to make sure we dont open for XSS style injections, if the parameter that
 | 
				
			||||||
	// is passed as a key is not parsable/validated as a NodePublic key, then fail to render
 | 
						// is passed as a key is not parsable/validated as a NodePublic key, then fail to render
 | 
				
			||||||
	// the template and log an error.
 | 
						// the template and log an error.
 | 
				
			||||||
	var nodeKey key.NodePublic
 | 
						var machineKey key.MachinePublic
 | 
				
			||||||
	err := nodeKey.UnmarshalText(
 | 
						err := machineKey.UnmarshalText(
 | 
				
			||||||
		[]byte(nodeKeyStr),
 | 
							[]byte(machineKeyStr),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
	if !ok || nodeKeyStr == "" || err != nil {
 | 
					 | 
				
			||||||
		log.Warn().Err(err).Msg("Failed to parse incoming nodekey")
 | 
							log.Warn().Err(err).Msg("Failed to parse incoming nodekey")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
							writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
				
			||||||
@ -251,7 +233,7 @@ func (h *Headscale) RegisterWebAPI(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var content bytes.Buffer
 | 
						var content bytes.Buffer
 | 
				
			||||||
	if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{
 | 
						if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{
 | 
				
			||||||
		Key: nodeKeyStr,
 | 
							Key: machineKey.String(),
 | 
				
			||||||
	}); err != nil {
 | 
						}); err != nil {
 | 
				
			||||||
		log.Error().
 | 
							log.Error().
 | 
				
			||||||
			Str("func", "RegisterWebAPI").
 | 
								Str("func", "RegisterWebAPI").
 | 
				
			||||||
 | 
				
			|||||||
@ -368,17 +368,6 @@ func (m *Mapper) marshalMapResponse(
 | 
				
			|||||||
) ([]byte, error) {
 | 
					) ([]byte, error) {
 | 
				
			||||||
	atomic.AddUint64(&m.seq, 1)
 | 
						atomic.AddUint64(&m.seq, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var machineKey key.MachinePublic
 | 
					 | 
				
			||||||
	err := machineKey.UnmarshalText([]byte(node.MachineKey))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Error().
 | 
					 | 
				
			||||||
			Caller().
 | 
					 | 
				
			||||||
			Err(err).
 | 
					 | 
				
			||||||
			Msg("Cannot parse client key")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	jsonBody, err := json.Marshal(resp)
 | 
						jsonBody, err := json.Marshal(resp)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error().
 | 
							log.Error().
 | 
				
			||||||
@ -426,11 +415,11 @@ func (m *Mapper) marshalMapResponse(
 | 
				
			|||||||
	if compression == util.ZstdCompression {
 | 
						if compression == util.ZstdCompression {
 | 
				
			||||||
		respBody = zstdEncode(jsonBody)
 | 
							respBody = zstdEncode(jsonBody)
 | 
				
			||||||
		if !m.isNoise { // if legacy protocol
 | 
							if !m.isNoise { // if legacy protocol
 | 
				
			||||||
			respBody = m.privateKey2019.SealTo(machineKey, respBody)
 | 
								respBody = m.privateKey2019.SealTo(node.MachineKey, respBody)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if !m.isNoise { // if legacy protocol
 | 
							if !m.isNoise { // if legacy protocol
 | 
				
			||||||
			respBody = m.privateKey2019.SealTo(machineKey, jsonBody)
 | 
								respBody = m.privateKey2019.SealTo(node.MachineKey, jsonBody)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			respBody = jsonBody
 | 
								respBody = jsonBody
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -167,9 +167,15 @@ func Test_fullMapResponse(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	mini := &types.Node{
 | 
						mini := &types.Node{
 | 
				
			||||||
		ID: 0,
 | 
							ID: 0,
 | 
				
			||||||
		MachineKey:  "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
							MachineKey: mustMK(
 | 
				
			||||||
		NodeKey:     "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
								"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
		DiscoKey:    "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
							),
 | 
				
			||||||
 | 
							NodeKey: mustNK(
 | 
				
			||||||
 | 
								"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
							DiscoKey: mustDK(
 | 
				
			||||||
 | 
								"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
		IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
 | 
							IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
 | 
				
			||||||
		Hostname:    "mini",
 | 
							Hostname:    "mini",
 | 
				
			||||||
		GivenName:   "mini",
 | 
							GivenName:   "mini",
 | 
				
			||||||
@ -226,7 +232,6 @@ func Test_fullMapResponse(t *testing.T) {
 | 
				
			|||||||
			netip.MustParsePrefix("0.0.0.0/0"),
 | 
								netip.MustParsePrefix("0.0.0.0/0"),
 | 
				
			||||||
			netip.MustParsePrefix("192.168.0.0/24"),
 | 
								netip.MustParsePrefix("192.168.0.0/24"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Endpoints:         []string{},
 | 
					 | 
				
			||||||
		DERP:              "127.3.3.40:0",
 | 
							DERP:              "127.3.3.40:0",
 | 
				
			||||||
		Hostinfo:          hiview(tailcfg.Hostinfo{}),
 | 
							Hostinfo:          hiview(tailcfg.Hostinfo{}),
 | 
				
			||||||
		Created:           created,
 | 
							Created:           created,
 | 
				
			||||||
@ -245,9 +250,15 @@ func Test_fullMapResponse(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	peer1 := &types.Node{
 | 
						peer1 := &types.Node{
 | 
				
			||||||
		ID: 1,
 | 
							ID: 1,
 | 
				
			||||||
		MachineKey:  "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
							MachineKey: mustMK(
 | 
				
			||||||
		NodeKey:     "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
								"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
		DiscoKey:    "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
							),
 | 
				
			||||||
 | 
							NodeKey: mustNK(
 | 
				
			||||||
 | 
								"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
							DiscoKey: mustDK(
 | 
				
			||||||
 | 
								"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
		IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
 | 
							IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
 | 
				
			||||||
		Hostname:    "peer1",
 | 
							Hostname:    "peer1",
 | 
				
			||||||
		GivenName:   "peer1",
 | 
							GivenName:   "peer1",
 | 
				
			||||||
@ -278,7 +289,6 @@ func Test_fullMapResponse(t *testing.T) {
 | 
				
			|||||||
		),
 | 
							),
 | 
				
			||||||
		Addresses:         []netip.Prefix{netip.MustParsePrefix("100.64.0.2/32")},
 | 
							Addresses:         []netip.Prefix{netip.MustParsePrefix("100.64.0.2/32")},
 | 
				
			||||||
		AllowedIPs:        []netip.Prefix{netip.MustParsePrefix("100.64.0.2/32")},
 | 
							AllowedIPs:        []netip.Prefix{netip.MustParsePrefix("100.64.0.2/32")},
 | 
				
			||||||
		Endpoints:         []string{},
 | 
					 | 
				
			||||||
		DERP:              "127.3.3.40:0",
 | 
							DERP:              "127.3.3.40:0",
 | 
				
			||||||
		Hostinfo:          hiview(tailcfg.Hostinfo{}),
 | 
							Hostinfo:          hiview(tailcfg.Hostinfo{}),
 | 
				
			||||||
		Created:           created,
 | 
							Created:           created,
 | 
				
			||||||
@ -297,9 +307,15 @@ func Test_fullMapResponse(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	peer2 := &types.Node{
 | 
						peer2 := &types.Node{
 | 
				
			||||||
		ID: 2,
 | 
							ID: 2,
 | 
				
			||||||
		MachineKey:  "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
							MachineKey: mustMK(
 | 
				
			||||||
		NodeKey:     "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
								"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
		DiscoKey:    "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
							),
 | 
				
			||||||
 | 
							NodeKey: mustNK(
 | 
				
			||||||
 | 
								"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
							DiscoKey: mustDK(
 | 
				
			||||||
 | 
								"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
		IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
 | 
							IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
 | 
				
			||||||
		Hostname:    "peer2",
 | 
							Hostname:    "peer2",
 | 
				
			||||||
		GivenName:   "peer2",
 | 
							GivenName:   "peer2",
 | 
				
			||||||
 | 
				
			|||||||
@ -52,21 +52,6 @@ func tailNode(
 | 
				
			|||||||
	baseDomain string,
 | 
						baseDomain string,
 | 
				
			||||||
	randomClientPort bool,
 | 
						randomClientPort bool,
 | 
				
			||||||
) (*tailcfg.Node, error) {
 | 
					) (*tailcfg.Node, error) {
 | 
				
			||||||
	nodeKey, err := node.NodePublicKey()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	machineKey, err := node.MachinePublicKey()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	discoKey, err := node.DiscoPublicKey()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	addrs := node.IPAddresses.Prefixes()
 | 
						addrs := node.IPAddresses.Prefixes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	allowedIPs := append(
 | 
						allowedIPs := append(
 | 
				
			||||||
@ -112,6 +97,11 @@ func tailNode(
 | 
				
			|||||||
	tags, _ := pol.TagsOfNode(node)
 | 
						tags, _ := pol.TagsOfNode(node)
 | 
				
			||||||
	tags = lo.Uniq(append(tags, node.ForcedTags...))
 | 
						tags = lo.Uniq(append(tags, node.ForcedTags...))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						endpoints, err := node.EndpointsToAddrPort()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tNode := tailcfg.Node{
 | 
						tNode := tailcfg.Node{
 | 
				
			||||||
		ID: tailcfg.NodeID(node.ID), // this is the actual ID
 | 
							ID: tailcfg.NodeID(node.ID), // this is the actual ID
 | 
				
			||||||
		StableID: tailcfg.StableNodeID(
 | 
							StableID: tailcfg.StableNodeID(
 | 
				
			||||||
@ -121,14 +111,14 @@ func tailNode(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		User: tailcfg.UserID(node.UserID),
 | 
							User: tailcfg.UserID(node.UserID),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Key:       nodeKey,
 | 
							Key:       node.NodeKey,
 | 
				
			||||||
		KeyExpiry: keyExpiry,
 | 
							KeyExpiry: keyExpiry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Machine:    machineKey,
 | 
							Machine:    node.MachineKey,
 | 
				
			||||||
		DiscoKey:   discoKey,
 | 
							DiscoKey:   node.DiscoKey,
 | 
				
			||||||
		Addresses:  addrs,
 | 
							Addresses:  addrs,
 | 
				
			||||||
		AllowedIPs: allowedIPs,
 | 
							AllowedIPs: allowedIPs,
 | 
				
			||||||
		Endpoints:  node.Endpoints,
 | 
							Endpoints:  endpoints,
 | 
				
			||||||
		DERP:       derp,
 | 
							DERP:       derp,
 | 
				
			||||||
		Hostinfo:   hostInfo.View(),
 | 
							Hostinfo:   hostInfo.View(),
 | 
				
			||||||
		Created:    node.CreatedAt,
 | 
							Created:    node.CreatedAt,
 | 
				
			||||||
 | 
				
			|||||||
@ -58,16 +58,36 @@ func TestTailNode(t *testing.T) {
 | 
				
			|||||||
			pol:        &policy.ACLPolicy{},
 | 
								pol:        &policy.ACLPolicy{},
 | 
				
			||||||
			dnsConfig:  &tailcfg.DNSConfig{},
 | 
								dnsConfig:  &tailcfg.DNSConfig{},
 | 
				
			||||||
			baseDomain: "",
 | 
								baseDomain: "",
 | 
				
			||||||
			want:       nil,
 | 
								want: &tailcfg.Node{
 | 
				
			||||||
			wantErr:    true,
 | 
									StableID:          "0",
 | 
				
			||||||
 | 
									Addresses:         []netip.Prefix{},
 | 
				
			||||||
 | 
									AllowedIPs:        []netip.Prefix{},
 | 
				
			||||||
 | 
									DERP:              "127.3.3.40:0",
 | 
				
			||||||
 | 
									Hostinfo:          hiview(tailcfg.Hostinfo{}),
 | 
				
			||||||
 | 
									Tags:              []string{},
 | 
				
			||||||
 | 
									PrimaryRoutes:     []netip.Prefix{},
 | 
				
			||||||
 | 
									Online:            new(bool),
 | 
				
			||||||
 | 
									MachineAuthorized: true,
 | 
				
			||||||
 | 
									Capabilities: []tailcfg.NodeCapability{
 | 
				
			||||||
 | 
										"https://tailscale.com/cap/file-sharing", "https://tailscale.com/cap/is-admin",
 | 
				
			||||||
 | 
										"https://tailscale.com/cap/ssh", "debug-disable-upnp",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantErr: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "minimal-node",
 | 
								name: "minimal-node",
 | 
				
			||||||
			node: &types.Node{
 | 
								node: &types.Node{
 | 
				
			||||||
				ID: 0,
 | 
									ID: 0,
 | 
				
			||||||
				MachineKey: "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
									MachineKey: mustMK(
 | 
				
			||||||
				NodeKey:    "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
										"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
				DiscoKey:   "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
									),
 | 
				
			||||||
 | 
									NodeKey: mustNK(
 | 
				
			||||||
 | 
										"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
 | 
									DiscoKey: mustDK(
 | 
				
			||||||
 | 
										"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
				IPAddresses: []netip.Addr{
 | 
									IPAddresses: []netip.Addr{
 | 
				
			||||||
					netip.MustParseAddr("100.64.0.1"),
 | 
										netip.MustParseAddr("100.64.0.1"),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@ -133,7 +153,6 @@ func TestTailNode(t *testing.T) {
 | 
				
			|||||||
					netip.MustParsePrefix("0.0.0.0/0"),
 | 
										netip.MustParsePrefix("0.0.0.0/0"),
 | 
				
			||||||
					netip.MustParsePrefix("192.168.0.0/24"),
 | 
										netip.MustParsePrefix("192.168.0.0/24"),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Endpoints: []string{},
 | 
					 | 
				
			||||||
				DERP:     "127.3.3.40:0",
 | 
									DERP:     "127.3.3.40:0",
 | 
				
			||||||
				Hostinfo: hiview(tailcfg.Hostinfo{}),
 | 
									Hostinfo: hiview(tailcfg.Hostinfo{}),
 | 
				
			||||||
				Created:  created,
 | 
									Created:  created,
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
 | 
						"tailscale.com/types/key"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Notifier struct {
 | 
					type Notifier struct {
 | 
				
			||||||
@ -17,9 +18,9 @@ func NewNotifier() *Notifier {
 | 
				
			|||||||
	return &Notifier{}
 | 
						return &Notifier{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (n *Notifier) AddNode(machineKey string, c chan<- types.StateUpdate) {
 | 
					func (n *Notifier) AddNode(machineKey key.MachinePublic, c chan<- types.StateUpdate) {
 | 
				
			||||||
	log.Trace().Caller().Str("key", machineKey).Msg("acquiring lock to add node")
 | 
						log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("acquiring lock to add node")
 | 
				
			||||||
	defer log.Trace().Caller().Str("key", machineKey).Msg("releasing lock to add node")
 | 
						defer log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("releasing lock to add node")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	n.l.Lock()
 | 
						n.l.Lock()
 | 
				
			||||||
	defer n.l.Unlock()
 | 
						defer n.l.Unlock()
 | 
				
			||||||
@ -28,17 +29,17 @@ func (n *Notifier) AddNode(machineKey string, c chan<- types.StateUpdate) {
 | 
				
			|||||||
		n.nodes = make(map[string]chan<- types.StateUpdate)
 | 
							n.nodes = make(map[string]chan<- types.StateUpdate)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	n.nodes[machineKey] = c
 | 
						n.nodes[machineKey.String()] = c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Trace().
 | 
						log.Trace().
 | 
				
			||||||
		Str("machine_key", machineKey).
 | 
							Str("machine_key", machineKey.ShortString()).
 | 
				
			||||||
		Int("open_chans", len(n.nodes)).
 | 
							Int("open_chans", len(n.nodes)).
 | 
				
			||||||
		Msg("Added new channel")
 | 
							Msg("Added new channel")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (n *Notifier) RemoveNode(machineKey string) {
 | 
					func (n *Notifier) RemoveNode(machineKey key.MachinePublic) {
 | 
				
			||||||
	log.Trace().Caller().Str("key", machineKey).Msg("acquiring lock to remove node")
 | 
						log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("acquiring lock to remove node")
 | 
				
			||||||
	defer log.Trace().Caller().Str("key", machineKey).Msg("releasing lock to remove node")
 | 
						defer log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("releasing lock to remove node")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	n.l.Lock()
 | 
						n.l.Lock()
 | 
				
			||||||
	defer n.l.Unlock()
 | 
						defer n.l.Unlock()
 | 
				
			||||||
@ -47,10 +48,10 @@ func (n *Notifier) RemoveNode(machineKey string) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	delete(n.nodes, machineKey)
 | 
						delete(n.nodes, machineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Trace().
 | 
						log.Trace().
 | 
				
			||||||
		Str("machine_key", machineKey).
 | 
							Str("machine_key", machineKey.ShortString()).
 | 
				
			||||||
		Int("open_chans", len(n.nodes)).
 | 
							Int("open_chans", len(n.nodes)).
 | 
				
			||||||
		Msg("Removed channel")
 | 
							Msg("Removed channel")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -90,42 +90,28 @@ func (h *Headscale) determineTokenExpiration(idTokenExpiration time.Time) time.T
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RegisterOIDC redirects to the OIDC provider for authentication
 | 
					// RegisterOIDC redirects to the OIDC provider for authentication
 | 
				
			||||||
// Puts NodeKey in cache so the callback can retrieve it using the oidc state param
 | 
					// Puts NodeKey in cache so the callback can retrieve it using the oidc state param
 | 
				
			||||||
// Listens in /oidc/register/:nKey.
 | 
					// Listens in /oidc/register/:mKey.
 | 
				
			||||||
func (h *Headscale) RegisterOIDC(
 | 
					func (h *Headscale) RegisterOIDC(
 | 
				
			||||||
	writer http.ResponseWriter,
 | 
						writer http.ResponseWriter,
 | 
				
			||||||
	req *http.Request,
 | 
						req *http.Request,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
	vars := mux.Vars(req)
 | 
						vars := mux.Vars(req)
 | 
				
			||||||
	nodeKeyStr, ok := vars["nkey"]
 | 
						machineKeyStr, ok := vars["mkey"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Debug().
 | 
						log.Debug().
 | 
				
			||||||
		Caller().
 | 
							Caller().
 | 
				
			||||||
		Str("node_key", nodeKeyStr).
 | 
							Str("machine_key", machineKeyStr).
 | 
				
			||||||
		Bool("ok", ok).
 | 
							Bool("ok", ok).
 | 
				
			||||||
		Msg("Received oidc register call")
 | 
							Msg("Received oidc register call")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !util.NodePublicKeyRegex.Match([]byte(nodeKeyStr)) {
 | 
					 | 
				
			||||||
		log.Warn().Str("node_key", nodeKeyStr).Msg("Invalid node key passed to registration url")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
					 | 
				
			||||||
		writer.WriteHeader(http.StatusUnauthorized)
 | 
					 | 
				
			||||||
		_, err := writer.Write([]byte("Unauthorized"))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			util.LogErr(err, "Failed to write response")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// We need to make sure we dont open for XSS style injections, if the parameter that
 | 
						// We need to make sure we dont open for XSS style injections, if the parameter that
 | 
				
			||||||
	// is passed as a key is not parsable/validated as a NodePublic key, then fail to render
 | 
						// is passed as a key is not parsable/validated as a NodePublic key, then fail to render
 | 
				
			||||||
	// the template and log an error.
 | 
						// the template and log an error.
 | 
				
			||||||
	var nodeKey key.NodePublic
 | 
						var machineKey key.MachinePublic
 | 
				
			||||||
	err := nodeKey.UnmarshalText(
 | 
						err := machineKey.UnmarshalText(
 | 
				
			||||||
		[]byte(nodeKeyStr),
 | 
							[]byte(machineKeyStr),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
	if !ok || nodeKeyStr == "" || err != nil {
 | 
					 | 
				
			||||||
		log.Warn().
 | 
							log.Warn().
 | 
				
			||||||
			Err(err).
 | 
								Err(err).
 | 
				
			||||||
			Msg("Failed to parse incoming nodekey in OIDC registration")
 | 
								Msg("Failed to parse incoming nodekey in OIDC registration")
 | 
				
			||||||
@ -154,7 +140,7 @@ func (h *Headscale) RegisterOIDC(
 | 
				
			|||||||
	// place the node key into the state cache, so it can be retrieved later
 | 
						// place the node key into the state cache, so it can be retrieved later
 | 
				
			||||||
	h.registrationCache.Set(
 | 
						h.registrationCache.Set(
 | 
				
			||||||
		stateStr,
 | 
							stateStr,
 | 
				
			||||||
		nodeKey,
 | 
							machineKey,
 | 
				
			||||||
		registerCacheExpiration,
 | 
							registerCacheExpiration,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -232,7 +218,7 @@ func (h *Headscale) OIDCCallback(
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodeKey, nodeExists, err := h.validateNodeForOIDCCallback(
 | 
						machineKey, nodeExists, err := h.validateNodeForOIDCCallback(
 | 
				
			||||||
		writer,
 | 
							writer,
 | 
				
			||||||
		state,
 | 
							state,
 | 
				
			||||||
		claims,
 | 
							claims,
 | 
				
			||||||
@ -255,7 +241,7 @@ func (h *Headscale) OIDCCallback(
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := h.registerNodeForOIDCCallback(writer, user, nodeKey, idTokenExpiry); err != nil {
 | 
						if err := h.registerNodeForOIDCCallback(writer, user, machineKey, idTokenExpiry); err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -462,10 +448,10 @@ func (h *Headscale) validateNodeForOIDCCallback(
 | 
				
			|||||||
	state string,
 | 
						state string,
 | 
				
			||||||
	claims *IDTokenClaims,
 | 
						claims *IDTokenClaims,
 | 
				
			||||||
	expiry time.Time,
 | 
						expiry time.Time,
 | 
				
			||||||
) (*key.NodePublic, bool, error) {
 | 
					) (*key.MachinePublic, bool, error) {
 | 
				
			||||||
	// retrieve nodekey from state cache
 | 
						// retrieve nodekey from state cache
 | 
				
			||||||
	nodeKeyIf, nodeKeyFound := h.registrationCache.Get(state)
 | 
						machineKeyIf, machineKeyFound := h.registrationCache.Get(state)
 | 
				
			||||||
	if !nodeKeyFound {
 | 
						if !machineKeyFound {
 | 
				
			||||||
		log.Trace().
 | 
							log.Trace().
 | 
				
			||||||
			Msg("requested node state key expired before authorisation completed")
 | 
								Msg("requested node state key expired before authorisation completed")
 | 
				
			||||||
		writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
							writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
				
			||||||
@ -478,11 +464,11 @@ func (h *Headscale) validateNodeForOIDCCallback(
 | 
				
			|||||||
		return nil, false, errOIDCNodeKeyMissing
 | 
							return nil, false, errOIDCNodeKeyMissing
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var nodeKey key.NodePublic
 | 
						var machineKey key.MachinePublic
 | 
				
			||||||
	nodeKey, nodeKeyOK := nodeKeyIf.(key.NodePublic)
 | 
						machineKey, machineKeyOK := machineKeyIf.(key.MachinePublic)
 | 
				
			||||||
	if !nodeKeyOK {
 | 
						if !machineKeyOK {
 | 
				
			||||||
		log.Trace().
 | 
							log.Trace().
 | 
				
			||||||
			Interface("got", nodeKeyIf).
 | 
								Interface("got", machineKeyIf).
 | 
				
			||||||
			Msg("requested node state key is not a nodekey")
 | 
								Msg("requested node state key is not a nodekey")
 | 
				
			||||||
		writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
							writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
				
			||||||
		writer.WriteHeader(http.StatusBadRequest)
 | 
							writer.WriteHeader(http.StatusBadRequest)
 | 
				
			||||||
@ -498,7 +484,7 @@ func (h *Headscale) validateNodeForOIDCCallback(
 | 
				
			|||||||
	// The error is not important, because if it does not
 | 
						// The error is not important, because if it does not
 | 
				
			||||||
	// exist, then this is a new node and we will move
 | 
						// exist, then this is a new node and we will move
 | 
				
			||||||
	// on to registration.
 | 
						// on to registration.
 | 
				
			||||||
	node, _ := h.db.GetNodeByNodeKey(nodeKey)
 | 
						node, _ := h.db.GetNodeByMachineKey(machineKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if node != nil {
 | 
						if node != nil {
 | 
				
			||||||
		log.Trace().
 | 
							log.Trace().
 | 
				
			||||||
@ -553,7 +539,7 @@ func (h *Headscale) validateNodeForOIDCCallback(
 | 
				
			|||||||
		return nil, true, nil
 | 
							return nil, true, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &nodeKey, false, nil
 | 
						return &machineKey, false, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getUserName(
 | 
					func getUserName(
 | 
				
			||||||
@ -624,13 +610,13 @@ func (h *Headscale) findOrCreateNewUserForOIDCCallback(
 | 
				
			|||||||
func (h *Headscale) registerNodeForOIDCCallback(
 | 
					func (h *Headscale) registerNodeForOIDCCallback(
 | 
				
			||||||
	writer http.ResponseWriter,
 | 
						writer http.ResponseWriter,
 | 
				
			||||||
	user *types.User,
 | 
						user *types.User,
 | 
				
			||||||
	nodeKey *key.NodePublic,
 | 
						machineKey *key.MachinePublic,
 | 
				
			||||||
	expiry time.Time,
 | 
						expiry time.Time,
 | 
				
			||||||
) error {
 | 
					) error {
 | 
				
			||||||
	if _, err := h.db.RegisterNodeFromAuthCallback(
 | 
						if _, err := h.db.RegisterNodeFromAuthCallback(
 | 
				
			||||||
		// TODO(kradalby): find a better way to use the cache across modules
 | 
							// TODO(kradalby): find a better way to use the cache across modules
 | 
				
			||||||
		h.registrationCache,
 | 
							h.registrationCache,
 | 
				
			||||||
		nodeKey.String(),
 | 
							*machineKey,
 | 
				
			||||||
		user.Name,
 | 
							user.Name,
 | 
				
			||||||
		&expiry,
 | 
							&expiry,
 | 
				
			||||||
		util.RegisterMethodOIDC,
 | 
							util.RegisterMethodOIDC,
 | 
				
			||||||
 | 
				
			|||||||
@ -14,12 +14,29 @@ import (
 | 
				
			|||||||
	"go4.org/netipx"
 | 
						"go4.org/netipx"
 | 
				
			||||||
	"gopkg.in/check.v1"
 | 
						"gopkg.in/check.v1"
 | 
				
			||||||
	"tailscale.com/tailcfg"
 | 
						"tailscale.com/tailcfg"
 | 
				
			||||||
 | 
						"tailscale.com/types/key"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var ipComparer = cmp.Comparer(func(x, y netip.Addr) bool {
 | 
					var ipComparer = cmp.Comparer(func(x, y netip.Addr) bool {
 | 
				
			||||||
	return x.Compare(y) == 0
 | 
						return x.Compare(y) == 0
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var mkeyComparer = cmp.Comparer(func(x, y key.MachinePublic) bool {
 | 
				
			||||||
 | 
						return x.String() == y.String()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var nkeyComparer = cmp.Comparer(func(x, y key.NodePublic) bool {
 | 
				
			||||||
 | 
						return x.String() == y.String()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var dkeyComparer = cmp.Comparer(func(x, y key.DiscoPublic) bool {
 | 
				
			||||||
 | 
						return x.String() == y.String()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var keyComparers []cmp.Option = []cmp.Option{
 | 
				
			||||||
 | 
						mkeyComparer, nkeyComparer, dkeyComparer,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Test(t *testing.T) {
 | 
					func Test(t *testing.T) {
 | 
				
			||||||
	check.TestingT(t)
 | 
						check.TestingT(t)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -951,7 +968,7 @@ func Test_listNodesInUser(t *testing.T) {
 | 
				
			|||||||
		t.Run(test.name, func(t *testing.T) {
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
			got := filterNodesByUser(test.args.nodes, test.args.user)
 | 
								got := filterNodesByUser(test.args.nodes, test.args.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if diff := cmp.Diff(test.want, got); diff != "" {
 | 
								if diff := cmp.Diff(test.want, got, keyComparers...); diff != "" {
 | 
				
			||||||
				t.Errorf("listNodesInUser() = (-want +got):\n%s", diff)
 | 
									t.Errorf("listNodesInUser() = (-want +got):\n%s", diff)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
@ -1704,7 +1721,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
 | 
				
			|||||||
				test.args.nodes,
 | 
									test.args.nodes,
 | 
				
			||||||
				test.args.user,
 | 
									test.args.user,
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
			if diff := cmp.Diff(test.want, got, ipComparer); diff != "" {
 | 
								if diff := cmp.Diff(test.want, got, ipComparer, mkeyComparer, nkeyComparer, dkeyComparer); diff != "" {
 | 
				
			||||||
				t.Errorf("excludeCorrectlyTaggedNodes() (-want +got):\n%s", diff)
 | 
									t.Errorf("excludeCorrectlyTaggedNodes() (-want +got):\n%s", diff)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
@ -2723,7 +2740,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
 | 
				
			|||||||
				tt.args.nodes,
 | 
									tt.args.nodes,
 | 
				
			||||||
				tt.args.rules,
 | 
									tt.args.rules,
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
			if diff := cmp.Diff(tt.want, got, ipComparer); diff != "" {
 | 
								if diff := cmp.Diff(tt.want, got, ipComparer, mkeyComparer, nkeyComparer, dkeyComparer); diff != "" {
 | 
				
			||||||
				t.Errorf("FilterNodesByACL() unexpected result (-want +got):\n%s", diff)
 | 
									t.Errorf("FilterNodesByACL() unexpected result (-want +got):\n%s", diff)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
@ -2986,9 +3003,6 @@ func TestValidExpandTagOwnersInSources(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:          0,
 | 
							ID:          0,
 | 
				
			||||||
		MachineKey:  "foo",
 | 
					 | 
				
			||||||
		NodeKey:     "bar",
 | 
					 | 
				
			||||||
		DiscoKey:    "faa",
 | 
					 | 
				
			||||||
		Hostname:    "testnodes",
 | 
							Hostname:    "testnodes",
 | 
				
			||||||
		IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
							IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
				
			||||||
		UserID:      0,
 | 
							UserID:      0,
 | 
				
			||||||
@ -3041,9 +3055,6 @@ func TestInvalidTagValidUser(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:          1,
 | 
							ID:          1,
 | 
				
			||||||
		MachineKey:  "12345",
 | 
					 | 
				
			||||||
		NodeKey:     "bar",
 | 
					 | 
				
			||||||
		DiscoKey:    "faa",
 | 
					 | 
				
			||||||
		Hostname:    "testnodes",
 | 
							Hostname:    "testnodes",
 | 
				
			||||||
		IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
							IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
				
			||||||
		UserID:      1,
 | 
							UserID:      1,
 | 
				
			||||||
@ -3095,9 +3106,6 @@ func TestValidExpandTagOwnersInDestinations(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:          1,
 | 
							ID:          1,
 | 
				
			||||||
		MachineKey:  "12345",
 | 
					 | 
				
			||||||
		NodeKey:     "bar",
 | 
					 | 
				
			||||||
		DiscoKey:    "faa",
 | 
					 | 
				
			||||||
		Hostname:    "testnodes",
 | 
							Hostname:    "testnodes",
 | 
				
			||||||
		IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
							IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
				
			||||||
		UserID:      1,
 | 
							UserID:      1,
 | 
				
			||||||
@ -3159,9 +3167,6 @@ func TestValidTagInvalidUser(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	node := &types.Node{
 | 
						node := &types.Node{
 | 
				
			||||||
		ID:          1,
 | 
							ID:          1,
 | 
				
			||||||
		MachineKey:  "12345",
 | 
					 | 
				
			||||||
		NodeKey:     "bar",
 | 
					 | 
				
			||||||
		DiscoKey:    "faa",
 | 
					 | 
				
			||||||
		Hostname:    "webserver",
 | 
							Hostname:    "webserver",
 | 
				
			||||||
		IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
							IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")},
 | 
				
			||||||
		UserID:      1,
 | 
							UserID:      1,
 | 
				
			||||||
@ -3179,9 +3184,6 @@ func TestValidTagInvalidUser(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	nodes2 := &types.Node{
 | 
						nodes2 := &types.Node{
 | 
				
			||||||
		ID:          2,
 | 
							ID:          2,
 | 
				
			||||||
		MachineKey:  "56789",
 | 
					 | 
				
			||||||
		NodeKey:     "bar2",
 | 
					 | 
				
			||||||
		DiscoKey:    "faab",
 | 
					 | 
				
			||||||
		Hostname:    "user",
 | 
							Hostname:    "user",
 | 
				
			||||||
		IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.2")},
 | 
							IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.2")},
 | 
				
			||||||
		UserID:      1,
 | 
							UserID:      1,
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ func logPollFunc(
 | 
				
			|||||||
				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).
 | 
									Str("node_key", node.NodeKey.ShortString()).
 | 
				
			||||||
				Str("node", node.Hostname).
 | 
									Str("node", node.Hostname).
 | 
				
			||||||
				Msg(msg)
 | 
									Msg(msg)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@ -45,7 +45,7 @@ func logPollFunc(
 | 
				
			|||||||
				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).
 | 
									Str("node_key", node.NodeKey.ShortString()).
 | 
				
			||||||
				Str("node", node.Hostname).
 | 
									Str("node", node.Hostname).
 | 
				
			||||||
				Err(err).
 | 
									Err(err).
 | 
				
			||||||
				Msg(msg)
 | 
									Msg(msg)
 | 
				
			||||||
@ -81,7 +81,7 @@ func (h *Headscale) handlePoll(
 | 
				
			|||||||
			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).
 | 
								Str("node_key", node.NodeKey.ShortString()).
 | 
				
			||||||
			Str("node", node.Hostname).
 | 
								Str("node", node.Hostname).
 | 
				
			||||||
			Strs("endpoints", node.Endpoints).
 | 
								Strs("endpoints", node.Endpoints).
 | 
				
			||||||
			Msg("Received endpoint update")
 | 
								Msg("Received endpoint update")
 | 
				
			||||||
@ -90,8 +90,8 @@ func (h *Headscale) handlePoll(
 | 
				
			|||||||
		node.LastSeen = &now
 | 
							node.LastSeen = &now
 | 
				
			||||||
		node.Hostname = mapRequest.Hostinfo.Hostname
 | 
							node.Hostname = mapRequest.Hostinfo.Hostname
 | 
				
			||||||
		node.HostInfo = types.HostInfo(*mapRequest.Hostinfo)
 | 
							node.HostInfo = types.HostInfo(*mapRequest.Hostinfo)
 | 
				
			||||||
		node.DiscoKey = mapRequest.DiscoKey.String()
 | 
							node.DiscoKey = mapRequest.DiscoKey
 | 
				
			||||||
		node.Endpoints = mapRequest.Endpoints
 | 
							node.SetEndpointsFromAddrPorts(mapRequest.Endpoints)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := h.db.NodeSave(node); err != nil {
 | 
							if err := h.db.NodeSave(node); err != nil {
 | 
				
			||||||
			logErr(err, "Failed to persist/update node in the database")
 | 
								logErr(err, "Failed to persist/update node in the database")
 | 
				
			||||||
@ -113,7 +113,7 @@ func (h *Headscale) handlePoll(
 | 
				
			|||||||
				Type:    types.StatePeerChanged,
 | 
									Type:    types.StatePeerChanged,
 | 
				
			||||||
				Changed: types.Nodes{node},
 | 
									Changed: types.Nodes{node},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			node.MachineKey)
 | 
								node.MachineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		writer.WriteHeader(http.StatusOK)
 | 
							writer.WriteHeader(http.StatusOK)
 | 
				
			||||||
		if f, ok := writer.(http.Flusher); ok {
 | 
							if f, ok := writer.(http.Flusher); ok {
 | 
				
			||||||
@ -143,8 +143,8 @@ func (h *Headscale) handlePoll(
 | 
				
			|||||||
	node.LastSeen = &now
 | 
						node.LastSeen = &now
 | 
				
			||||||
	node.Hostname = mapRequest.Hostinfo.Hostname
 | 
						node.Hostname = mapRequest.Hostinfo.Hostname
 | 
				
			||||||
	node.HostInfo = types.HostInfo(*mapRequest.Hostinfo)
 | 
						node.HostInfo = types.HostInfo(*mapRequest.Hostinfo)
 | 
				
			||||||
	node.DiscoKey = mapRequest.DiscoKey.String()
 | 
						node.DiscoKey = mapRequest.DiscoKey
 | 
				
			||||||
	node.Endpoints = mapRequest.Endpoints
 | 
						node.SetEndpointsFromAddrPorts(mapRequest.Endpoints)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// When a node connects to control, list the peers it has at
 | 
						// When a node connects to control, list the peers it has at
 | 
				
			||||||
	// that given point, further updates are kept in memory in
 | 
						// that given point, further updates are kept in memory in
 | 
				
			||||||
@ -222,7 +222,7 @@ func (h *Headscale) handlePoll(
 | 
				
			|||||||
			Type:    types.StatePeerChanged,
 | 
								Type:    types.StatePeerChanged,
 | 
				
			||||||
			Changed: types.Nodes{node},
 | 
								Changed: types.Nodes{node},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		node.MachineKey)
 | 
							node.MachineKey.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set up the client stream
 | 
						// Set up the client stream
 | 
				
			||||||
	h.pollNetMapStreamWG.Add(1)
 | 
						h.pollNetMapStreamWG.Add(1)
 | 
				
			||||||
@ -342,7 +342,7 @@ func (h *Headscale) handlePoll(
 | 
				
			|||||||
				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).
 | 
									Str("node_key", node.NodeKey.ShortString()).
 | 
				
			||||||
				Str("node", node.Hostname).
 | 
									Str("node", node.Hostname).
 | 
				
			||||||
				TimeDiff("timeSpent", time.Now(), now).
 | 
									TimeDiff("timeSpent", time.Now(), now).
 | 
				
			||||||
				Msg("update sent")
 | 
									Msg("update sent")
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
	"github.com/juanfont/headscale/hscontrol/policy/matcher"
 | 
						"github.com/juanfont/headscale/hscontrol/policy/matcher"
 | 
				
			||||||
	"go4.org/netipx"
 | 
						"go4.org/netipx"
 | 
				
			||||||
	"google.golang.org/protobuf/types/known/timestamppb"
 | 
						"google.golang.org/protobuf/types/known/timestamppb"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
	"tailscale.com/tailcfg"
 | 
						"tailscale.com/tailcfg"
 | 
				
			||||||
	"tailscale.com/types/key"
 | 
						"tailscale.com/types/key"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -25,9 +26,29 @@ var (
 | 
				
			|||||||
// Node is a Headscale client.
 | 
					// Node is a Headscale client.
 | 
				
			||||||
type Node struct {
 | 
					type Node struct {
 | 
				
			||||||
	ID uint64 `gorm:"primary_key"`
 | 
						ID uint64 `gorm:"primary_key"`
 | 
				
			||||||
	MachineKey  string `gorm:"type:varchar(64);unique_index"`
 | 
					
 | 
				
			||||||
	NodeKey     string
 | 
						// MachineKeyValue is the string representation of MachineKey
 | 
				
			||||||
	DiscoKey    string
 | 
						// it is _only_ used for reading and writing the key to the
 | 
				
			||||||
 | 
						// database and should not be used.
 | 
				
			||||||
 | 
						// Use MachineKey instead.
 | 
				
			||||||
 | 
						MachineKeyValue string `gorm:"column:machine_key;unique_index"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// NodeKeyValue is the string representation of NodeKey
 | 
				
			||||||
 | 
						// it is _only_ used for reading and writing the key to the
 | 
				
			||||||
 | 
						// database and should not be used.
 | 
				
			||||||
 | 
						// Use NodeKey instead.
 | 
				
			||||||
 | 
						NodeKeyValue string `gorm:"column:node_key"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// DiscoKeyValue is the string representation of DiscoKey
 | 
				
			||||||
 | 
						// it is _only_ used for reading and writing the key to the
 | 
				
			||||||
 | 
						// database and should not be used.
 | 
				
			||||||
 | 
						// Use DiscoKey instead.
 | 
				
			||||||
 | 
						DiscoKeyValue string `gorm:"column:disco_key"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MachineKey key.MachinePublic `gorm:"-"`
 | 
				
			||||||
 | 
						NodeKey    key.NodePublic    `gorm:"-"`
 | 
				
			||||||
 | 
						DiscoKey   key.DiscoPublic   `gorm:"-"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	IPAddresses NodeAddresses
 | 
						IPAddresses NodeAddresses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Hostname represents the name given by the Tailscale
 | 
						// Hostname represents the name given by the Tailscale
 | 
				
			||||||
@ -174,6 +195,31 @@ func (node Node) IsExpired() bool {
 | 
				
			|||||||
	return time.Now().UTC().After(*node.Expiry)
 | 
						return time.Now().UTC().After(*node.Expiry)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO(kradalby): Try to replace the types in the DB to be correct.
 | 
				
			||||||
 | 
					func (node *Node) EndpointsToAddrPort() ([]netip.AddrPort, error) {
 | 
				
			||||||
 | 
						var ret []netip.AddrPort
 | 
				
			||||||
 | 
						for _, ep := range node.Endpoints {
 | 
				
			||||||
 | 
							addrPort, err := netip.ParseAddrPort(ep)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = append(ret, addrPort)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO(kradalby): Try to replace the types in the DB to be correct.
 | 
				
			||||||
 | 
					func (node *Node) SetEndpointsFromAddrPorts(in []netip.AddrPort) {
 | 
				
			||||||
 | 
						var strs StringList
 | 
				
			||||||
 | 
						for _, addrPort := range in {
 | 
				
			||||||
 | 
							strs = append(strs, addrPort.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						node.Endpoints = strs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsOnline returns if the node is connected to Headscale.
 | 
					// IsOnline returns if the node is connected to Headscale.
 | 
				
			||||||
// This is really a naive implementation, as we don't really see
 | 
					// This is really a naive implementation, as we don't really see
 | 
				
			||||||
// if there is a working connection between the client and the server.
 | 
					// if there is a working connection between the client and the server.
 | 
				
			||||||
@ -226,13 +272,52 @@ func (nodes Nodes) FilterByIP(ip netip.Addr) Nodes {
 | 
				
			|||||||
	return found
 | 
						return found
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BeforeSave is a hook that ensures that some values that
 | 
				
			||||||
 | 
					// cannot be directly marshalled into database values are stored
 | 
				
			||||||
 | 
					// correctly in the database.
 | 
				
			||||||
 | 
					// This currently means storing the keys as strings.
 | 
				
			||||||
 | 
					func (n *Node) BeforeSave(tx *gorm.DB) (err error) {
 | 
				
			||||||
 | 
						n.MachineKeyValue = n.MachineKey.String()
 | 
				
			||||||
 | 
						n.NodeKeyValue = n.NodeKey.String()
 | 
				
			||||||
 | 
						n.DiscoKeyValue = n.DiscoKey.String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AfterFind is a hook that ensures that Node objects fields that
 | 
				
			||||||
 | 
					// has a different type in the database is unwrapped and populated
 | 
				
			||||||
 | 
					// correctly.
 | 
				
			||||||
 | 
					// This currently unmarshals all the keys, stored as strings, into
 | 
				
			||||||
 | 
					// the proper types.
 | 
				
			||||||
 | 
					func (n *Node) AfterFind(tx *gorm.DB) (err error) {
 | 
				
			||||||
 | 
						var machineKey key.MachinePublic
 | 
				
			||||||
 | 
						if err := machineKey.UnmarshalText([]byte(n.MachineKeyValue)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n.MachineKey = machineKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var nodeKey key.NodePublic
 | 
				
			||||||
 | 
						if err := nodeKey.UnmarshalText([]byte(n.NodeKeyValue)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n.NodeKey = nodeKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var discoKey key.DiscoPublic
 | 
				
			||||||
 | 
						if err := discoKey.UnmarshalText([]byte(n.DiscoKeyValue)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n.DiscoKey = discoKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (node *Node) Proto() *v1.Node {
 | 
					func (node *Node) Proto() *v1.Node {
 | 
				
			||||||
	nodeProto := &v1.Node{
 | 
						nodeProto := &v1.Node{
 | 
				
			||||||
		Id:         node.ID,
 | 
							Id:         node.ID,
 | 
				
			||||||
		MachineKey: node.MachineKey,
 | 
							MachineKey: node.MachineKey.String(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		NodeKey:     node.NodeKey,
 | 
							NodeKey:     node.NodeKey.String(),
 | 
				
			||||||
		DiscoKey:    node.DiscoKey,
 | 
							DiscoKey:    node.DiscoKey.String(),
 | 
				
			||||||
		IpAddresses: node.IPAddresses.StringSlice(),
 | 
							IpAddresses: node.IPAddresses.StringSlice(),
 | 
				
			||||||
		Name:        node.Hostname,
 | 
							Name:        node.Hostname,
 | 
				
			||||||
		GivenName:   node.GivenName,
 | 
							GivenName:   node.GivenName,
 | 
				
			||||||
@ -289,47 +374,6 @@ func (node *Node) GetFQDN(dnsConfig *tailcfg.DNSConfig, baseDomain string) (stri
 | 
				
			|||||||
	return hostname, nil
 | 
						return hostname, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (node *Node) MachinePublicKey() (key.MachinePublic, error) {
 | 
					 | 
				
			||||||
	var machineKey key.MachinePublic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if node.MachineKey != "" {
 | 
					 | 
				
			||||||
		err := machineKey.UnmarshalText(
 | 
					 | 
				
			||||||
			[]byte(node.MachineKey),
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return key.MachinePublic{}, fmt.Errorf("failed to parse machine public key: %w", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return machineKey, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (node *Node) DiscoPublicKey() (key.DiscoPublic, error) {
 | 
					 | 
				
			||||||
	var discoKey key.DiscoPublic
 | 
					 | 
				
			||||||
	if node.DiscoKey != "" {
 | 
					 | 
				
			||||||
		err := discoKey.UnmarshalText(
 | 
					 | 
				
			||||||
			[]byte(node.DiscoKey),
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return key.DiscoPublic{}, fmt.Errorf("failed to parse disco public key: %w", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		discoKey = key.DiscoPublic{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return discoKey, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (node *Node) NodePublicKey() (key.NodePublic, error) {
 | 
					 | 
				
			||||||
	var nodeKey key.NodePublic
 | 
					 | 
				
			||||||
	err := nodeKey.UnmarshalText([]byte(node.NodeKey))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return key.NodePublic{}, fmt.Errorf("failed to parse node public key: %w", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nodeKey, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (node Node) String() string {
 | 
					func (node Node) String() string {
 | 
				
			||||||
	return node.Hostname
 | 
						return node.Hostname
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -683,8 +683,8 @@ func TestNodeTagCommand(t *testing.T) {
 | 
				
			|||||||
	assertNoErr(t, err)
 | 
						assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	machineKeys := []string{
 | 
						machineKeys := []string{
 | 
				
			||||||
		"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
							"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
		"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
							"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodes := make([]*v1.Node, len(machineKeys))
 | 
						nodes := make([]*v1.Node, len(machineKeys))
 | 
				
			||||||
	assert.Nil(t, err)
 | 
						assert.Nil(t, err)
 | 
				
			||||||
@ -816,13 +816,13 @@ func TestNodeCommand(t *testing.T) {
 | 
				
			|||||||
	headscale, err := scenario.Headscale()
 | 
						headscale, err := scenario.Headscale()
 | 
				
			||||||
	assertNoErr(t, err)
 | 
						assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Randomly generated node keys
 | 
						// Pregenerated machine keys
 | 
				
			||||||
	machineKeys := []string{
 | 
						machineKeys := []string{
 | 
				
			||||||
		"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
							"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
		"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
							"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
				
			||||||
		"nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
							"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
		"nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
							"mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
				
			||||||
		"nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
							"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodes := make([]*v1.Node, len(machineKeys))
 | 
						nodes := make([]*v1.Node, len(machineKeys))
 | 
				
			||||||
	assert.Nil(t, err)
 | 
						assert.Nil(t, err)
 | 
				
			||||||
@ -898,8 +898,8 @@ func TestNodeCommand(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, "node-5", listAll[4].Name)
 | 
						assert.Equal(t, "node-5", listAll[4].Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	otherUserMachineKeys := []string{
 | 
						otherUserMachineKeys := []string{
 | 
				
			||||||
		"nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
 | 
							"mkey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
 | 
				
			||||||
		"nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
 | 
							"mkey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	otherUserMachines := make([]*v1.Node, len(otherUserMachineKeys))
 | 
						otherUserMachines := make([]*v1.Node, len(otherUserMachineKeys))
 | 
				
			||||||
	assert.Nil(t, err)
 | 
						assert.Nil(t, err)
 | 
				
			||||||
@ -1056,13 +1056,13 @@ func TestNodeExpireCommand(t *testing.T) {
 | 
				
			|||||||
	headscale, err := scenario.Headscale()
 | 
						headscale, err := scenario.Headscale()
 | 
				
			||||||
	assertNoErr(t, err)
 | 
						assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Randomly generated node keys
 | 
						// Pregenerated machine keys
 | 
				
			||||||
	machineKeys := []string{
 | 
						machineKeys := []string{
 | 
				
			||||||
		"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
							"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
		"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
							"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
				
			||||||
		"nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
							"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
		"nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
							"mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
				
			||||||
		"nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
							"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodes := make([]*v1.Node, len(machineKeys))
 | 
						nodes := make([]*v1.Node, len(machineKeys))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1183,13 +1183,13 @@ func TestNodeRenameCommand(t *testing.T) {
 | 
				
			|||||||
	headscale, err := scenario.Headscale()
 | 
						headscale, err := scenario.Headscale()
 | 
				
			||||||
	assertNoErr(t, err)
 | 
						assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Randomly generated node keys
 | 
						// Pregenerated machine keys
 | 
				
			||||||
	machineKeys := []string{
 | 
						machineKeys := []string{
 | 
				
			||||||
		"nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
							"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
				
			||||||
		"nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
							"mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
				
			||||||
		"nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
							"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
				
			||||||
		"nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
							"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
				
			||||||
		"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
							"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodes := make([]*v1.Node, len(machineKeys))
 | 
						nodes := make([]*v1.Node, len(machineKeys))
 | 
				
			||||||
	assert.Nil(t, err)
 | 
						assert.Nil(t, err)
 | 
				
			||||||
@ -1210,7 +1210,7 @@ func TestNodeRenameCommand(t *testing.T) {
 | 
				
			|||||||
				"json",
 | 
									"json",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		assert.Nil(t, err)
 | 
							assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var node v1.Node
 | 
							var node v1.Node
 | 
				
			||||||
		err = executeAndUnmarshal(
 | 
							err = executeAndUnmarshal(
 | 
				
			||||||
@ -1228,7 +1228,7 @@ func TestNodeRenameCommand(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
			&node,
 | 
								&node,
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		assert.Nil(t, err)
 | 
							assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nodes[index] = &node
 | 
							nodes[index] = &node
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -1350,7 +1350,7 @@ func TestNodeMoveCommand(t *testing.T) {
 | 
				
			|||||||
	assertNoErr(t, err)
 | 
						assertNoErr(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Randomly generated node key
 | 
						// Randomly generated node key
 | 
				
			||||||
	machineKey := "nodekey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
 | 
						machineKey := "mkey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = headscale.Execute(
 | 
						_, err = headscale.Execute(
 | 
				
			||||||
		[]string{
 | 
							[]string{
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user