From ed4e19996b33f35ddbca02a01bd68aaa11e4b01d Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 19 Nov 2023 22:37:04 +0100 Subject: [PATCH] Use tailscale key types instead of strings (#1609) * upgrade tailscale Signed-off-by: Kristoffer Dalby * 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 * use key types throughout the code Signed-off-by: Kristoffer Dalby * make sure machinekey is concistently used Signed-off-by: Kristoffer Dalby * use machine key in auth url Signed-off-by: Kristoffer Dalby * fix web register Signed-off-by: Kristoffer Dalby * use key type in notifier Signed-off-by: Kristoffer Dalby * fix relogin with webauth Signed-off-by: Kristoffer Dalby --------- Signed-off-by: Kristoffer Dalby --- cmd/headscale/cli/debug.go | 10 ++- flake.nix | 2 +- go.mod | 67 +++++++-------- go.sum | 142 ++++++++++++++++---------------- hscontrol/app.go | 4 +- hscontrol/auth.go | 38 ++++----- hscontrol/db/db.go | 77 ++++++++++------- hscontrol/db/node.go | 59 +++++++------ hscontrol/db/node_test.go | 94 ++++++++++++--------- hscontrol/db/suite_test.go | 14 ++-- hscontrol/grpcv1.go | 33 +++++--- hscontrol/handlers.go | 30 ++----- hscontrol/mapper/mapper.go | 15 +--- hscontrol/mapper/mapper_test.go | 44 ++++++---- hscontrol/mapper/tail.go | 28 ++----- hscontrol/mapper/tail_test.go | 39 ++++++--- hscontrol/notifier/notifier.go | 21 ++--- hscontrol/oidc.go | 56 +++++-------- hscontrol/policy/acls_test.go | 38 +++++---- hscontrol/poll.go | 20 ++--- hscontrol/types/node.go | 140 ++++++++++++++++++++----------- integration/cli_test.go | 50 +++++------ 22 files changed, 550 insertions(+), 471 deletions(-) diff --git a/cmd/headscale/cli/debug.go b/cmd/headscale/cli/debug.go index 8250910..c77aeef 100644 --- a/cmd/headscale/cli/debug.go +++ b/cmd/headscale/cli/debug.go @@ -4,10 +4,10 @@ import ( "fmt" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" - "github.com/juanfont/headscale/hscontrol/util" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "google.golang.org/grpc/status" + "tailscale.com/types/key" ) const ( @@ -93,11 +93,13 @@ var createNodeCmd = &cobra.Command{ return } - if !util.NodePublicKeyRegex.Match([]byte(machineKey)) { - err = errPreAuthKeyMalformed + + var mkey key.MachinePublic + err = mkey.UnmarshalText([]byte(machineKey)) + if err != nil { ErrorOutput( err, - fmt.Sprintf("Error: %s", err), + fmt.Sprintf("Failed to parse machine key from flag: %s", err), output, ) diff --git a/flake.nix b/flake.nix index 65f9d0a..b9f2399 100644 --- a/flake.nix +++ b/flake.nix @@ -33,7 +33,7 @@ # 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. - vendorSha256 = "sha256-Q6eySc8lXYhkWka7Y+qOM6viv7QhdjFZDX8PttaLfr4="; + vendorSha256 = "sha256-SYb2LCCZT/p1UHwB1b0IHPfk6Sxh3UYkR4r+KjkL4F8="; ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"]; }; diff --git a/go.mod b/go.mod index 590947b..1e2c850 100644 --- a/go.mod +++ b/go.mod @@ -16,14 +16,14 @@ require ( github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 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/ory/dockertest/v3 v3.9.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/philip-bui/grpc-zerolog v1.0.1 github.com/pkg/profile v1.7.0 - github.com/prometheus/client_golang v1.15.1 - github.com/prometheus/common v0.42.0 + github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/common v0.44.0 github.com/pterm/pterm v0.12.58 github.com/puzpuzpuz/xsync/v2 v2.4.0 github.com/rs/zerolog v1.29.0 @@ -33,19 +33,19 @@ require ( github.com/stretchr/testify v1.8.4 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e - go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 - golang.org/x/crypto v0.12.0 - golang.org/x/net v0.14.0 - golang.org/x/oauth2 v0.7.0 - golang.org/x/sync v0.2.0 + go4.org/netipx v0.0.0-20230824141953-6213f710f925 + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 + golang.org/x/oauth2 v0.12.0 + golang.org/x/sync v0.3.0 google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 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/yaml.v3 v3.0.1 gorm.io/driver/postgres v1.4.8 gorm.io/gorm v1.24.6 - tailscale.com v1.50.0 + tailscale.com v1.54.0 ) require ( @@ -56,22 +56,22 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // 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/cenkalti/backoff/v4 v4.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/containerd/continuity v0.3.0 // indirect - github.com/coreos/go-iptables v0.6.0 // indirect - github.com/dblohm7/wingoes v0.0.0-20230821191801-fc76608aecf0 // indirect - github.com/docker/cli v23.0.5+incompatible // indirect - github.com/docker/docker v24.0.4+incompatible // indirect + github.com/coreos/go-iptables v0.7.0 // indirect + github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077 // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/fgprof v0.9.3 // 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/go-jose/go-jose/v3 v3.0.0 // 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/pprof v0.0.0-20221118152302-e6195bd50e26 // 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/hashicorp/go-version v1.6.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/now v1.1.5 // 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/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -104,33 +104,34 @@ require ( github.com/lithammer/fuzzysearch v1.1.5 // indirect github.com/magiconair/properties v1.8.7 // 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/matttproud/golang_protobuf_extensions v1.0.4 // 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/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/mapstructure v1.5.0 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // 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/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // 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/vishvananda/netlink v1.2.1-beta.2 // indirect github.com/vishvananda/netns v0.0.4 // indirect @@ -140,15 +141,15 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect - golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.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 - 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/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 7eaf620..ed9d3d7 100644 --- a/go.sum +++ b/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/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= 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-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +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/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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/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.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ= -github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE= +github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= +github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= 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-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/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= 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.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= +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/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM= 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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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-20230821191801-fc76608aecf0/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs= +github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077 h1:WphxHslVftszsr0oZOHPaOjpmN/BsgNYF+gW/hxZXXc= +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/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= -github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v24.0.4+incompatible h1:s/LVDftw9hjblvqIeTiGYXBCD95nOEEl7qRsRrIOuQI= -github.com/docker/docker v24.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +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/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 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.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +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/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 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/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 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.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +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.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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/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/jsimonetti/rtnetlink v1.3.2 h1:dcn0uWkfxycEEyNy0IGfx3GrhQ38LH7odjxAghimsVI= -github.com/jsimonetti/rtnetlink v1.3.2/go.mod h1:BBu4jZCpTjP6Gk0/wfrO8qcqymnN3g0hoFqObRmUo6U= +github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA= +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.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 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/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.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +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.10/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.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.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +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.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 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/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= 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.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= +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-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 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.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +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/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= 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/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/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= -github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +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/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +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.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +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.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= 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.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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +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/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= 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/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.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +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/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 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/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 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/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= 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= 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/netipx v0.0.0-20230728180743-ad4cb58a6516 h1:X66ZEoMN2SuaoI/dfZVYobB6E5zjZyyHUMWlCA7MgGE= -go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y= +go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ= +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-20190510104115-cbcb75029529/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-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-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-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= -golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +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-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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.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.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +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-20180826012351-8a410e7b638d/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.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.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +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-20190226205417-e64efc72b421/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-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.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +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-20181108010431-42b317875d0f/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-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.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +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-20190215142949-d0b11bdaac8a/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.5.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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.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.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +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.3.0/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.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.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +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-20190308202827-9d24e82272b4/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.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.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +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-20191011141410-1b5146add898/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.5/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.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-20190307195333-5fe7a883aa19/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.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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +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 v1.0.0-20180628173108-788fd7840127/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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 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.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= -tailscale.com v1.50.0 h1:98infw8rznkdntRgcnlrAC5JuZfDH0bqKLyg8ZKfwMk= -tailscale.com v1.50.0/go.mod h1:lBw7+Mw2d7rea3kefGjYWN8IJkB5dyaakMNMOinNGDo= +software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8= +software.sslmate.com/src/go-pkcs12 v0.2.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= +tailscale.com v1.54.0 h1:Dri5BTKkHYpl+/t8ofY+tyvoTDbH/FpP7iB4B0cAQOY= +tailscale.com v1.54.0/go.mod h1:MnLFoCRwzFWr3qtkSW2nZdQpK7wQRZEk1KtcEGAuZYw= diff --git a/hscontrol/app.go b/hscontrol/app.go index bb67ffc..95d244d 100644 --- a/hscontrol/app.go +++ b/hscontrol/app.go @@ -449,10 +449,10 @@ func (h *Headscale) createRouter(grpcMux *grpcRuntime.ServeMux) *mux.Router { router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet) router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet) - router.HandleFunc("/register/{nkey}", h.RegisterWebAPI).Methods(http.MethodGet) + router.HandleFunc("/register/{mkey}", h.RegisterWebAPI).Methods(http.MethodGet) 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("/apple", h.AppleConfigMessage).Methods(http.MethodGet) router.HandleFunc("/apple/{platform}", h.ApplePlatformConfig). diff --git a/hscontrol/auth.go b/hscontrol/auth.go index 22dc869..579f9a8 100644 --- a/hscontrol/auth.go +++ b/hscontrol/auth.go @@ -45,7 +45,7 @@ func (h *Headscale) handleRegister( // is that the client will hammer headscale with requests until it gets a // successful RegisterResponse. if registerRequest.Followup != "" { - if _, ok := h.registrationCache.Get(registerRequest.NodeKey.String()); ok { + if _, ok := h.registrationCache.Get(machineKey.String()); ok { log.Debug(). Caller(). Str("node", registerRequest.Hostinfo.Hostname). @@ -78,7 +78,7 @@ func (h *Headscale) handleRegister( Msg("New node not yet in the database") givenName, err := h.db.GenerateGivenName( - machineKey.String(), + machineKey, registerRequest.Hostinfo.Hostname, ) if err != nil { @@ -97,10 +97,10 @@ func (h *Headscale) handleRegister( // We create the node and then keep it around until a callback // happens newNode := types.Node{ - MachineKey: machineKey.String(), + MachineKey: machineKey, Hostname: registerRequest.Hostinfo.Hostname, GivenName: givenName, - NodeKey: registerRequest.NodeKey.String(), + NodeKey: registerRequest.NodeKey, LastSeen: &now, Expiry: &time.Time{}, } @@ -116,7 +116,7 @@ func (h *Headscale) handleRegister( } h.registrationCache.Set( - newNode.NodeKey, + machineKey.String(), newNode, 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, // 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. - var storedMachineKey key.MachinePublic - err = storedMachineKey.UnmarshalText( - []byte(node.MachineKey), - ) - if err != nil || storedMachineKey.IsZero() { + if err != nil || node.MachineKey.IsZero() { if err := h.db.NodeSetMachineKey(node, machineKey); err != nil { log.Error(). Caller(). @@ -156,7 +152,7 @@ func (h *Headscale) handleRegister( // - Trying to log out (sending a expiry in the past) // - A valid, registered node, looking for /map // - 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) // https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648 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 - if node.NodeKey == registerRequest.OldNodeKey.String() && + if node.NodeKey.String() == registerRequest.OldNodeKey.String() && !node.IsExpired() { h.handleNodeKeyRefresh( writer, @@ -207,9 +203,9 @@ func (h *Headscale) handleRegister( // we need to make sure the NodeKey matches the one in the request // TODO(juan): What happens when using fast user switching between two // headscale-managed tailnets? - node.NodeKey = registerRequest.NodeKey.String() + node.NodeKey = registerRequest.NodeKey h.registrationCache.Set( - registerRequest.NodeKey.String(), + machineKey.String(), *node, registerCacheExpiration, ) @@ -294,7 +290,7 @@ func (h *Headscale) handleAuthKey( Str("node", registerRequest.Hostinfo.Hostname). Msg("Authentication key was valid, proceeding to acquire IP addresses") - nodeKey := registerRequest.NodeKey.String() + nodeKey := registerRequest.NodeKey // retrieve node information if it exist // The error is not important, because if it does not @@ -342,7 +338,7 @@ func (h *Headscale) handleAuthKey( } else { 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 { log.Error(). Caller(). @@ -359,7 +355,7 @@ func (h *Headscale) handleAuthKey( Hostname: registerRequest.Hostinfo.Hostname, GivenName: givenName, UserID: pak.User.ID, - MachineKey: machineKey.String(), + MachineKey: machineKey, RegisterMethod: util.RegisterMethodAuthKey, Expiry: ®isterRequest.Expiry, NodeKey: nodeKey, @@ -460,12 +456,12 @@ func (h *Headscale) handleNewNode( resp.AuthURL = fmt.Sprintf( "%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), - registerRequest.NodeKey, + machineKey.String(), ) } else { resp.AuthURL = fmt.Sprintf("%s/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), - registerRequest.NodeKey) + machineKey.String()) } respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) @@ -715,11 +711,11 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut( if h.oauth2Config != nil { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), - registerRequest.NodeKey) + machineKey.String()) } else { resp.AuthURL = fmt.Sprintf("%s/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), - registerRequest.NodeKey) + machineKey.String()) } respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey) diff --git a/hscontrol/db/db.go b/hscontrol/db/db.go index 0deeb41..e477448 100644 --- a/hscontrol/db/db.go +++ b/hscontrol/db/db.go @@ -2,6 +2,7 @@ package db import ( "context" + "database/sql" "errors" "fmt" "net/netip" @@ -99,7 +100,7 @@ func NewHeadscaleDatabase( // node was registered. _ = 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 // remove the column. if dbConn.Migrator().HasColumn(&types.Node{}, "registered") { @@ -114,13 +115,13 @@ func NewHeadscaleDatabase( for _, node := range nodes { log.Info(). Str("node", node.Hostname). - Str("machine_key", node.MachineKey). + Str("machine_key", node.MachineKey.ShortString()). Msg("Deleting unregistered node") if err := dbConn.Delete(&types.Node{}, node.ID).Error; err != nil { log.Error(). Err(err). Str("node", node.Hostname). - Str("machine_key", node.MachineKey). + Str("machine_key", node.MachineKey.ShortString()). Msg("Error deleting unregistered node") } } @@ -136,6 +137,50 @@ func NewHeadscaleDatabase( 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") { 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") { nodes := types.Nodes{} if err := dbConn.Find(&nodes).Error; err != nil { @@ -253,27 +293,6 @@ func NewHeadscaleDatabase( 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? err = db.setValue("db_version", dbVersion) diff --git a/hscontrol/db/node.go b/hscontrol/db/node.go index 05d1cd3..bc122ee 100644 --- a/hscontrol/db/node.go +++ b/hscontrol/db/node.go @@ -55,7 +55,7 @@ func (hsdb *HSDatabase) listPeers(node *types.Node) (types.Nodes, error) { Preload("User"). Preload("Routes"). Where("node_key <> ?", - node.NodeKey).Find(&nodes).Error; err != nil { + node.NodeKey.String()).Find(&nodes).Error; err != nil { return types.Nodes{}, err } @@ -268,7 +268,7 @@ func (hsdb *HSDatabase) SetTags( hsdb.notifier.NotifyWithIgnore(types.StateUpdate{ Type: types.StatePeerChanged, Changed: types.Nodes{node}, - }, node.MachineKey) + }, node.MachineKey.String()) return nil } @@ -304,7 +304,7 @@ func (hsdb *HSDatabase) RenameNode(node *types.Node, newName string) error { hsdb.notifier.NotifyWithIgnore(types.StateUpdate{ Type: types.StatePeerChanged, Changed: types.Nodes{node}, - }, node.MachineKey) + }, node.MachineKey.String()) return nil } @@ -330,7 +330,7 @@ func (hsdb *HSDatabase) nodeSetExpiry(node *types.Node, expiry time.Time) error hsdb.notifier.NotifyWithIgnore(types.StateUpdate{ Type: types.StatePeerChanged, Changed: types.Nodes{node}, - }, node.MachineKey) + }, node.MachineKey.String()) return nil } @@ -376,7 +376,7 @@ func (hsdb *HSDatabase) UpdateLastSeen(node *types.Node) error { func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( cache *cache.Cache, - nodeKeyStr string, + mkey key.MachinePublic, userName string, nodeExpiry *time.Time, registrationMethod string, @@ -384,20 +384,14 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( hsdb.mu.Lock() defer hsdb.mu.Unlock() - nodeKey := key.NodePublic{} - err := nodeKey.UnmarshalText([]byte(nodeKeyStr)) - if err != nil { - return nil, err - } - log.Debug(). - Str("nodeKey", nodeKey.ShortString()). + Str("machine_key", mkey.ShortString()). Str("userName", userName). Str("registrationMethod", registrationMethod). Str("expiresAt", fmt.Sprintf("%v", nodeExpiry)). 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 { user, err := hsdb.getUser(userName) if err != nil { @@ -425,7 +419,7 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( ) if err == nil { - cache.Delete(nodeKeyStr) + cache.Delete(mkey.String()) } 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) { log.Debug(). Str("node", node.Hostname). - Str("machine_key", node.MachineKey). - Str("node_key", node.NodeKey). + Str("machine_key", node.MachineKey.ShortString()). + Str("node_key", node.NodeKey.ShortString()). Str("user", node.User.Name). Msg("Registering node") @@ -464,8 +458,8 @@ func (hsdb *HSDatabase) registerNode(node types.Node) (*types.Node, error) { log.Trace(). Caller(). Str("node", node.Hostname). - Str("machine_key", node.MachineKey). - Str("node_key", node.NodeKey). + Str("machine_key", node.MachineKey.ShortString()). + Str("node_key", node.NodeKey.ShortString()). Str("user", node.User.Name). Msg("Node authorized again") @@ -507,7 +501,7 @@ func (hsdb *HSDatabase) NodeSetNodeKey(node *types.Node, nodeKey key.NodePublic) defer hsdb.mu.Unlock() if err := hsdb.db.Model(node).Updates(types.Node{ - NodeKey: nodeKey.String(), + NodeKey: nodeKey, }).Error; err != nil { return err } @@ -524,7 +518,7 @@ func (hsdb *HSDatabase) NodeSetMachineKey( defer hsdb.mu.Unlock() if err := hsdb.db.Model(node).Updates(types.Node{ - MachineKey: machineKey.String(), + MachineKey: machineKey, }).Error; err != nil { return err } @@ -703,7 +697,7 @@ func (hsdb *HSDatabase) enableRoutes(node *types.Node, routeStrs ...string) erro hsdb.notifier.NotifyWithIgnore(types.StateUpdate{ Type: types.StatePeerChanged, Changed: types.Nodes{node}, - }, node.MachineKey) + }, node.MachineKey.String()) return nil } @@ -734,7 +728,7 @@ func generateGivenName(suppliedName string, randomSuffix bool) (string, error) { 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() defer hsdb.mu.RUnlock() @@ -749,17 +743,22 @@ func (hsdb *HSDatabase) GenerateGivenName(machineKey string, suppliedName string return "", err } - for _, node := range nodes { - if node.MachineKey != machineKey && node.GivenName == givenName { - postfixedName, err := generateGivenName(suppliedName, true) - if err != nil { - return "", err - } - - givenName = postfixedName + var nodeFound *types.Node + 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) + if err != nil { + return "", err + } + + givenName = postfixedName + } + return givenName, nil } diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 07b6193..d63611b 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -25,11 +25,13 @@ func (s *Suite) TestGetNode(c *check.C) { _, err = db.GetNode("test", "testnode") c.Assert(err, check.NotNil) + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := &types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: "bar", - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -51,11 +53,13 @@ func (s *Suite) TestGetNodeByID(c *check.C) { _, err = db.GetNodeByID(0) c.Assert(err, check.NotNil) + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: "bar", - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -82,9 +86,8 @@ func (s *Suite) TestGetNodeByNodeKey(c *check.C) { node := types.Node{ ID: 0, - MachineKey: machineKey.Public().String(), - NodeKey: nodeKey.Public().String(), - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -113,9 +116,8 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) { node := types.Node{ ID: 0, - MachineKey: machineKey.Public().String(), - NodeKey: nodeKey.Public().String(), - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -130,11 +132,14 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) { func (s *Suite) TestHardDeleteNode(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) + + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: "bar", - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode3", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -160,11 +165,13 @@ func (s *Suite) TestListPeers(c *check.C) { c.Assert(err, check.NotNil) for index := 0; index <= 10; index++ { + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := types.Node{ ID: uint64(index), - MachineKey: "foo" + strconv.Itoa(index), - NodeKey: "bar" + strconv.Itoa(index), - DiscoKey: "faa" + strconv.Itoa(index), + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -205,11 +212,13 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { c.Assert(err, check.NotNil) for index := 0; index <= 10; index++ { + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := types.Node{ ID: uint64(index), - MachineKey: "foo" + strconv.Itoa(index), - NodeKey: "bar" + strconv.Itoa(index), - DiscoKey: "faa" + strconv.Itoa(index), + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), IPAddresses: types.NodeAddresses{ 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") c.Assert(err, check.NotNil) + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := &types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: "bar", - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -345,11 +356,15 @@ func (s *Suite) TestGenerateGivenName(c *check.C) { _, err = db.GetNode("user-1", "testnode") c.Assert(err, check.NotNil) + nodeKey := key.NewNode() + machineKey := key.NewMachine() + + machineKey2 := key.NewMachine() + node := &types.Node{ ID: 0, - MachineKey: "node-key-1", - NodeKey: "node-key-1", - DiscoKey: "disco-key-1", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "hostname-1", GivenName: "hostname-1", UserID: user1.ID, @@ -358,25 +373,20 @@ func (s *Suite) TestGenerateGivenName(c *check.C) { } 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") c.Assert(err, check.IsNil, 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") c.Assert(err, check.IsNil, 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") c.Assert(err, check.IsNil, 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) { @@ -389,11 +399,13 @@ func (s *Suite) TestSetTags(c *check.C) { _, err = db.GetNode("test", "testnode") c.Assert(err, check.NotNil) + nodeKey := key.NewNode() + machineKey := key.NewMachine() + node := &types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: "bar", - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "testnode", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, @@ -565,6 +577,7 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) { c.Assert(err, check.IsNil) nodeKey := key.NewNode() + machineKey := key.NewMachine() defaultRouteV4 := netip.MustParsePrefix("0.0.0.0/0") defaultRouteV6 := netip.MustParsePrefix("::/0") @@ -574,9 +587,8 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) { node := types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: nodeKey.Public().String(), - DiscoKey: "faa", + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), Hostname: "test", UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, diff --git a/hscontrol/db/suite_test.go b/hscontrol/db/suite_test.go index ff1a095..1c38491 100644 --- a/hscontrol/db/suite_test.go +++ b/hscontrol/db/suite_test.go @@ -1,6 +1,7 @@ package db import ( + "log" "net/netip" "os" "testing" @@ -27,19 +28,22 @@ func (s *Suite) SetUpTest(c *check.C) { } func (s *Suite) TearDownTest(c *check.C) { - os.RemoveAll(tmpDir) + // os.RemoveAll(tmpDir) } func (s *Suite) ResetDB(c *check.C) { - if len(tmpDir) != 0 { - os.RemoveAll(tmpDir) - } + // if len(tmpDir) != 0 { + // os.RemoveAll(tmpDir) + // } + var err error - tmpDir, err = os.MkdirTemp("", "autoygg-client-test") + tmpDir, err = os.MkdirTemp("", "headscale-db-test-*") if err != nil { c.Fatal(err) } + log.Printf("database path: %s", tmpDir+"/headscale_test.db") + db, err = NewHeadscaleDatabase( "sqlite3", tmpDir+"/headscale_test.db", diff --git a/hscontrol/grpcv1.go b/hscontrol/grpcv1.go index 926e3e8..5c05146 100644 --- a/hscontrol/grpcv1.go +++ b/hscontrol/grpcv1.go @@ -172,12 +172,18 @@ func (api headscaleV1APIServer) RegisterNode( ) (*v1.RegisterNodeResponse, error) { log.Trace(). Str("user", request.GetUser()). - Str("node_key", request.GetKey()). + Str("machine_key", request.GetKey()). 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( api.h.registrationCache, - request.GetKey(), + mkey, request.GetUser(), nil, util.RegisterMethodCLI, @@ -521,13 +527,22 @@ func (api headscaleV1APIServer) DebugCreateNode( 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 { return nil, err } + givenName, err := api.h.db.GenerateGivenName(mkey, request.GetName()) + if err != nil { + return nil, err + } + + nodeKey := key.NewNode() + newNode := types.Node{ - MachineKey: request.GetKey(), + MachineKey: mkey, + NodeKey: nodeKey.Public(), Hostname: request.GetName(), GivenName: givenName, User: *user, @@ -538,14 +553,12 @@ func (api headscaleV1APIServer) DebugCreateNode( HostInfo: types.HostInfo(hostinfo), } - nodeKey := key.NodePublic{} - err = nodeKey.UnmarshalText([]byte(request.GetKey())) - if err != nil { - log.Panic().Msg("can not add node for debug. invalid node key") - } + log.Debug(). + Str("machine_key", mkey.ShortString()). + Msg("adding debug machine via CLI, appending to registration cache") api.h.registrationCache.Set( - nodeKey.String(), + mkey.String(), newNode, registerCacheExpiration, ) diff --git a/hscontrol/handlers.go b/hscontrol/handlers.go index 5c0baa7..175b6e2 100644 --- a/hscontrol/handlers.go +++ b/hscontrol/handlers.go @@ -12,7 +12,6 @@ import ( "time" "github.com/gorilla/mux" - "github.com/juanfont/headscale/hscontrol/util" "github.com/rs/zerolog/log" "tailscale.com/tailcfg" "tailscale.com/types/key" @@ -207,33 +206,16 @@ func (h *Headscale) RegisterWebAPI( req *http.Request, ) { vars := mux.Vars(req) - nodeKeyStr, ok := vars["nkey"] - - 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 - } + machineKeyStr := vars["mkey"] // 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 // the template and log an error. - var nodeKey key.NodePublic - err := nodeKey.UnmarshalText( - []byte(nodeKeyStr), + var machineKey key.MachinePublic + err := machineKey.UnmarshalText( + []byte(machineKeyStr), ) - - if !ok || nodeKeyStr == "" || err != nil { + if err != nil { log.Warn().Err(err).Msg("Failed to parse incoming nodekey") writer.Header().Set("Content-Type", "text/plain; charset=utf-8") @@ -251,7 +233,7 @@ func (h *Headscale) RegisterWebAPI( var content bytes.Buffer if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{ - Key: nodeKeyStr, + Key: machineKey.String(), }); err != nil { log.Error(). Str("func", "RegisterWebAPI"). diff --git a/hscontrol/mapper/mapper.go b/hscontrol/mapper/mapper.go index 7f9e17c..806e901 100644 --- a/hscontrol/mapper/mapper.go +++ b/hscontrol/mapper/mapper.go @@ -368,17 +368,6 @@ func (m *Mapper) marshalMapResponse( ) ([]byte, error) { 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) if err != nil { log.Error(). @@ -426,11 +415,11 @@ func (m *Mapper) marshalMapResponse( if compression == util.ZstdCompression { respBody = zstdEncode(jsonBody) if !m.isNoise { // if legacy protocol - respBody = m.privateKey2019.SealTo(machineKey, respBody) + respBody = m.privateKey2019.SealTo(node.MachineKey, respBody) } } else { if !m.isNoise { // if legacy protocol - respBody = m.privateKey2019.SealTo(machineKey, jsonBody) + respBody = m.privateKey2019.SealTo(node.MachineKey, jsonBody) } else { respBody = jsonBody } diff --git a/hscontrol/mapper/mapper_test.go b/hscontrol/mapper/mapper_test.go index 8270fc3..fd314ae 100644 --- a/hscontrol/mapper/mapper_test.go +++ b/hscontrol/mapper/mapper_test.go @@ -166,10 +166,16 @@ func Test_fullMapResponse(t *testing.T) { expire := time.Date(2500, time.November, 11, 23, 0, 0, 0, time.UTC) mini := &types.Node{ - ID: 0, - MachineKey: "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - NodeKey: "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - DiscoKey: "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ID: 0, + MachineKey: mustMK( + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + ), + NodeKey: mustNK( + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + ), + DiscoKey: mustDK( + "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ), IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")}, Hostname: "mini", GivenName: "mini", @@ -226,7 +232,6 @@ func Test_fullMapResponse(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("192.168.0.0/24"), }, - Endpoints: []string{}, DERP: "127.3.3.40:0", Hostinfo: hiview(tailcfg.Hostinfo{}), Created: created, @@ -244,10 +249,16 @@ func Test_fullMapResponse(t *testing.T) { } peer1 := &types.Node{ - ID: 1, - MachineKey: "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - NodeKey: "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - DiscoKey: "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ID: 1, + MachineKey: mustMK( + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + ), + NodeKey: mustNK( + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + ), + DiscoKey: mustDK( + "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ), IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")}, Hostname: "peer1", GivenName: "peer1", @@ -278,7 +289,6 @@ func Test_fullMapResponse(t *testing.T) { ), Addresses: []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", Hostinfo: hiview(tailcfg.Hostinfo{}), Created: created, @@ -296,10 +306,16 @@ func Test_fullMapResponse(t *testing.T) { } peer2 := &types.Node{ - ID: 2, - MachineKey: "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - NodeKey: "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - DiscoKey: "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ID: 2, + MachineKey: mustMK( + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + ), + NodeKey: mustNK( + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + ), + DiscoKey: mustDK( + "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ), IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")}, Hostname: "peer2", GivenName: "peer2", diff --git a/hscontrol/mapper/tail.go b/hscontrol/mapper/tail.go index 13958c9..c5cee63 100644 --- a/hscontrol/mapper/tail.go +++ b/hscontrol/mapper/tail.go @@ -52,21 +52,6 @@ func tailNode( baseDomain string, randomClientPort bool, ) (*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() allowedIPs := append( @@ -112,6 +97,11 @@ func tailNode( tags, _ := pol.TagsOfNode(node) tags = lo.Uniq(append(tags, node.ForcedTags...)) + endpoints, err := node.EndpointsToAddrPort() + if err != nil { + return nil, err + } + tNode := tailcfg.Node{ ID: tailcfg.NodeID(node.ID), // this is the actual ID StableID: tailcfg.StableNodeID( @@ -121,14 +111,14 @@ func tailNode( User: tailcfg.UserID(node.UserID), - Key: nodeKey, + Key: node.NodeKey, KeyExpiry: keyExpiry, - Machine: machineKey, - DiscoKey: discoKey, + Machine: node.MachineKey, + DiscoKey: node.DiscoKey, Addresses: addrs, AllowedIPs: allowedIPs, - Endpoints: node.Endpoints, + Endpoints: endpoints, DERP: derp, Hostinfo: hostInfo.View(), Created: node.CreatedAt, diff --git a/hscontrol/mapper/tail_test.go b/hscontrol/mapper/tail_test.go index ced7537..734c27f 100644 --- a/hscontrol/mapper/tail_test.go +++ b/hscontrol/mapper/tail_test.go @@ -58,16 +58,36 @@ func TestTailNode(t *testing.T) { pol: &policy.ACLPolicy{}, dnsConfig: &tailcfg.DNSConfig{}, baseDomain: "", - want: nil, - wantErr: true, + want: &tailcfg.Node{ + 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", node: &types.Node{ - ID: 0, - MachineKey: "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - NodeKey: "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - DiscoKey: "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ID: 0, + MachineKey: mustMK( + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + ), + NodeKey: mustNK( + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + ), + DiscoKey: mustDK( + "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + ), IPAddresses: []netip.Addr{ netip.MustParseAddr("100.64.0.1"), }, @@ -133,10 +153,9 @@ func TestTailNode(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("192.168.0.0/24"), }, - Endpoints: []string{}, - DERP: "127.3.3.40:0", - Hostinfo: hiview(tailcfg.Hostinfo{}), - Created: created, + DERP: "127.3.3.40:0", + Hostinfo: hiview(tailcfg.Hostinfo{}), + Created: created, Tags: []string{}, diff --git a/hscontrol/notifier/notifier.go b/hscontrol/notifier/notifier.go index 32f426a..fee0bef 100644 --- a/hscontrol/notifier/notifier.go +++ b/hscontrol/notifier/notifier.go @@ -6,6 +6,7 @@ import ( "github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/util" "github.com/rs/zerolog/log" + "tailscale.com/types/key" ) type Notifier struct { @@ -17,9 +18,9 @@ func NewNotifier() *Notifier { return &Notifier{} } -func (n *Notifier) AddNode(machineKey string, c chan<- types.StateUpdate) { - log.Trace().Caller().Str("key", machineKey).Msg("acquiring lock to add node") - defer log.Trace().Caller().Str("key", machineKey).Msg("releasing lock to add node") +func (n *Notifier) AddNode(machineKey key.MachinePublic, c chan<- types.StateUpdate) { + log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("acquiring lock to add node") + defer log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("releasing lock to add node") n.l.Lock() 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[machineKey] = c + n.nodes[machineKey.String()] = c log.Trace(). - Str("machine_key", machineKey). + Str("machine_key", machineKey.ShortString()). Int("open_chans", len(n.nodes)). Msg("Added new channel") } -func (n *Notifier) RemoveNode(machineKey string) { - log.Trace().Caller().Str("key", machineKey).Msg("acquiring lock to remove node") - defer log.Trace().Caller().Str("key", machineKey).Msg("releasing lock to remove node") +func (n *Notifier) RemoveNode(machineKey key.MachinePublic) { + log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("acquiring lock to remove node") + defer log.Trace().Caller().Str("key", machineKey.ShortString()).Msg("releasing lock to remove node") n.l.Lock() defer n.l.Unlock() @@ -47,10 +48,10 @@ func (n *Notifier) RemoveNode(machineKey string) { return } - delete(n.nodes, machineKey) + delete(n.nodes, machineKey.String()) log.Trace(). - Str("machine_key", machineKey). + Str("machine_key", machineKey.ShortString()). Int("open_chans", len(n.nodes)). Msg("Removed channel") } diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index a5ddd97..568519f 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -90,42 +90,28 @@ func (h *Headscale) determineTokenExpiration(idTokenExpiration time.Time) time.T // RegisterOIDC redirects to the OIDC provider for authentication // 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( writer http.ResponseWriter, req *http.Request, ) { vars := mux.Vars(req) - nodeKeyStr, ok := vars["nkey"] + machineKeyStr, ok := vars["mkey"] log.Debug(). Caller(). - Str("node_key", nodeKeyStr). + Str("machine_key", machineKeyStr). Bool("ok", ok). 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 // is passed as a key is not parsable/validated as a NodePublic key, then fail to render // the template and log an error. - var nodeKey key.NodePublic - err := nodeKey.UnmarshalText( - []byte(nodeKeyStr), + var machineKey key.MachinePublic + err := machineKey.UnmarshalText( + []byte(machineKeyStr), ) - - if !ok || nodeKeyStr == "" || err != nil { + if err != nil { log.Warn(). Err(err). 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 h.registrationCache.Set( stateStr, - nodeKey, + machineKey, registerCacheExpiration, ) @@ -232,7 +218,7 @@ func (h *Headscale) OIDCCallback( return } - nodeKey, nodeExists, err := h.validateNodeForOIDCCallback( + machineKey, nodeExists, err := h.validateNodeForOIDCCallback( writer, state, claims, @@ -255,7 +241,7 @@ func (h *Headscale) OIDCCallback( return } - if err := h.registerNodeForOIDCCallback(writer, user, nodeKey, idTokenExpiry); err != nil { + if err := h.registerNodeForOIDCCallback(writer, user, machineKey, idTokenExpiry); err != nil { return } @@ -462,10 +448,10 @@ func (h *Headscale) validateNodeForOIDCCallback( state string, claims *IDTokenClaims, expiry time.Time, -) (*key.NodePublic, bool, error) { +) (*key.MachinePublic, bool, error) { // retrieve nodekey from state cache - nodeKeyIf, nodeKeyFound := h.registrationCache.Get(state) - if !nodeKeyFound { + machineKeyIf, machineKeyFound := h.registrationCache.Get(state) + if !machineKeyFound { log.Trace(). Msg("requested node state key expired before authorisation completed") writer.Header().Set("Content-Type", "text/plain; charset=utf-8") @@ -478,11 +464,11 @@ func (h *Headscale) validateNodeForOIDCCallback( return nil, false, errOIDCNodeKeyMissing } - var nodeKey key.NodePublic - nodeKey, nodeKeyOK := nodeKeyIf.(key.NodePublic) - if !nodeKeyOK { + var machineKey key.MachinePublic + machineKey, machineKeyOK := machineKeyIf.(key.MachinePublic) + if !machineKeyOK { log.Trace(). - Interface("got", nodeKeyIf). + Interface("got", machineKeyIf). Msg("requested node state key is not a nodekey") writer.Header().Set("Content-Type", "text/plain; charset=utf-8") writer.WriteHeader(http.StatusBadRequest) @@ -498,7 +484,7 @@ func (h *Headscale) validateNodeForOIDCCallback( // The error is not important, because if it does not // exist, then this is a new node and we will move // on to registration. - node, _ := h.db.GetNodeByNodeKey(nodeKey) + node, _ := h.db.GetNodeByMachineKey(machineKey) if node != nil { log.Trace(). @@ -553,7 +539,7 @@ func (h *Headscale) validateNodeForOIDCCallback( return nil, true, nil } - return &nodeKey, false, nil + return &machineKey, false, nil } func getUserName( @@ -624,13 +610,13 @@ func (h *Headscale) findOrCreateNewUserForOIDCCallback( func (h *Headscale) registerNodeForOIDCCallback( writer http.ResponseWriter, user *types.User, - nodeKey *key.NodePublic, + machineKey *key.MachinePublic, expiry time.Time, ) error { if _, err := h.db.RegisterNodeFromAuthCallback( // TODO(kradalby): find a better way to use the cache across modules h.registrationCache, - nodeKey.String(), + *machineKey, user.Name, &expiry, util.RegisterMethodOIDC, diff --git a/hscontrol/policy/acls_test.go b/hscontrol/policy/acls_test.go index b2d694b..661c9cf 100644 --- a/hscontrol/policy/acls_test.go +++ b/hscontrol/policy/acls_test.go @@ -14,12 +14,29 @@ import ( "go4.org/netipx" "gopkg.in/check.v1" "tailscale.com/tailcfg" + "tailscale.com/types/key" ) var ipComparer = cmp.Comparer(func(x, y netip.Addr) bool { 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) { check.TestingT(t) } @@ -951,7 +968,7 @@ func Test_listNodesInUser(t *testing.T) { t.Run(test.name, func(t *testing.T) { 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) } }) @@ -1704,7 +1721,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { test.args.nodes, 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) } }) @@ -2723,7 +2740,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { tt.args.nodes, 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) } }) @@ -2986,9 +3003,6 @@ func TestValidExpandTagOwnersInSources(t *testing.T) { node := &types.Node{ ID: 0, - MachineKey: "foo", - NodeKey: "bar", - DiscoKey: "faa", Hostname: "testnodes", IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")}, UserID: 0, @@ -3041,9 +3055,6 @@ func TestInvalidTagValidUser(t *testing.T) { node := &types.Node{ ID: 1, - MachineKey: "12345", - NodeKey: "bar", - DiscoKey: "faa", Hostname: "testnodes", IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")}, UserID: 1, @@ -3095,9 +3106,6 @@ func TestValidExpandTagOwnersInDestinations(t *testing.T) { node := &types.Node{ ID: 1, - MachineKey: "12345", - NodeKey: "bar", - DiscoKey: "faa", Hostname: "testnodes", IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")}, UserID: 1, @@ -3159,9 +3167,6 @@ func TestValidTagInvalidUser(t *testing.T) { node := &types.Node{ ID: 1, - MachineKey: "12345", - NodeKey: "bar", - DiscoKey: "faa", Hostname: "webserver", IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.1")}, UserID: 1, @@ -3179,9 +3184,6 @@ func TestValidTagInvalidUser(t *testing.T) { nodes2 := &types.Node{ ID: 2, - MachineKey: "56789", - NodeKey: "bar2", - DiscoKey: "faab", Hostname: "user", IPAddresses: types.NodeAddresses{netip.MustParseAddr("100.64.0.2")}, UserID: 1, diff --git a/hscontrol/poll.go b/hscontrol/poll.go index 91858a9..050e857 100644 --- a/hscontrol/poll.go +++ b/hscontrol/poll.go @@ -34,7 +34,7 @@ func logPollFunc( Bool("readOnly", mapRequest.ReadOnly). Bool("omitPeers", mapRequest.OmitPeers). Bool("stream", mapRequest.Stream). - Str("node_key", node.NodeKey). + Str("node_key", node.NodeKey.ShortString()). Str("node", node.Hostname). Msg(msg) }, @@ -45,7 +45,7 @@ func logPollFunc( Bool("readOnly", mapRequest.ReadOnly). Bool("omitPeers", mapRequest.OmitPeers). Bool("stream", mapRequest.Stream). - Str("node_key", node.NodeKey). + Str("node_key", node.NodeKey.ShortString()). Str("node", node.Hostname). Err(err). Msg(msg) @@ -81,7 +81,7 @@ func (h *Headscale) handlePoll( Bool("readOnly", mapRequest.ReadOnly). Bool("omitPeers", mapRequest.OmitPeers). Bool("stream", mapRequest.Stream). - Str("node_key", node.NodeKey). + Str("node_key", node.NodeKey.ShortString()). Str("node", node.Hostname). Strs("endpoints", node.Endpoints). Msg("Received endpoint update") @@ -90,8 +90,8 @@ func (h *Headscale) handlePoll( node.LastSeen = &now node.Hostname = mapRequest.Hostinfo.Hostname node.HostInfo = types.HostInfo(*mapRequest.Hostinfo) - node.DiscoKey = mapRequest.DiscoKey.String() - node.Endpoints = mapRequest.Endpoints + node.DiscoKey = mapRequest.DiscoKey + node.SetEndpointsFromAddrPorts(mapRequest.Endpoints) if err := h.db.NodeSave(node); err != nil { logErr(err, "Failed to persist/update node in the database") @@ -113,7 +113,7 @@ func (h *Headscale) handlePoll( Type: types.StatePeerChanged, Changed: types.Nodes{node}, }, - node.MachineKey) + node.MachineKey.String()) writer.WriteHeader(http.StatusOK) if f, ok := writer.(http.Flusher); ok { @@ -143,8 +143,8 @@ func (h *Headscale) handlePoll( node.LastSeen = &now node.Hostname = mapRequest.Hostinfo.Hostname node.HostInfo = types.HostInfo(*mapRequest.Hostinfo) - node.DiscoKey = mapRequest.DiscoKey.String() - node.Endpoints = mapRequest.Endpoints + node.DiscoKey = mapRequest.DiscoKey + node.SetEndpointsFromAddrPorts(mapRequest.Endpoints) // When a node connects to control, list the peers it has at // that given point, further updates are kept in memory in @@ -222,7 +222,7 @@ func (h *Headscale) handlePoll( Type: types.StatePeerChanged, Changed: types.Nodes{node}, }, - node.MachineKey) + node.MachineKey.String()) // Set up the client stream h.pollNetMapStreamWG.Add(1) @@ -342,7 +342,7 @@ func (h *Headscale) handlePoll( Bool("readOnly", mapRequest.ReadOnly). Bool("omitPeers", mapRequest.OmitPeers). Bool("stream", mapRequest.Stream). - Str("node_key", node.NodeKey). + Str("node_key", node.NodeKey.ShortString()). Str("node", node.Hostname). TimeDiff("timeSpent", time.Now(), now). Msg("update sent") diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index ae11b71..da20bc4 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -13,6 +13,7 @@ import ( "github.com/juanfont/headscale/hscontrol/policy/matcher" "go4.org/netipx" "google.golang.org/protobuf/types/known/timestamppb" + "gorm.io/gorm" "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -24,10 +25,30 @@ var ( // Node is a Headscale client. type Node struct { - ID uint64 `gorm:"primary_key"` - MachineKey string `gorm:"type:varchar(64);unique_index"` - NodeKey string - DiscoKey string + ID uint64 `gorm:"primary_key"` + + // MachineKeyValue is the string representation of MachineKey + // 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 // Hostname represents the name given by the Tailscale @@ -174,6 +195,31 @@ func (node Node) IsExpired() bool { 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. // This is really a naive implementation, as we don't really see // 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 } +// 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 { nodeProto := &v1.Node{ Id: node.ID, - MachineKey: node.MachineKey, + MachineKey: node.MachineKey.String(), - NodeKey: node.NodeKey, - DiscoKey: node.DiscoKey, + NodeKey: node.NodeKey.String(), + DiscoKey: node.DiscoKey.String(), IpAddresses: node.IPAddresses.StringSlice(), Name: node.Hostname, GivenName: node.GivenName, @@ -289,47 +374,6 @@ func (node *Node) GetFQDN(dnsConfig *tailcfg.DNSConfig, baseDomain string) (stri 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 { return node.Hostname } diff --git a/integration/cli_test.go b/integration/cli_test.go index 61439c3..ed1b3fc 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -683,8 +683,8 @@ func TestNodeTagCommand(t *testing.T) { assertNoErr(t, err) machineKeys := []string{ - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", } nodes := make([]*v1.Node, len(machineKeys)) assert.Nil(t, err) @@ -816,13 +816,13 @@ func TestNodeCommand(t *testing.T) { headscale, err := scenario.Headscale() assertNoErr(t, err) - // Randomly generated node keys + // Pregenerated machine keys machineKeys := []string{ - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", - "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + "mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", } nodes := make([]*v1.Node, len(machineKeys)) assert.Nil(t, err) @@ -898,8 +898,8 @@ func TestNodeCommand(t *testing.T) { assert.Equal(t, "node-5", listAll[4].Name) otherUserMachineKeys := []string{ - "nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", - "nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", + "mkey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", + "mkey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", } otherUserMachines := make([]*v1.Node, len(otherUserMachineKeys)) assert.Nil(t, err) @@ -1056,13 +1056,13 @@ func TestNodeExpireCommand(t *testing.T) { headscale, err := scenario.Headscale() assertNoErr(t, err) - // Randomly generated node keys + // Pregenerated machine keys machineKeys := []string{ - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", - "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + "mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", } nodes := make([]*v1.Node, len(machineKeys)) @@ -1183,13 +1183,13 @@ func TestNodeRenameCommand(t *testing.T) { headscale, err := scenario.Headscale() assertNoErr(t, err) - // Randomly generated node keys + // Pregenerated machine keys machineKeys := []string{ - "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", - "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", - "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + "mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", } nodes := make([]*v1.Node, len(machineKeys)) assert.Nil(t, err) @@ -1210,7 +1210,7 @@ func TestNodeRenameCommand(t *testing.T) { "json", }, ) - assert.Nil(t, err) + assertNoErr(t, err) var node v1.Node err = executeAndUnmarshal( @@ -1228,7 +1228,7 @@ func TestNodeRenameCommand(t *testing.T) { }, &node, ) - assert.Nil(t, err) + assertNoErr(t, err) nodes[index] = &node } @@ -1350,7 +1350,7 @@ func TestNodeMoveCommand(t *testing.T) { assertNoErr(t, err) // Randomly generated node key - machineKey := "nodekey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa" + machineKey := "mkey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa" _, err = headscale.Execute( []string{