From 67d6c8f9465d52401666b27a5f96aff2bf17b5f9 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 09:04:27 +0100 Subject: [PATCH 01/53] Remove oversensitive tracing output --- utils.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/utils.go b/utils.go index 004bf306..af267eb3 100644 --- a/utils.go +++ b/utils.go @@ -196,10 +196,6 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) { var addressesSlices []string h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices) - log.Trace(). - Strs("addresses", addressesSlices). - Msg("Got allocated ip addresses from databases") - var ips netaddr.IPSetBuilder for _, slice := range addressesSlices { var machineAddresses MachineAddresses @@ -216,10 +212,6 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) { } } - log.Trace(). - Interface("addresses", ips). - Msg("Parsed ip addresses that has been allocated from databases") - ipSet, err := ips.IPSet() if err != nil { return &netaddr.IPSet{}, fmt.Errorf( From 8a3a0b64037bea8c15700943d37a16f02375cf28 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 09:04:48 +0100 Subject: [PATCH 02/53] Add YAML support to ACLs --- acls.go | 40 +++++++++++++++++++++++++++++++--------- acls_types.go | 44 +++++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/acls.go b/acls.go index ce14a89a..1a597c24 100644 --- a/acls.go +++ b/acls.go @@ -5,11 +5,13 @@ import ( "fmt" "io" "os" + "path/filepath" "strconv" "strings" "github.com/rs/zerolog/log" "github.com/tailscale/hujson" + "gopkg.in/yaml.v3" "inet.af/netaddr" "tailscale.com/tailcfg" ) @@ -53,16 +55,36 @@ func (h *Headscale) LoadACLPolicy(path string) error { return err } - ast, err := hujson.Parse(policyBytes) - if err != nil { - return err - } - ast.Standardize() - policyBytes = ast.Pack() - err = json.Unmarshal(policyBytes, &policy) - if err != nil { - return err + switch filepath.Ext(path) { + case ".yml", ".yaml": + log.Debug(). + Str("path", path). + Bytes("file", policyBytes). + Msg("Loading ACLs from YAML") + + err := yaml.Unmarshal(policyBytes, &policy) + if err != nil { + return err + } + + log.Trace(). + Interface("policy", policy). + Msg("Loaded policy from YAML") + + default: + ast, err := hujson.Parse(policyBytes) + if err != nil { + return err + } + + ast.Standardize() + policyBytes = ast.Pack() + err = json.Unmarshal(policyBytes, &policy) + if err != nil { + return err + } } + if policy.IsZero() { return errEmptyPolicy } diff --git a/acls_types.go b/acls_types.go index 08e650ff..fb869821 100644 --- a/acls_types.go +++ b/acls_types.go @@ -5,23 +5,24 @@ import ( "strings" "github.com/tailscale/hujson" + "gopkg.in/yaml.v3" "inet.af/netaddr" ) // ACLPolicy represents a Tailscale ACL Policy. type ACLPolicy struct { - Groups Groups `json:"Groups"` - Hosts Hosts `json:"Hosts"` - TagOwners TagOwners `json:"TagOwners"` - ACLs []ACL `json:"ACLs"` - Tests []ACLTest `json:"Tests"` + Groups Groups `json:"Groups" yaml:"Groups"` + Hosts Hosts `json:"Hosts" yaml:"Hosts"` + TagOwners TagOwners `json:"TagOwners" yaml:"TagOwners"` + ACLs []ACL `json:"ACLs" yaml:"ACLs"` + Tests []ACLTest `json:"Tests" yaml:"Tests"` } // ACL is a basic rule for the ACL Policy. type ACL struct { - Action string `json:"Action"` - Users []string `json:"Users"` - Ports []string `json:"Ports"` + Action string `json:"Action" yaml:"Action"` + Users []string `json:"Users" yaml:"Users"` + Ports []string `json:"Ports" yaml:"Ports"` } // Groups references a series of alias in the ACL rules. @@ -35,9 +36,9 @@ type TagOwners map[string][]string // ACLTest is not implemented, but should be use to check if a certain rule is allowed. type ACLTest struct { - User string `json:"User"` - Allow []string `json:"Allow"` - Deny []string `json:"Deny,omitempty"` + User string `json:"User" yaml:"User"` + Allow []string `json:"Allow" yaml:"Allow"` + Deny []string `json:"Deny,omitempty" yaml:"Deny,omitempty"` } // UnmarshalJSON allows to parse the Hosts directly into netaddr objects. @@ -69,6 +70,27 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error { return nil } +// UnmarshalYAML allows to parse the Hosts directly into netaddr objects. +func (hosts *Hosts) UnmarshalYAML(data []byte) error { + newHosts := Hosts{} + hostIPPrefixMap := make(map[string]string) + + err := yaml.Unmarshal(data, &hostIPPrefixMap) + if err != nil { + return err + } + for host, prefixStr := range hostIPPrefixMap { + prefix, err := netaddr.ParseIPPrefix(prefixStr) + if err != nil { + return err + } + newHosts[host] = prefix + } + *hosts = newHosts + + return nil +} + // IsZero is perhaps a bit naive here. func (policy ACLPolicy) IsZero() bool { if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 { From c159eb7541e18f3ed1c926826d5075a7bf54b5cf Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 09:04:59 +0100 Subject: [PATCH 03/53] Add basic test of yaml parsing --- acls_test.go | 16 ++++++++++++++++ tests/acls/acl_policy_basic_wildcards.yaml | 10 ++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/acls/acl_policy_basic_wildcards.yaml diff --git a/acls_test.go b/acls_test.go index 5534257d..9f0432a7 100644 --- a/acls_test.go +++ b/acls_test.go @@ -328,6 +328,22 @@ func (s *Suite) TestPortWildcard(c *check.C) { c.Assert(rules[0].SrcIPs[0], check.Equals, "*") } +func (s *Suite) TestPortWildcardYAML(c *check.C) { + err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.yaml") + c.Assert(err, check.IsNil) + + rules, err := app.generateACLRules() + c.Assert(err, check.IsNil) + c.Assert(rules, check.NotNil) + + c.Assert(rules, check.HasLen, 1) + c.Assert(rules[0].DstPorts, check.HasLen, 1) + c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0)) + c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535)) + c.Assert(rules[0].SrcIPs, check.HasLen, 1) + c.Assert(rules[0].SrcIPs[0], check.Equals, "*") +} + func (s *Suite) TestPortNamespace(c *check.C) { namespace, err := app.CreateNamespace("testnamespace") c.Assert(err, check.IsNil) diff --git a/tests/acls/acl_policy_basic_wildcards.yaml b/tests/acls/acl_policy_basic_wildcards.yaml new file mode 100644 index 00000000..8e7c817f --- /dev/null +++ b/tests/acls/acl_policy_basic_wildcards.yaml @@ -0,0 +1,10 @@ +--- +Hosts: + host-1: 100.100.100.100/32 + subnet-1: 100.100.101.100/24 +ACLs: + - Action: accept + Users: + - "*" + Ports: + - host-1:* From e0b9a317f4c30ad07e275b179b5037d61b8bdc02 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 09:05:08 +0100 Subject: [PATCH 04/53] Add note to config example --- config-example.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config-example.yaml b/config-example.yaml index bac3b0ca..e14d1a15 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -132,7 +132,8 @@ tls_key_path: "" log_level: info # Path to a file containg ACL policies. -# Recommended path: /etc/headscale/acl.hujson +# ACLs can be defined as YAML or HUJSON. +# https://tailscale.com/kb/1018/acls/ acl_policy_path: "" ## DNS From d6f6939c54dc0144fa9cc5e6ddf97e45652780ae Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 09:08:29 +0100 Subject: [PATCH 05/53] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44db566..b2b93ef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357) - To limit access between nodes, use [ACLs](./docs/acls.md). +**Features**: + +- Add support for writing ACL files with YAML [#359](https://github.com/juanfont/headscale/pull/359) + **Changes**: - Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) From c58ce6f60cd0e85750228acc30f24a3dda93b619 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 18:40:10 +0100 Subject: [PATCH 06/53] Generalise the registration method to DRY stuff up --- cli_test.go | 2 ++ machine.go | 75 ++++++++++++++++++++++++++++------------------------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/cli_test.go b/cli_test.go index 71f2bea5..2eedb5b4 100644 --- a/cli_test.go +++ b/cli_test.go @@ -32,6 +32,8 @@ func (s *Suite) TestRegisterMachine(c *check.C) { machineAfterRegistering, err := app.RegisterMachine( machine.MachineKey, namespace.Name, + RegisterMethodCLI, + nil, nil, nil, nil, ) c.Assert(err, check.IsNil) c.Assert(machineAfterRegistering.Registered, check.Equals, true) diff --git a/machine.go b/machine.go index dac44591..3415b408 100644 --- a/machine.go +++ b/machine.go @@ -44,8 +44,10 @@ type Machine struct { Registered bool // temp RegisterMethod string - AuthKeyID uint - AuthKey *PreAuthKey + + // TODO(kradalby): This seems like irrelevant information? + AuthKeyID uint + AuthKey *PreAuthKey LastSeen *time.Time LastSuccessfulUpdate *time.Time @@ -686,6 +688,13 @@ func (machine *Machine) toProto() *v1.Machine { func (h *Headscale) RegisterMachine( machineKeyStr string, namespaceName string, + registrationMethod string, + + // Optionals + expiry *time.Time, + authKey *PreAuthKey, + nodePublicKey *string, + lastSeen *time.Time, ) (*Machine, error) { namespace, err := h.GetNamespace(namespaceName) if err != nil { @@ -709,27 +718,13 @@ func (h *Headscale) RegisterMachine( return nil, err } - // TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set - // This means that if a user is to slow with register a machine, it will possibly not - // have the correct expiry. - requestedTime := time.Time{} - if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.String()); found { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("Expiry time found in cache, assigning to node") - if reqTime, ok := requestedTimeIf.(time.Time); ok { - requestedTime = reqTime - } - } - if machine.isRegistered() { log.Trace(). Caller(). Str("machine", machine.Name). Msg("machine already registered, reauthenticating") - h.RefreshMachine(machine, requestedTime) + h.RefreshMachine(machine, *expiry) return machine, nil } @@ -739,17 +734,6 @@ func (h *Headscale) RegisterMachine( Str("machine", machine.Name). Msg("Attempting to register machine") - if machine.isRegistered() { - err := errMachineAlreadyRegistered - log.Error(). - Caller(). - Err(err). - Str("machine", machine.Name). - Msg("Attempting to register machine") - - return nil, err - } - h.ipAllocationMutex.Lock() defer h.ipAllocationMutex.Unlock() @@ -764,17 +748,36 @@ func (h *Headscale) RegisterMachine( return nil, err } - log.Trace(). - Caller(). - Str("machine", machine.Name). - Str("ip", strings.Join(ips.ToStringSlice(), ",")). - Msg("Found IP for host") - machine.IPAddresses = ips + + if expiry != nil { + machine.Expiry = expiry + } + + if authKey != nil { + machine.AuthKeyID = uint(authKey.ID) + } + + if nodePublicKey != nil { + machine.NodeKey = *nodePublicKey + } + + if lastSeen != nil { + machine.LastSeen = lastSeen + } + machine.NamespaceID = namespace.ID + + // TODO(kradalby): This field is uneccessary metadata, + // move it to tags instead of having a column. + machine.RegisterMethod = registrationMethod + + // TODO(kradalby): Registered is a very frustrating value + // to keep up to date, and it makes is have to care if a + // machine is registered, authenticated and expired. + // Let us simplify the model, a machine is _only_ saved if + // it is registered. machine.Registered = true - machine.RegisterMethod = RegisterMethodCLI - machine.Expiry = &requestedTime h.db.Save(&machine) log.Trace(). From acb945841c6878c1af6e66522f146265bd7feaa3 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 18:42:15 +0100 Subject: [PATCH 07/53] Generalise registration for pre auth keys --- api.go | 53 +++++++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/api.go b/api.go index bb5495ae..810271cf 100644 --- a/api.go +++ b/api.go @@ -22,7 +22,7 @@ import ( const ( reservedResponseHeaderSize = 4 - RegisterMethodAuthKey = "authKey" + RegisterMethodAuthKey = RegisterMethodAuthKey RegisterMethodOIDC = "oidc" RegisterMethodCLI = "cli" ErrRegisterMethodCLIDoesNotSupportExpire = Error( @@ -545,7 +545,7 @@ func (h *Headscale) handleAuthKey( Err(err). Msg("Cannot encode message") ctx.String(http.StatusInternalServerError, "") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name). Inc() return @@ -556,7 +556,7 @@ func (h *Headscale) handleAuthKey( Str("func", "handleAuthKey"). Str("machine", machine.Name). Msg("Failed authentication via AuthKey") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name). Inc() return @@ -575,38 +575,31 @@ func (h *Headscale) handleAuthKey( Str("machine", machine.Name). Msg("Authentication key was valid, proceeding to acquire IP addresses") - h.ipAllocationMutex.Lock() + nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey) + now := time.Now().UTC() - ips, err := h.getAvailableIPs() + _, err = h.RegisterMachine( + machine.Name, + machine.Namespace.Name, + RegisterMethodAuthKey, + ®isterRequest.Expiry, + pak, + &nodeKey, + &now, + ) if err != nil { log.Error(). Caller(). - Str("func", "handleAuthKey"). - Str("machine", machine.Name). - Msg("Failed to find an available IP address") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). - Inc() + Err(err). + Msg("could not register machine") + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name).Inc() + ctx.String( + http.StatusInternalServerError, + "could not register machine", + ) return } - log.Info(). - Str("func", "handleAuthKey"). - Str("machine", machine.Name). - Str("ips", strings.Join(ips.ToStringSlice(), ",")). - Msgf("Assigning %s to %s", strings.Join(ips.ToStringSlice(), ","), machine.Name) - - machine.Expiry = ®isterRequest.Expiry - machine.AuthKeyID = uint(pak.ID) - machine.IPAddresses = ips - machine.NamespaceID = pak.NamespaceID - - machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey) - // we update it just in case - machine.Registered = true - machine.RegisterMethod = RegisterMethodAuthKey - h.db.Save(&machine) - - h.ipAllocationMutex.Unlock() } pak.Used = true @@ -622,13 +615,13 @@ func (h *Headscale) handleAuthKey( Str("machine", machine.Name). Err(err). Msg("Cannot encode message") - machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name). Inc() ctx.String(http.StatusInternalServerError, "Extremely sad!") return } - machineRegistrations.WithLabelValues("new", "authkey", "success", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", machine.Namespace.Name). Inc() ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) log.Info(). From fd1e4a1dcd4b08868f70f221eee4f2524a69ffdf Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 18:42:24 +0100 Subject: [PATCH 08/53] Generalise registration for openid --- oidc.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/oidc.go b/oidc.go index edea32a8..5207c64e 100644 --- a/oidc.go +++ b/oidc.go @@ -328,27 +328,28 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - ips, err := h.getAvailableIPs() + _, err = h.RegisterMachine( + machineKeyStr, + namespace.Name, + RegisterMethodOIDC, + &requestedTime, + nil, + nil, + &now, + ) if err != nil { log.Error(). Caller(). Err(err). - Msg("could not get an IP from the pool") + Msg("could not register machine") ctx.String( http.StatusInternalServerError, - "could not get an IP from the pool", + "could not register machine", ) return } - machine.IPAddresses = ips - machine.NamespaceID = namespace.ID - machine.Registered = true - machine.RegisterMethod = RegisterMethodOIDC - machine.LastSuccessfulUpdate = &now - machine.Expiry = &requestedTime - h.db.Save(&machine) } var content bytes.Buffer From caffbd8956776e97e54bbeb6b18ea30a4c287b40 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 18:42:43 +0100 Subject: [PATCH 09/53] Update cli registration with new method --- grpcv1.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/grpcv1.go b/grpcv1.go index 60e181df..b1396bc8 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -159,9 +159,27 @@ func (api headscaleV1APIServer) RegisterMachine( Str("namespace", request.GetNamespace()). Str("machine_key", request.GetKey()). Msg("Registering machine") + + // TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set + // This means that if a user is to slow with register a machine, it will possibly not + // have the correct expiry. + requestedTime := time.Time{} + if requestedTimeIf, found := api.h.requestedExpiryCache.Get(request.GetKey()); found { + log.Trace(). + Caller(). + Str("machine", request.Key). + Msg("Expiry time found in cache, assigning to node") + if reqTime, ok := requestedTimeIf.(time.Time); ok { + requestedTime = reqTime + } + } + machine, err := api.h.RegisterMachine( request.GetKey(), request.GetNamespace(), + RegisterMethodCLI, + &requestedTime, + nil, nil, nil, ) if err != nil { return nil, err From ecc26432fd64c19d3b314536fcfee634d53e3517 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 18:48:12 +0100 Subject: [PATCH 10/53] Fix excessive replace --- api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.go b/api.go index 810271cf..7d535322 100644 --- a/api.go +++ b/api.go @@ -22,7 +22,7 @@ import ( const ( reservedResponseHeaderSize = 4 - RegisterMethodAuthKey = RegisterMethodAuthKey + RegisterMethodAuthKey = "authkey" RegisterMethodOIDC = "oidc" RegisterMethodCLI = "cli" ErrRegisterMethodCLIDoesNotSupportExpire = Error( From 1caa6f5d6915b762cad4c9d5b28654ae0d2efe67 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 27 Feb 2022 18:48:25 +0100 Subject: [PATCH 11/53] Add todo for JSON datatype --- machine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/machine.go b/machine.go index 3415b408..b805de67 100644 --- a/machine.go +++ b/machine.go @@ -53,6 +53,8 @@ type Machine struct { LastSuccessfulUpdate *time.Time Expiry *time.Time + // TODO(kradalby): Figure out a way to use tailcfg datatypes + // here and have gorm serialise them. HostInfo datatypes.JSON Endpoints datatypes.JSON EnabledRoutes datatypes.JSON From 469551bc5df6dd8483af1246560f2f358a2ed943 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 08:06:39 +0000 Subject: [PATCH 12/53] Register new machines needing callback in memory This commit stores temporary registration data in cache, instead of memory allowing us to only have actually registered machines in the database. --- api.go | 153 +++++++++++++++++++++++------------------------- app.go | 9 +++ cli_test.go | 1 + grpcv1.go | 3 +- machine.go | 52 +++++++++++++--- oidc.go | 7 +-- preauth_keys.go | 6 ++ 7 files changed, 136 insertions(+), 95 deletions(-) diff --git a/api.go b/api.go index 7d535322..60ca63f2 100644 --- a/api.go +++ b/api.go @@ -125,25 +125,40 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { machine, err := h.GetMachineByMachineKey(machineKey) if errors.Is(err, gorm.ErrRecordNotFound) { log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") - newMachine := Machine{ - Expiry: &time.Time{}, - MachineKey: MachinePublicKeyStripPrefix(machineKey), - Name: req.Hostinfo.Hostname, - } - if err := h.db.Create(&newMachine).Error; err != nil { - log.Error(). - Caller(). - Err(err). - Msg("Could not create row") - machineRegistrations.WithLabelValues("unknown", "web", "error", machine.Namespace.Name). - Inc() + + machineKeyStr := MachinePublicKeyStripPrefix(machineKey) + + // If the machine has AuthKey set, handle registration via PreAuthKeys + if req.Auth.AuthKey != "" { + h.handleAuthKey(ctx, machineKey, req) return } - machine = &newMachine + + // The machine did not have a key to authenticate, which means + // that we rely on a method that calls back some how (OpenID or CLI) + // We create the machine and then keep it around until a callback + // happens + newMachine := Machine{ + Expiry: &time.Time{}, + MachineKey: machineKeyStr, + Name: req.Hostinfo.Hostname, + NodeKey: NodePublicKeyStripPrefix(req.NodeKey), + LastSeen: &now, + } + + h.registrationCache.Set( + machineKeyStr, + newMachine, + requestedExpiryCacheExpiration, + ) + + h.handleMachineRegistrationNew(ctx, machineKey, req) + return } - if machine.Registered { + // The machine is already registered, so we need to pass through reauth or key update. + if machine != nil { // If the NodeKey stored in headscale is the same as the key presented in a registration // request, then we have a node that is either: // - Trying to log out (sending a expiry in the past) @@ -180,15 +195,6 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { return } - - // If the machine has AuthKey set, handle registration via PreAuthKeys - if req.Auth.AuthKey != "" { - h.handleAuthKey(ctx, machineKey, req, *machine) - - return - } - - h.handleMachineRegistrationNew(ctx, machineKey, req, *machine) } func (h *Headscale) getMapResponse( @@ -402,7 +408,7 @@ func (h *Headscale) handleMachineExpired( Msg("Machine registration has expired. Sending a authurl to register") if registerRequest.Auth.AuthKey != "" { - h.handleAuthKey(ctx, machineKey, registerRequest, machine) + h.handleAuthKey(ctx, machineKey, registerRequest) return } @@ -465,13 +471,12 @@ func (h *Headscale) handleMachineRegistrationNew( ctx *gin.Context, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, - machine Machine, ) { resp := tailcfg.RegisterResponse{} // The machine registration is new, redirect the client to the registration URL log.Debug(). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Msg("The node is sending us a new NodeKey, sending auth url") if h.cfg.OIDC.Issuer != "" { resp.AuthURL = fmt.Sprintf( @@ -487,7 +492,7 @@ func (h *Headscale) handleMachineRegistrationNew( if !registerRequest.Expiry.IsZero() { log.Trace(). Caller(). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Time("expiry", registerRequest.Expiry). Msg("Non-zero expiry time requested, adding to cache") h.requestedExpiryCache.Set( @@ -497,11 +502,6 @@ func (h *Headscale) handleMachineRegistrationNew( ) } - machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey) - - // save the NodeKey - h.db.Save(&machine) - respBody, err := encode(resp, &machineKey, h.privateKey) if err != nil { log.Error(). @@ -520,19 +520,21 @@ func (h *Headscale) handleAuthKey( ctx *gin.Context, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, - machine Machine, ) { + machineKeyStr := MachinePublicKeyStripPrefix(machineKey) + log.Debug(). Str("func", "handleAuthKey"). Str("machine", registerRequest.Hostinfo.Hostname). Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname) resp := tailcfg.RegisterResponse{} + pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey) if err != nil { log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Failed authentication via AuthKey") resp.MachineAuthorized = false @@ -541,69 +543,62 @@ func (h *Headscale) handleAuthKey( log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Cannot encode message") ctx.String(http.StatusInternalServerError, "") - machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() return } + ctx.Data(http.StatusUnauthorized, "application/json; charset=utf-8", respBody) log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Msg("Failed authentication via AuthKey") - machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() return } - if machine.isRegistered() { - log.Trace(). + log.Debug(). + Str("func", "handleAuthKey"). + Str("machine", registerRequest.Hostinfo.Hostname). + Msg("Authentication key was valid, proceeding to acquire IP addresses") + + nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey) + now := time.Now().UTC() + + machine, err := h.RegisterMachine( + registerRequest.Hostinfo.Hostname, + machineKeyStr, + pak.Namespace.Name, + RegisterMethodAuthKey, + ®isterRequest.Expiry, + pak, + &nodeKey, + &now, + ) + if err != nil { + log.Error(). Caller(). - Str("machine", machine.Name). - Msg("machine already registered, reauthenticating") - - h.RefreshMachine(&machine, registerRequest.Expiry) - } else { - log.Debug(). - Str("func", "handleAuthKey"). - Str("machine", machine.Name). - Msg("Authentication key was valid, proceeding to acquire IP addresses") - - nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey) - now := time.Now().UTC() - - _, err = h.RegisterMachine( - machine.Name, - machine.Namespace.Name, - RegisterMethodAuthKey, - ®isterRequest.Expiry, - pak, - &nodeKey, - &now, + Err(err). + Msg("could not register machine") + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). + Inc() + ctx.String( + http.StatusInternalServerError, + "could not register machine", ) - if err != nil { - log.Error(). - Caller(). - Err(err). - Msg("could not register machine") - machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name).Inc() - ctx.String( - http.StatusInternalServerError, - "could not register machine", - ) - return - } + return } - pak.Used = true - h.db.Save(&pak) + h.UsePreAuthKey(pak) resp.MachineAuthorized = true resp.User = *pak.Namespace.toUser() @@ -612,21 +607,21 @@ func (h *Headscale) handleAuthKey( log.Error(). Caller(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Cannot encode message") - machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() ctx.String(http.StatusInternalServerError, "Extremely sad!") return } - machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", machine.Namespace.Name). + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name). Inc() ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) log.Info(). Str("func", "handleAuthKey"). - Str("machine", machine.Name). + Str("machine", registerRequest.Hostinfo.Hostname). Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")). Msg("Successfully authenticated via AuthKey") } diff --git a/app.go b/app.go index eda670ed..63ca10e4 100644 --- a/app.go +++ b/app.go @@ -154,6 +154,8 @@ type Headscale struct { requestedExpiryCache *cache.Cache + registrationCache *cache.Cache + ipAllocationMutex sync.Mutex } @@ -207,6 +209,12 @@ func NewHeadscale(cfg Config) (*Headscale, error) { requestedExpiryCacheCleanupInterval, ) + registrationCache := cache.New( + // TODO(kradalby): Add unified cache expiry config options + requestedExpiryCacheExpiration, + requestedExpiryCacheCleanupInterval, + ) + app := Headscale{ cfg: cfg, dbType: cfg.DBtype, @@ -214,6 +222,7 @@ func NewHeadscale(cfg Config) (*Headscale, error) { privateKey: privKey, aclRules: tailcfg.FilterAllowAll, // default allowall requestedExpiryCache: requestedExpiryCache, + registrationCache: registrationCache, } err = app.initDB() diff --git a/cli_test.go b/cli_test.go index 2eedb5b4..fab8201f 100644 --- a/cli_test.go +++ b/cli_test.go @@ -30,6 +30,7 @@ func (s *Suite) TestRegisterMachine(c *check.C) { c.Assert(err, check.IsNil) machineAfterRegistering, err := app.RegisterMachine( + "testmachine", machine.MachineKey, namespace.Name, RegisterMethodCLI, diff --git a/grpcv1.go b/grpcv1.go index b1396bc8..3a2e19ce 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -174,12 +174,11 @@ func (api headscaleV1APIServer) RegisterMachine( } } - machine, err := api.h.RegisterMachine( + machine, err := api.h.RegisterMachineFromAuthCallback( request.GetKey(), request.GetNamespace(), RegisterMethodCLI, &requestedTime, - nil, nil, nil, ) if err != nil { return nil, err diff --git a/machine.go b/machine.go index b805de67..74251553 100644 --- a/machine.go +++ b/machine.go @@ -20,11 +20,15 @@ import ( ) const ( - errMachineNotFound = Error("machine not found") - errMachineAlreadyRegistered = Error("machine already registered") - errMachineRouteIsNotAvailable = Error("route is not available on machine") - errMachineAddressesInvalid = Error("failed to parse machine addresses") - errHostnameTooLong = Error("Hostname too long") + errMachineNotFound = Error("machine not found") + errMachineAlreadyRegistered = Error("machine already registered") + errMachineRouteIsNotAvailable = Error("route is not available on machine") + errMachineAddressesInvalid = Error("failed to parse machine addresses") + errMachineNotFoundRegistrationCache = Error( + "machine not found in registration cache", + ) + errCouldNotConvertMachineInterface = Error("failed to convert machine interface") + errHostnameTooLong = Error("Hostname too long") ) const ( @@ -686,14 +690,44 @@ func (machine *Machine) toProto() *v1.Machine { return machineProto } -// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. -func (h *Headscale) RegisterMachine( +func (h *Headscale) RegisterMachineFromAuthCallback( machineKeyStr string, namespaceName string, registrationMethod string, + expiry *time.Time, +) (*Machine, error) { + if machineInterface, ok := h.registrationCache.Get(machineKeyStr); ok { + if registrationMachine, ok := machineInterface.(Machine); ok { + machine, err := h.RegisterMachine( + registrationMachine.Name, + machineKeyStr, + namespaceName, + registrationMethod, + expiry, + nil, + ®istrationMachine.NodeKey, + registrationMachine.LastSeen, + ) + + return machine, err + + } else { + return nil, errCouldNotConvertMachineInterface + } + } + + return nil, errMachineNotFoundRegistrationCache +} + +// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. +func (h *Headscale) RegisterMachine( + machineName string, + machineKeyStr string, + namespaceName string, + registrationMethod string, + expiry *time.Time, // Optionals - expiry *time.Time, authKey *PreAuthKey, nodePublicKey *string, lastSeen *time.Time, @@ -768,6 +802,7 @@ func (h *Headscale) RegisterMachine( machine.LastSeen = lastSeen } + machine.Name = machineName machine.NamespaceID = namespace.ID // TODO(kradalby): This field is uneccessary metadata, @@ -780,6 +815,7 @@ func (h *Headscale) RegisterMachine( // Let us simplify the model, a machine is _only_ saved if // it is registered. machine.Registered = true + h.db.Save(&machine) log.Trace(). diff --git a/oidc.go b/oidc.go index 5207c64e..48664aed 100644 --- a/oidc.go +++ b/oidc.go @@ -279,8 +279,6 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - now := time.Now().UTC() - namespaceName, err := NormalizeNamespaceName( claims.Email, h.cfg.OIDC.StripEmaildomain, @@ -328,14 +326,11 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - _, err = h.RegisterMachine( + _, err = h.RegisterMachineFromAuthCallback( machineKeyStr, namespace.Name, RegisterMethodOIDC, &requestedTime, - nil, - nil, - &now, ) if err != nil { log.Error(). diff --git a/preauth_keys.go b/preauth_keys.go index 50bc4746..55f62226 100644 --- a/preauth_keys.go +++ b/preauth_keys.go @@ -113,6 +113,12 @@ func (h *Headscale) ExpirePreAuthKey(k *PreAuthKey) error { return nil } +// UsePreAuthKey marks a PreAuthKey as used. +func (h *Headscale) UsePreAuthKey(k *PreAuthKey) { + k.Used = true + h.db.Save(k) +} + // checkKeyValidity does the heavy lifting for validation of the PreAuthKey coming from a node // If returns no error and a PreAuthKey, it can be used. func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { From 402a76070f501bf490dd75391b72ef1743db66e8 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:34:28 +0000 Subject: [PATCH 13/53] Reuse machine structure for parameters, named parameters --- machine.go | 89 +++++++++++++++++------------------------------------- 1 file changed, 27 insertions(+), 62 deletions(-) diff --git a/machine.go b/machine.go index 74251553..6e5b62d9 100644 --- a/machine.go +++ b/machine.go @@ -21,7 +21,6 @@ import ( const ( errMachineNotFound = Error("machine not found") - errMachineAlreadyRegistered = Error("machine already registered") errMachineRouteIsNotAvailable = Error("route is not available on machine") errMachineAddressesInvalid = Error("failed to parse machine addresses") errMachineNotFoundRegistrationCache = Error( @@ -698,19 +697,23 @@ func (h *Headscale) RegisterMachineFromAuthCallback( ) (*Machine, error) { if machineInterface, ok := h.registrationCache.Get(machineKeyStr); ok { if registrationMachine, ok := machineInterface.(Machine); ok { + namespace, err := h.GetNamespace(namespaceName) + if err != nil { + return nil, fmt.Errorf( + "failed to find namespace in register machine from auth callback, %w", + err, + ) + } + + registrationMachine.NamespaceID = namespace.ID + registrationMachine.RegisterMethod = registrationMethod + registrationMachine.Expiry = expiry + machine, err := h.RegisterMachine( - registrationMachine.Name, - machineKeyStr, - namespaceName, - registrationMethod, - expiry, - nil, - ®istrationMachine.NodeKey, - registrationMachine.LastSeen, + registrationMachine, ) return machine, err - } else { return nil, errCouldNotConvertMachineInterface } @@ -720,49 +723,30 @@ func (h *Headscale) RegisterMachineFromAuthCallback( } // RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. -func (h *Headscale) RegisterMachine( - machineName string, - machineKeyStr string, - namespaceName string, - registrationMethod string, - expiry *time.Time, - - // Optionals - authKey *PreAuthKey, - nodePublicKey *string, - lastSeen *time.Time, +func (h *Headscale) RegisterMachine(machine Machine, ) (*Machine, error) { - namespace, err := h.GetNamespace(namespaceName) - if err != nil { - return nil, err - } - - var machineKey key.MachinePublic - err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) - if err != nil { - return nil, err - } - log.Trace(). Caller(). - Str("machine_key_str", machineKeyStr). - Str("machine_key", machineKey.String()). + Str("machine_key", machine.MachineKey). Msg("Registering machine") - machine, err := h.GetMachineByMachineKey(machineKey) - if err != nil { - return nil, err - } - + // If the machine is already in the database, it is seeking + // reauthentication, and by reaching this step, has been authenticated + // and need to have an updated expiry. + var machineKey key.MachinePublic + _ = machineKey.UnmarshalText( + []byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)), + ) + machineFromDatabase, _ := h.GetMachineByMachineKey(machineKey) if machine.isRegistered() { log.Trace(). Caller(). Str("machine", machine.Name). Msg("machine already registered, reauthenticating") - h.RefreshMachine(machine, *expiry) + h.RefreshMachine(machineFromDatabase, *machine.Expiry) - return machine, nil + return machineFromDatabase, nil } log.Trace(). @@ -786,28 +770,9 @@ func (h *Headscale) RegisterMachine( machine.IPAddresses = ips - if expiry != nil { - machine.Expiry = expiry - } - - if authKey != nil { - machine.AuthKeyID = uint(authKey.ID) - } - - if nodePublicKey != nil { - machine.NodeKey = *nodePublicKey - } - - if lastSeen != nil { - machine.LastSeen = lastSeen - } - - machine.Name = machineName - machine.NamespaceID = namespace.ID - // TODO(kradalby): This field is uneccessary metadata, // move it to tags instead of having a column. - machine.RegisterMethod = registrationMethod + // machine.RegisterMethod = registrationMethod // TODO(kradalby): Registered is a very frustrating value // to keep up to date, and it makes is have to care if a @@ -824,7 +789,7 @@ func (h *Headscale) RegisterMachine( Str("ip", strings.Join(ips.ToStringSlice(), ",")). Msg("Machine registered with the database") - return machine, nil + return &machine, nil } func (machine *Machine) GetAdvertisedRoutes() ([]netaddr.IPPrefix, error) { From 54cc3c067ffabf2cae5339a615598ab82feb02fd Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:34:50 +0000 Subject: [PATCH 14/53] Implement new machine register parameter --- api.go | 21 +++++++++++++-------- grpcv1.go | 10 +++++----- oidc.go | 1 - 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/api.go b/api.go index 60ca63f2..f831cacf 100644 --- a/api.go +++ b/api.go @@ -154,6 +154,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { ) h.handleMachineRegistrationNew(ctx, machineKey, req) + return } @@ -573,15 +574,19 @@ func (h *Headscale) handleAuthKey( nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey) now := time.Now().UTC() + machineToRegister := Machine{ + Name: registerRequest.Hostinfo.Hostname, + NamespaceID: pak.Namespace.ID, + MachineKey: machineKeyStr, + RegisterMethod: RegisterMethodAuthKey, + Expiry: ®isterRequest.Expiry, + NodeKey: nodeKey, + LastSeen: &now, + AuthKeyID: uint(pak.ID), + } + machine, err := h.RegisterMachine( - registerRequest.Hostinfo.Hostname, - machineKeyStr, - pak.Namespace.Name, - RegisterMethodAuthKey, - ®isterRequest.Expiry, - pak, - &nodeKey, - &now, + machineToRegister, ) if err != nil { log.Error(). diff --git a/grpcv1.go b/grpcv1.go index 3a2e19ce..75732b1d 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -415,11 +415,11 @@ func (api headscaleV1APIServer) DebugCreateMachine( HostInfo: datatypes.JSON(hostinfoJson), } - // log.Trace().Caller().Interface("machine", newMachine).Msg("") - - if err := api.h.db.Create(&newMachine).Error; err != nil { - return nil, err - } + api.h.registrationCache.Set( + request.GetKey(), + newMachine, + requestedExpiryCacheExpiration, + ) return &v1.DebugCreateMachineResponse{Machine: newMachine.toProto()}, nil } diff --git a/oidc.go b/oidc.go index 48664aed..952e2600 100644 --- a/oidc.go +++ b/oidc.go @@ -344,7 +344,6 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - } var content bytes.Buffer From 50053e616a42109b22a1aa15cba56461c04035eb Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:35:08 +0000 Subject: [PATCH 15/53] Ignore complexity linter --- .golangci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.golangci.yaml b/.golangci.yaml index 153cd7c3..56fdc28e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -29,6 +29,7 @@ linters: - wrapcheck - dupl - makezero + - maintidx # We might want to enable this, but it might be a lot of work - cyclop From c6b87de95901193b7a72494832fdfc94456dceae Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:36:16 +0000 Subject: [PATCH 16/53] Remove poorly aged test --- cli_test.go | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 cli_test.go diff --git a/cli_test.go b/cli_test.go deleted file mode 100644 index fab8201f..00000000 --- a/cli_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package headscale - -import ( - "time" - - "gopkg.in/check.v1" - "inet.af/netaddr" -) - -func (s *Suite) TestRegisterMachine(c *check.C) { - namespace, err := app.CreateNamespace("test") - c.Assert(err, check.IsNil) - - now := time.Now().UTC() - - machine := Machine{ - ID: 0, - MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", - NodeKey: "bar", - DiscoKey: "faa", - Name: "testmachine", - NamespaceID: namespace.ID, - IPAddresses: []netaddr.IP{netaddr.MustParseIP("10.0.0.1")}, - Expiry: &now, - } - err = app.db.Save(&machine).Error - c.Assert(err, check.IsNil) - - _, err = app.GetMachine(namespace.Name, machine.Name) - c.Assert(err, check.IsNil) - - machineAfterRegistering, err := app.RegisterMachine( - "testmachine", - machine.MachineKey, - namespace.Name, - RegisterMethodCLI, - nil, nil, nil, nil, - ) - c.Assert(err, check.IsNil) - c.Assert(machineAfterRegistering.Registered, check.Equals, true) - - _, err = machineAfterRegistering.GetHostInfo() - c.Assert(err, check.IsNil) -} From e7bef567184622a7e5374023ae5238e2fccd00ed Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:36:29 +0000 Subject: [PATCH 17/53] Remove reference to registered in integration test --- integration_cli_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/integration_cli_test.go b/integration_cli_test.go index aae80cb0..7ce0758f 100644 --- a/integration_cli_test.go +++ b/integration_cli_test.go @@ -621,12 +621,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { assert.Equal(s.T(), "machine-4", listAll[3].Name) assert.Equal(s.T(), "machine-5", listAll[4].Name) - assert.True(s.T(), listAll[0].Registered) - assert.True(s.T(), listAll[1].Registered) - assert.True(s.T(), listAll[2].Registered) - assert.True(s.T(), listAll[3].Registered) - assert.True(s.T(), listAll[4].Registered) - otherNamespaceMachineKeys := []string{ "b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", "dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", @@ -710,9 +704,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { assert.Equal(s.T(), "otherNamespace-machine-1", listAllWithotherNamespace[5].Name) assert.Equal(s.T(), "otherNamespace-machine-2", listAllWithotherNamespace[6].Name) - assert.True(s.T(), listAllWithotherNamespace[5].Registered) - assert.True(s.T(), listAllWithotherNamespace[6].Registered) - // Test list all nodes after added otherNamespace listOnlyotherNamespaceMachineNamespaceResult, err := ExecuteCommand( &s.headscale, @@ -752,9 +743,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { listOnlyotherNamespaceMachineNamespace[1].Name, ) - assert.True(s.T(), listOnlyotherNamespaceMachineNamespace[0].Registered) - assert.True(s.T(), listOnlyotherNamespaceMachineNamespace[1].Registered) - // Delete a machines _, err = ExecuteCommand( &s.headscale, @@ -979,7 +967,6 @@ func (s *IntegrationCLITestSuite) TestRouteCommand() { assert.Equal(s.T(), uint64(1), machine.Id) assert.Equal(s.T(), "route-machine", machine.Name) - assert.True(s.T(), machine.Registered) listAllResult, err := ExecuteCommand( &s.headscale, From 35616eb8611ab7514e45ab3a7eb01a5abe0c52f3 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:41:28 +0000 Subject: [PATCH 18/53] Fix oidc error were namespace isnt created #365 --- oidc.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/oidc.go b/oidc.go index 952e2600..f291e8c9 100644 --- a/oidc.go +++ b/oidc.go @@ -17,7 +17,6 @@ import ( "github.com/patrickmn/go-cache" "github.com/rs/zerolog/log" "golang.org/x/oauth2" - "gorm.io/gorm" "tailscale.com/types/key" ) @@ -297,7 +296,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { log.Debug().Msg("Registering new machine after successful callback") namespace, err := h.GetNamespace(namespaceName) - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, errNamespaceNotFound) { namespace, err = h.CreateNamespace(namespaceName) if err != nil { From 16b21e8158d7a32d1ed1b093f05268a633a5bd17 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 16:55:57 +0000 Subject: [PATCH 19/53] Remove all references to Machine.Registered --- machine.go | 24 +++------------- oidc.go | 81 +++++++++++++++++++++++++++--------------------------- 2 files changed, 44 insertions(+), 61 deletions(-) diff --git a/machine.go b/machine.go index 6e5b62d9..16d6d86f 100644 --- a/machine.go +++ b/machine.go @@ -72,11 +72,6 @@ type ( MachinesP []*Machine ) -// For the time being this method is rather naive. -func (machine Machine) isRegistered() bool { - return machine.Registered -} - type MachineAddresses []netaddr.IP func (ma MachineAddresses) ToStringSlice() []string { @@ -221,7 +216,7 @@ func (h *Headscale) ListPeers(machine *Machine) (Machines, error) { Msg("Finding direct peers") machines := Machines{} - if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("machine_key <> ? AND registered", + if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("machine_key <> ?", machine.MachineKey).Find(&machines).Error; err != nil { log.Error().Err(err).Msg("Error accessing db") @@ -284,7 +279,7 @@ func (h *Headscale) getValidPeers(machine *Machine) (Machines, error) { } for _, peer := range peers { - if peer.isRegistered() && !peer.isExpired() { + if !peer.isExpired() { validPeers = append(validPeers, peer) } } @@ -373,8 +368,6 @@ func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) { // DeleteMachine softs deletes a Machine from the database. func (h *Headscale) DeleteMachine(machine *Machine) error { - machine.Registered = false - h.db.Save(&machine) // we mark it as unregistered, just in case if err := h.db.Delete(&machine).Error; err != nil { return err } @@ -642,7 +635,7 @@ func (machine Machine) toNode( LastSeen: machine.LastSeen, KeepAlive: true, - MachineAuthorized: machine.Registered, + MachineAuthorized: !machine.isExpired(), Capabilities: []string{tailcfg.CapabilityFileSharing}, } @@ -660,8 +653,6 @@ func (machine *Machine) toProto() *v1.Machine { Name: machine.Name, Namespace: machine.Namespace.toProto(), - Registered: machine.Registered, - // TODO(kradalby): Implement register method enum converter // RegisterMethod: , @@ -738,7 +729,7 @@ func (h *Headscale) RegisterMachine(machine Machine, []byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)), ) machineFromDatabase, _ := h.GetMachineByMachineKey(machineKey) - if machine.isRegistered() { + if machineFromDatabase != nil { log.Trace(). Caller(). Str("machine", machine.Name). @@ -774,13 +765,6 @@ func (h *Headscale) RegisterMachine(machine Machine, // move it to tags instead of having a column. // machine.RegisterMethod = registrationMethod - // TODO(kradalby): Registered is a very frustrating value - // to keep up to date, and it makes is have to care if a - // machine is registered, authenticated and expired. - // Let us simplify the model, a machine is _only_ saved if - // it is registered. - machine.Registered = true - h.db.Save(&machine) log.Trace(). diff --git a/oidc.go b/oidc.go index f291e8c9..e745236b 100644 --- a/oidc.go +++ b/oidc.go @@ -248,7 +248,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - if machine.isRegistered() { + if machine != nil { log.Trace(). Caller(). Str("machine", machine.Name). @@ -291,58 +291,57 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } + // register the machine if it's new - if !machine.Registered { - log.Debug().Msg("Registering new machine after successful callback") + log.Debug().Msg("Registering new machine after successful callback") - namespace, err := h.GetNamespace(namespaceName) - if errors.Is(err, errNamespaceNotFound) { - namespace, err = h.CreateNamespace(namespaceName) + namespace, err := h.GetNamespace(namespaceName) + if errors.Is(err, errNamespaceNotFound) { + namespace, err = h.CreateNamespace(namespaceName) - if err != nil { - log.Error(). - Err(err). - Caller(). - Msgf("could not create new namespace '%s'", namespaceName) - ctx.String( - http.StatusInternalServerError, - "could not create new namespace", - ) - - return - } - } else if err != nil { - log.Error(). - Caller(). - Err(err). - Str("namespace", namespaceName). - Msg("could not find or create namespace") - ctx.String( - http.StatusInternalServerError, - "could not find or create namespace", - ) - - return - } - - _, err = h.RegisterMachineFromAuthCallback( - machineKeyStr, - namespace.Name, - RegisterMethodOIDC, - &requestedTime, - ) if err != nil { log.Error(). - Caller(). Err(err). - Msg("could not register machine") + Caller(). + Msgf("could not create new namespace '%s'", namespaceName) ctx.String( http.StatusInternalServerError, - "could not register machine", + "could not create new namespace", ) return } + } else if err != nil { + log.Error(). + Caller(). + Err(err). + Str("namespace", namespaceName). + Msg("could not find or create namespace") + ctx.String( + http.StatusInternalServerError, + "could not find or create namespace", + ) + + return + } + + _, err = h.RegisterMachineFromAuthCallback( + machineKeyStr, + namespace.Name, + RegisterMethodOIDC, + &requestedTime, + ) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("could not register machine") + ctx.String( + http.StatusInternalServerError, + "could not register machine", + ) + + return } var content bytes.Buffer From a8649d83c4b655eae469d556eacafe8c81985a40 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 17:42:03 +0000 Subject: [PATCH 20/53] Remove all references to Machine.Registered from tests --- acls_test.go | 7 ------- dns_test.go | 8 -------- machine_test.go | 7 ------- namespaces_test.go | 5 ----- preauth_keys_test.go | 3 --- routes_test.go | 2 -- utils_test.go | 3 --- 7 files changed, 35 deletions(-) diff --git a/acls_test.go b/acls_test.go index 5534257d..edea854a 100644 --- a/acls_test.go +++ b/acls_test.go @@ -119,7 +119,6 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) { Name: "testmachine", IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostInfo), @@ -163,7 +162,6 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) { Name: "testmachine", IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostInfo), @@ -207,7 +205,6 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { Name: "testmachine", IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostInfo), @@ -250,7 +247,6 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { Name: "webserver", IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostInfo), @@ -267,7 +263,6 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { Name: "user", IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostInfo), @@ -345,7 +340,6 @@ func (s *Suite) TestPortNamespace(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: ips, AuthKeyID: uint(pak.ID), @@ -388,7 +382,6 @@ func (s *Suite) TestPortGroup(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: ips, AuthKeyID: uint(pak.ID), diff --git a/dns_test.go b/dns_test.go index 23a34ca7..5fd30d3b 100644 --- a/dns_test.go +++ b/dns_test.go @@ -164,7 +164,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_1", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, AuthKeyID: uint(preAuthKeyInShared1.ID), @@ -182,7 +181,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_2", NamespaceID: namespaceShared2.ID, Namespace: *namespaceShared2, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, AuthKeyID: uint(preAuthKeyInShared2.ID), @@ -200,7 +198,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_3", NamespaceID: namespaceShared3.ID, Namespace: *namespaceShared3, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, AuthKeyID: uint(preAuthKeyInShared3.ID), @@ -218,7 +215,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { Name: "test_get_shared_nodes_4", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, AuthKeyID: uint(PreAuthKey2InShared1.ID), @@ -311,7 +307,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_1", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, AuthKeyID: uint(preAuthKeyInShared1.ID), @@ -329,7 +324,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_2", NamespaceID: namespaceShared2.ID, Namespace: *namespaceShared2, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, AuthKeyID: uint(preAuthKeyInShared2.ID), @@ -347,7 +341,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_3", NamespaceID: namespaceShared3.ID, Namespace: *namespaceShared3, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, AuthKeyID: uint(preAuthKeyInShared3.ID), @@ -365,7 +358,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { Name: "test_get_shared_nodes_4", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, AuthKeyID: uint(preAuthKey2InShared1.ID), diff --git a/machine_test.go b/machine_test.go index e9c91f8b..733270e4 100644 --- a/machine_test.go +++ b/machine_test.go @@ -29,7 +29,6 @@ func (s *Suite) TestGetMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -59,7 +58,6 @@ func (s *Suite) TestGetMachineByID(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -82,7 +80,6 @@ func (s *Suite) TestDeleteMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(1), } @@ -105,7 +102,6 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine3", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(1), } @@ -136,7 +132,6 @@ func (s *Suite) TestListPeers(c *check.C) { DiscoKey: "faa" + strconv.Itoa(index), Name: "testmachine" + strconv.Itoa(index), NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -188,7 +183,6 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { }, Name: "testmachine" + strconv.Itoa(index), NamespaceID: stor[index%2].namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(stor[index%2].key.ID), } @@ -258,7 +252,6 @@ func (s *Suite) TestExpireMachine(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), Expiry: &time.Time{}, diff --git a/namespaces_test.go b/namespaces_test.go index 585ba938..1710f7d6 100644 --- a/namespaces_test.go +++ b/namespaces_test.go @@ -54,7 +54,6 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -146,7 +145,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_1", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, AuthKeyID: uint(preAuthKeyShared1.ID), @@ -164,7 +162,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_2", NamespaceID: namespaceShared2.ID, Namespace: *namespaceShared2, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, AuthKeyID: uint(preAuthKeyShared2.ID), @@ -182,7 +179,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_3", NamespaceID: namespaceShared3.ID, Namespace: *namespaceShared3, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, AuthKeyID: uint(preAuthKeyShared3.ID), @@ -200,7 +196,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { Name: "test_get_shared_nodes_4", NamespaceID: namespaceShared1.ID, Namespace: *namespaceShared1, - Registered: true, RegisterMethod: RegisterMethodAuthKey, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, AuthKeyID: uint(preAuthKey2Shared1.ID), diff --git a/preauth_keys_test.go b/preauth_keys_test.go index f8cf276d..6f233a94 100644 --- a/preauth_keys_test.go +++ b/preauth_keys_test.go @@ -80,7 +80,6 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { DiscoKey: "faa", Name: "testest", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -105,7 +104,6 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { DiscoKey: "faa", Name: "testest", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } @@ -143,7 +141,6 @@ func (*Suite) TestEphemeralKey(c *check.C) { DiscoKey: "faa", Name: "testest", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, LastSeen: &now, AuthKeyID: uint(pak.ID), diff --git a/routes_test.go b/routes_test.go index 94bda45b..1c24bf26 100644 --- a/routes_test.go +++ b/routes_test.go @@ -35,7 +35,6 @@ func (s *Suite) TestGetRoutes(c *check.C) { DiscoKey: "faa", Name: "test_get_route_machine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostinfo), @@ -89,7 +88,6 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { DiscoKey: "faa", Name: "test_enable_route_machine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), HostInfo: datatypes.JSON(hostinfo), diff --git a/utils_test.go b/utils_test.go index 271013a1..7c72c09a 100644 --- a/utils_test.go +++ b/utils_test.go @@ -36,7 +36,6 @@ func (s *Suite) TestGetUsedIps(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), IPAddresses: ips, @@ -85,7 +84,6 @@ func (s *Suite) TestGetMultiIp(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), IPAddresses: ips, @@ -176,7 +174,6 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { DiscoKey: "faa", Name: "testmachine", NamespaceID: namespace.ID, - Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), } From 78251ce8ec5e37295e2498dabb87170c3f5e1cce Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 18:05:03 +0000 Subject: [PATCH 21/53] Remove registrated field This commit removes the field from the database and does a DB migration **removing** all unregistered machines from headscale. This means that from this version, all machines in the database is considered registered. --- db.go | 33 +++++++++++++++++++++++++++++++++ machine.go | 1 - 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/db.go b/db.go index 8db7bc69..e87cb6d3 100644 --- a/db.go +++ b/db.go @@ -5,6 +5,7 @@ import ( "time" "github.com/glebarez/sqlite" + "github.com/rs/zerolog/log" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" @@ -34,6 +35,38 @@ func (h *Headscale) initDB() error { _ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses") + // If the Machine table has a column for registered, + // find all occourences of "false" and drop them. Then + // remove the column. + if db.Migrator().HasColumn(&Machine{}, "registered") { + log.Info(). + Msg(`Database has legacy "registered" column in machine, removing...`) + + machines := Machines{} + if err := h.db.Not("registered").Find(&machines).Error; err != nil { + log.Error().Err(err).Msg("Error accessing db") + } + + for _, machine := range machines { + log.Info(). + Str("machine", machine.Name). + Str("machine_key", machine.MachineKey). + Msg("Deleting unregistered machine") + if err := h.db.Delete(&Machine{}, machine.ID).Error; err != nil { + log.Error(). + Err(err). + Str("machine", machine.Name). + Str("machine_key", machine.MachineKey). + Msg("Error deleting unregistered machine") + } + } + + err := db.Migrator().DropColumn(&Machine{}, "registered") + if err != nil { + log.Error().Err(err).Msg("Error dropping registered column") + } + } + err = db.AutoMigrate(&Machine{}) if err != nil { return err diff --git a/machine.go b/machine.go index 16d6d86f..ac1a1c84 100644 --- a/machine.go +++ b/machine.go @@ -45,7 +45,6 @@ type Machine struct { NamespaceID uint Namespace Namespace `gorm:"foreignKey:NamespaceID"` - Registered bool // temp RegisterMethod string // TODO(kradalby): This seems like irrelevant information? From eea8e7ba6f2d69d41a065959d566e69013480e67 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 22:11:31 +0000 Subject: [PATCH 22/53] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44db566..20c23c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ **Changes**: - Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) +- Simplify the code behind registration of machines [#366](https://github.com/juanfont/headscale/pull/366) + - Nodes are now only written to database if they are registrated successfully **0.14.0 (2022-02-24):** From 5e1b12948e305dca78eeccdce826784092174cd0 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 22:21:06 +0000 Subject: [PATCH 23/53] Remove registered field from proto --- proto/headscale/v1/machine.proto | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proto/headscale/v1/machine.proto b/proto/headscale/v1/machine.proto index 7451db83..75552d78 100644 --- a/proto/headscale/v1/machine.proto +++ b/proto/headscale/v1/machine.proto @@ -22,16 +22,16 @@ message Machine { string name = 6; Namespace namespace = 7; - bool registered = 8; - RegisterMethod register_method = 9; - google.protobuf.Timestamp last_seen = 10; - google.protobuf.Timestamp last_successful_update = 11; - google.protobuf.Timestamp expiry = 12; + google.protobuf.Timestamp last_seen = 8; + google.protobuf.Timestamp last_successful_update = 9; + google.protobuf.Timestamp expiry = 10; - PreAuthKey pre_auth_key = 13; + PreAuthKey pre_auth_key = 11; - google.protobuf.Timestamp created_at = 14; + google.protobuf.Timestamp created_at = 12; + + RegisterMethod register_method = 13; // google.protobuf.Timestamp updated_at = 14; // google.protobuf.Timestamp deleted_at = 15; From e64bee778f4e925553c4045b6087ef933bff3779 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 22:21:14 +0000 Subject: [PATCH 24/53] Regenerate proto --- gen/go/headscale/v1/machine.pb.go | 100 ++++++++---------- .../headscale/v1/headscale.swagger.json | 9 +- 2 files changed, 48 insertions(+), 61 deletions(-) diff --git a/gen/go/headscale/v1/machine.pb.go b/gen/go/headscale/v1/machine.pb.go index 9a5064fe..b25db290 100644 --- a/gen/go/headscale/v1/machine.pb.go +++ b/gen/go/headscale/v1/machine.pb.go @@ -85,13 +85,12 @@ type Machine struct { IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` Namespace *Namespace `protobuf:"bytes,7,opt,name=namespace,proto3" json:"namespace,omitempty"` - Registered bool `protobuf:"varint,8,opt,name=registered,proto3" json:"registered,omitempty"` - RegisterMethod RegisterMethod `protobuf:"varint,9,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"` - Expiry *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=expiry,proto3" json:"expiry,omitempty"` - PreAuthKey *PreAuthKey `protobuf:"bytes,13,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"` + Expiry *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=expiry,proto3" json:"expiry,omitempty"` + PreAuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + RegisterMethod RegisterMethod `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` } func (x *Machine) Reset() { @@ -175,20 +174,6 @@ func (x *Machine) GetNamespace() *Namespace { return nil } -func (x *Machine) GetRegistered() bool { - if x != nil { - return x.Registered - } - return false -} - -func (x *Machine) GetRegisterMethod() RegisterMethod { - if x != nil { - return x.RegisterMethod - } - return RegisterMethod_REGISTER_METHOD_UNSPECIFIED -} - func (x *Machine) GetLastSeen() *timestamppb.Timestamp { if x != nil { return x.LastSeen @@ -224,6 +209,13 @@ func (x *Machine) GetCreatedAt() *timestamppb.Timestamp { return nil } +func (x *Machine) GetRegisterMethod() RegisterMethod { + if x != nil { + return x.RegisterMethod + } + return RegisterMethod_REGISTER_METHOD_UNSPECIFIED +} + type RegisterMachineRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -822,7 +814,7 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{ 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, - 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63, + 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, @@ -836,33 +828,31 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{ 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0f, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, - 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, - 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, + 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, + 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, + 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, @@ -962,12 +952,12 @@ var file_headscale_v1_machine_proto_goTypes = []interface{}{ } var file_headscale_v1_machine_proto_depIdxs = []int32{ 14, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace - 0, // 1: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod - 15, // 2: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp - 15, // 3: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp - 15, // 4: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp - 16, // 5: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey - 15, // 6: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp + 15, // 1: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp + 15, // 2: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp + 15, // 3: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp + 16, // 4: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey + 15, // 5: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp + 0, // 6: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod 1, // 7: headscale.v1.RegisterMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 8: headscale.v1.GetMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 9: headscale.v1.ExpireMachineResponse.machine:type_name -> headscale.v1.Machine diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index db348f41..8d808f98 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -885,12 +885,6 @@ "namespace": { "$ref": "#/definitions/v1Namespace" }, - "registered": { - "type": "boolean" - }, - "registerMethod": { - "$ref": "#/definitions/v1RegisterMethod" - }, "lastSeen": { "type": "string", "format": "date-time" @@ -909,6 +903,9 @@ "createdAt": { "type": "string", "format": "date-time" + }, + "registerMethod": { + "$ref": "#/definitions/v1RegisterMethod" } } }, From 5e92ddad4363815c0b29bd5933ec274db55bbb29 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 22:42:30 +0000 Subject: [PATCH 25/53] Remove redundant caches This commit removes the two extra caches (oidc, requested time) and uses the new central registration cache instead. The requested time is unified into the main machine object and the oidc key is just added to the same cache, as a string with the state as a key instead of machine key. --- api.go | 25 ++++++++++--------------- app.go | 33 ++++++++++++--------------------- app_test.go | 5 ----- grpcv1.go | 17 +---------------- machine.go | 2 -- oidc.go | 29 ++++------------------------- 6 files changed, 27 insertions(+), 84 deletions(-) diff --git a/api.go b/api.go index f831cacf..9b8fafd2 100644 --- a/api.go +++ b/api.go @@ -140,17 +140,25 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { // We create the machine and then keep it around until a callback // happens newMachine := Machine{ - Expiry: &time.Time{}, MachineKey: machineKeyStr, Name: req.Hostinfo.Hostname, NodeKey: NodePublicKeyStripPrefix(req.NodeKey), LastSeen: &now, } + if !req.Expiry.IsZero() { + log.Trace(). + Caller(). + Str("machine", req.Hostinfo.Hostname). + Time("expiry", req.Expiry). + Msg("Non-zero expiry time requested") + newMachine.Expiry = &req.Expiry + } + h.registrationCache.Set( machineKeyStr, newMachine, - requestedExpiryCacheExpiration, + registerCacheExpiration, ) h.handleMachineRegistrationNew(ctx, machineKey, req) @@ -490,19 +498,6 @@ func (h *Headscale) handleMachineRegistrationNew( strings.TrimSuffix(h.cfg.ServerURL, "/"), MachinePublicKeyStripPrefix(machineKey)) } - if !registerRequest.Expiry.IsZero() { - log.Trace(). - Caller(). - Str("machine", registerRequest.Hostinfo.Hostname). - Time("expiry", registerRequest.Expiry). - Msg("Non-zero expiry time requested, adding to cache") - h.requestedExpiryCache.Set( - machineKey.String(), - registerRequest.Expiry, - requestedExpiryCacheExpiration, - ) - } - respBody, err := encode(resp, &machineKey, h.privateKey) if err != nil { log.Error(). diff --git a/app.go b/app.go index 63ca10e4..f7a6e636 100644 --- a/app.go +++ b/app.go @@ -55,8 +55,8 @@ const ( HTTPReadTimeout = 30 * time.Second privateKeyFileMode = 0o600 - requestedExpiryCacheExpiration = time.Minute * 5 - requestedExpiryCacheCleanupInterval = time.Minute * 10 + registerCacheExpiration = time.Minute * 15 + registerCacheCleanup = time.Minute * 20 errUnsupportedDatabase = Error("unsupported DB") errUnsupportedLetsEncryptChallengeType = Error( @@ -148,11 +148,8 @@ type Headscale struct { lastStateChange sync.Map - oidcProvider *oidc.Provider - oauth2Config *oauth2.Config - oidcStateCache *cache.Cache - - requestedExpiryCache *cache.Cache + oidcProvider *oidc.Provider + oauth2Config *oauth2.Config registrationCache *cache.Cache @@ -204,25 +201,19 @@ func NewHeadscale(cfg Config) (*Headscale, error) { return nil, errUnsupportedDatabase } - requestedExpiryCache := cache.New( - requestedExpiryCacheExpiration, - requestedExpiryCacheCleanupInterval, - ) - registrationCache := cache.New( // TODO(kradalby): Add unified cache expiry config options - requestedExpiryCacheExpiration, - requestedExpiryCacheCleanupInterval, + registerCacheExpiration, + registerCacheCleanup, ) app := Headscale{ - cfg: cfg, - dbType: cfg.DBtype, - dbString: dbString, - privateKey: privKey, - aclRules: tailcfg.FilterAllowAll, // default allowall - requestedExpiryCache: requestedExpiryCache, - registrationCache: registrationCache, + cfg: cfg, + dbType: cfg.DBtype, + dbString: dbString, + privateKey: privKey, + aclRules: tailcfg.FilterAllowAll, // default allowall + registrationCache: registrationCache, } err = app.initDB() diff --git a/app_test.go b/app_test.go index 53c703a6..96036a1d 100644 --- a/app_test.go +++ b/app_test.go @@ -5,7 +5,6 @@ import ( "os" "testing" - "github.com/patrickmn/go-cache" "gopkg.in/check.v1" "inet.af/netaddr" ) @@ -50,10 +49,6 @@ func (s *Suite) ResetDB(c *check.C) { cfg: cfg, dbType: "sqlite3", dbString: tmpDir + "/headscale_test.db", - requestedExpiryCache: cache.New( - requestedExpiryCacheExpiration, - requestedExpiryCacheCleanupInterval, - ), } err = app.initDB() if err != nil { diff --git a/grpcv1.go b/grpcv1.go index 75732b1d..1c022905 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -160,25 +160,10 @@ func (api headscaleV1APIServer) RegisterMachine( Str("machine_key", request.GetKey()). Msg("Registering machine") - // TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set - // This means that if a user is to slow with register a machine, it will possibly not - // have the correct expiry. - requestedTime := time.Time{} - if requestedTimeIf, found := api.h.requestedExpiryCache.Get(request.GetKey()); found { - log.Trace(). - Caller(). - Str("machine", request.Key). - Msg("Expiry time found in cache, assigning to node") - if reqTime, ok := requestedTimeIf.(time.Time); ok { - requestedTime = reqTime - } - } - machine, err := api.h.RegisterMachineFromAuthCallback( request.GetKey(), request.GetNamespace(), RegisterMethodCLI, - &requestedTime, ) if err != nil { return nil, err @@ -418,7 +403,7 @@ func (api headscaleV1APIServer) DebugCreateMachine( api.h.registrationCache.Set( request.GetKey(), newMachine, - requestedExpiryCacheExpiration, + registerCacheExpiration, ) return &v1.DebugCreateMachineResponse{Machine: newMachine.toProto()}, nil diff --git a/machine.go b/machine.go index ac1a1c84..03f727a2 100644 --- a/machine.go +++ b/machine.go @@ -683,7 +683,6 @@ func (h *Headscale) RegisterMachineFromAuthCallback( machineKeyStr string, namespaceName string, registrationMethod string, - expiry *time.Time, ) (*Machine, error) { if machineInterface, ok := h.registrationCache.Get(machineKeyStr); ok { if registrationMachine, ok := machineInterface.(Machine); ok { @@ -697,7 +696,6 @@ func (h *Headscale) RegisterMachineFromAuthCallback( registrationMachine.NamespaceID = namespace.ID registrationMachine.RegisterMethod = registrationMethod - registrationMachine.Expiry = expiry machine, err := h.RegisterMachine( registrationMachine, diff --git a/oidc.go b/oidc.go index e745236b..fe69a76f 100644 --- a/oidc.go +++ b/oidc.go @@ -10,20 +10,16 @@ import ( "html/template" "net/http" "strings" - "time" "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" - "github.com/patrickmn/go-cache" "github.com/rs/zerolog/log" "golang.org/x/oauth2" "tailscale.com/types/key" ) const ( - oidcStateCacheExpiration = time.Minute * 5 - oidcStateCacheCleanupInterval = time.Minute * 10 - randomByteSize = 16 + randomByteSize = 16 ) type IDTokenClaims struct { @@ -60,14 +56,6 @@ func (h *Headscale) initOIDC() error { } } - // init the state cache if it hasn't been already - if h.oidcStateCache == nil { - h.oidcStateCache = cache.New( - oidcStateCacheExpiration, - oidcStateCacheCleanupInterval, - ) - } - return nil } @@ -100,7 +88,7 @@ func (h *Headscale) RegisterOIDC(ctx *gin.Context) { stateStr := hex.EncodeToString(randomBlob)[:32] // place the machine key into the state cache, so it can be retrieved later - h.oidcStateCache.Set(stateStr, machineKeyStr, oidcStateCacheExpiration) + h.registrationCache.Set(stateStr, machineKeyStr, registerCacheExpiration) authURL := h.oauth2Config.AuthCodeURL(stateStr) log.Debug().Msgf("Redirecting to %s for authentication", authURL) @@ -196,7 +184,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { } // retrieve machinekey from state cache - machineKeyIf, machineKeyFound := h.oidcStateCache.Get(state) + machineKeyIf, machineKeyFound := h.registrationCache.Get(state) if !machineKeyFound { log.Error(). @@ -228,14 +216,6 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - // TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set - requestedTime := time.Time{} - if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.String()); found { - if reqTime, ok := requestedTimeIf.(time.Time); ok { - requestedTime = reqTime - } - } - // retrieve machine information machine, err := h.GetMachineByMachineKey(machineKey) if err != nil { @@ -254,7 +234,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { Str("machine", machine.Name). Msg("machine already registered, reauthenticating") - h.RefreshMachine(machine, requestedTime) + h.RefreshMachine(machine, *machine.Expiry) var content bytes.Buffer if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ @@ -329,7 +309,6 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { machineKeyStr, namespace.Name, RegisterMethodOIDC, - &requestedTime, ) if err != nil { log.Error(). From 8bef04d8df774d3d816e6f872436e4ec416bf8cc Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 22:45:42 +0000 Subject: [PATCH 26/53] Remove sorted todo --- oidc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/oidc.go b/oidc.go index fe69a76f..93fc1be6 100644 --- a/oidc.go +++ b/oidc.go @@ -112,7 +112,6 @@ var oidcCallbackTemplate = template.Must( `), ) -// TODO: Why is the entire machine registration logic duplicated here? // OIDCCallback handles the callback from the OIDC endpoint // Retrieves the mkey from the state cache and adds the machine to the users email namespace // TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities From 379017602cec416135e8f34b4b407c4099d722c7 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 22:50:35 +0000 Subject: [PATCH 27/53] Reformat and add db backup note --- CHANGELOG.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c23c99..1a2bda31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,31 @@ # CHANGELOG -**0.15.0 (2022-xx-xx):** +## 0.15.0 (2022-xx-xx) -**BREAKING**: +**Note:** Take a backup of your database before upgrading. + +### BREAKING - Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357) - To limit access between nodes, use [ACLs](./docs/acls.md). -**Changes**: +### Changes - Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) - Simplify the code behind registration of machines [#366](https://github.com/juanfont/headscale/pull/366) - Nodes are now only written to database if they are registrated successfully -**0.14.0 (2022-02-24):** +## 0.14.0 (2022-02-24) -**UPCOMING BREAKING**: -From the **next** version (`0.15.0`), all machines will be able to communicate regardless of +**UPCOMING ### BREAKING +From the **next\*\* version (`0.15.0`), all machines will be able to communicate regardless of if they are in the same namespace. This means that the behaviour currently limited to ACLs will become default. From version `0.15.0`, all limitation of communications must be done with ACLs. This is a part of aligning `headscale`'s behaviour with Tailscale's upstream behaviour. -**BREAKING**: +### BREAKING - ACLs have been rewritten to align with the bevaviour Tailscale Control Panel provides. **NOTE:** This is only active if you use ACLs - Namespaces are now treated as Users @@ -31,17 +33,17 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh - Tags should now work correctly and adding a host to Headscale should now reload the rules. - The documentation have a [fictional example](docs/acls.md) that should cover some use cases of the ACLs features -**Features**: +### Features - Add support for configurable mTLS [docs](docs/tls.md#configuring-mutual-tls-authentication-mtls) [#297](https://github.com/juanfont/headscale/pull/297) -**Changes**: +### Changes - Remove dependency on CGO (switch from CGO SQLite to pure Go) [#346](https://github.com/juanfont/headscale/pull/346) **0.13.0 (2022-02-18):** -**Features**: +### Features - Add IPv6 support to the prefix assigned to namespaces - Add API Key support @@ -52,7 +54,7 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh - `oidc.domain_map` option has been removed - `strip_email_domain` option has been added (see [config-example.yaml](./config_example.yaml)) -**Changes**: +### Changes - `ip_prefix` is now superseded by `ip_prefixes` in the configuration [#208](https://github.com/juanfont/headscale/pull/208) - Upgrade `tailscale` (1.20.4) and other dependencies to latest [#314](https://github.com/juanfont/headscale/pull/314) @@ -61,35 +63,35 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh **0.12.4 (2022-01-29):** -**Changes**: +### Changes - Make gRPC Unix Socket permissions configurable [#292](https://github.com/juanfont/headscale/pull/292) - Trim whitespace before reading Private Key from file [#289](https://github.com/juanfont/headscale/pull/289) - Add new command to generate a private key for `headscale` [#290](https://github.com/juanfont/headscale/pull/290) - Fixed issue where hosts deleted from control server may be written back to the database, as long as they are connected to the control server [#278](https://github.com/juanfont/headscale/pull/278) -**0.12.3 (2022-01-13):** +## 0.12.3 (2022-01-13) -**Changes**: +### Changes - Added Alpine container [#270](https://github.com/juanfont/headscale/pull/270) - Minor updates in dependencies [#271](https://github.com/juanfont/headscale/pull/271) -**0.12.2 (2022-01-11):** +## 0.12.2 (2022-01-11) Happy New Year! -**Changes**: +### Changes - Fix Docker release [#258](https://github.com/juanfont/headscale/pull/258) - Rewrite main docs [#262](https://github.com/juanfont/headscale/pull/262) - Improve Docker docs [#263](https://github.com/juanfont/headscale/pull/263) -**0.12.1 (2021-12-24):** +## 0.12.1 (2021-12-24) (We are skipping 0.12.0 to correct a mishap done weeks ago with the version tagging) -**BREAKING**: +### BREAKING - Upgrade to Tailscale 1.18 [#229](https://github.com/juanfont/headscale/pull/229) - This change requires a new format for private key, private keys are now generated automatically: @@ -97,19 +99,19 @@ Happy New Year! 2. Restart `headscale`, a new key will be generated. 3. Restart all Tailscale clients to fetch the new key -**Changes**: +### Changes - Unify configuration example [#197](https://github.com/juanfont/headscale/pull/197) - Add stricter linting and formatting [#223](https://github.com/juanfont/headscale/pull/223) -**Features**: +### Features - Add gRPC and HTTP API (HTTP API is currently disabled) [#204](https://github.com/juanfont/headscale/pull/204) - Use gRPC between the CLI and the server [#206](https://github.com/juanfont/headscale/pull/206), [#212](https://github.com/juanfont/headscale/pull/212) - Beta OpenID Connect support [#126](https://github.com/juanfont/headscale/pull/126), [#227](https://github.com/juanfont/headscale/pull/227) -**0.11.0 (2021-10-25):** +## 0.11.0 (2021-10-25) -**BREAKING**: +### BREAKING - Make headscale fetch DERP map from URL and file [#196](https://github.com/juanfont/headscale/pull/196) From 82cb6b9ddce6d0f2e7b3539313378d301b54e671 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 23:00:41 +0000 Subject: [PATCH 28/53] Cleanup some unreachable code --- machine.go | 23 ----------------------- oidc.go | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/machine.go b/machine.go index 03f727a2..1560858b 100644 --- a/machine.go +++ b/machine.go @@ -718,25 +718,6 @@ func (h *Headscale) RegisterMachine(machine Machine, Str("machine_key", machine.MachineKey). Msg("Registering machine") - // If the machine is already in the database, it is seeking - // reauthentication, and by reaching this step, has been authenticated - // and need to have an updated expiry. - var machineKey key.MachinePublic - _ = machineKey.UnmarshalText( - []byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)), - ) - machineFromDatabase, _ := h.GetMachineByMachineKey(machineKey) - if machineFromDatabase != nil { - log.Trace(). - Caller(). - Str("machine", machine.Name). - Msg("machine already registered, reauthenticating") - - h.RefreshMachine(machineFromDatabase, *machine.Expiry) - - return machineFromDatabase, nil - } - log.Trace(). Caller(). Str("machine", machine.Name). @@ -758,10 +739,6 @@ func (h *Headscale) RegisterMachine(machine Machine, machine.IPAddresses = ips - // TODO(kradalby): This field is uneccessary metadata, - // move it to tags instead of having a column. - // machine.RegisterMethod = registrationMethod - h.db.Save(&machine) log.Trace(). diff --git a/oidc.go b/oidc.go index 93fc1be6..8e89e27f 100644 --- a/oidc.go +++ b/oidc.go @@ -215,7 +215,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - // retrieve machine information + // retrieve machine information if it exist machine, err := h.GetMachineByMachineKey(machineKey) if err != nil { log.Error().Msg("machine key not found in database") From 7c63412df53f2db419ddfcb66a6add530109a3c6 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 28 Feb 2022 23:02:41 +0000 Subject: [PATCH 29/53] Remove todo --- app.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app.go b/app.go index f7a6e636..58185093 100644 --- a/app.go +++ b/app.go @@ -202,7 +202,6 @@ func NewHeadscale(cfg Config) (*Headscale, error) { } registrationCache := cache.New( - // TODO(kradalby): Add unified cache expiry config options registerCacheExpiration, registerCacheCleanup, ) From 5157f356cb5e1fdd83c29f7d6050450b9635d182 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 1 Mar 2022 07:30:35 +0000 Subject: [PATCH 30/53] Fix apple profile issue being generated with escaped characters --- apple_mobileconfig.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apple_mobileconfig.go b/apple_mobileconfig.go index e5d9eede..69f61a63 100644 --- a/apple_mobileconfig.go +++ b/apple_mobileconfig.go @@ -4,6 +4,7 @@ import ( "bytes" "html/template" "net/http" + textTemplate "text/template" "github.com/gin-gonic/gin" "github.com/gofrs/uuid" @@ -30,7 +31,7 @@ func (h *Headscale) AppleMobileConfig(ctx *gin.Context) {

curl {{.Url}}/apple/ios

-->

curl {{.Url}}/apple/macos

- +

Profiles

- +

macOS

Headscale can be set to the default server by installing a Headscale configuration profile:

@@ -58,7 +59,7 @@ func (h *Headscale) AppleMobileConfig(ctx *gin.Context) { defaults write io.tailscale.ipn.macos ControlURL {{.URL}}

Restart Tailscale.app and log in.

- + `)) @@ -202,8 +203,8 @@ type AppleMobilePlatformConfig struct { URL string } -var commonTemplate = template.Must( - template.New("mobileconfig").Parse(` +var commonTemplate = textTemplate.Must( + textTemplate.New("mobileconfig").Parse(` @@ -229,7 +230,7 @@ var commonTemplate = template.Must( `), ) -var iosTemplate = template.Must(template.New("iosTemplate").Parse(` +var iosTemplate = textTemplate.Must(textTemplate.New("iosTemplate").Parse(` PayloadType io.tailscale.ipn.ios From 1cb39d914cf4ff5c4a451f3479746a39b71580a0 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 1 Mar 2022 07:35:17 +0000 Subject: [PATCH 31/53] Update dependencies --- go.mod | 26 +++++++++++++------------- go.sum | 35 +++++++++++++++++++++++++++++++++++ machine.go | 2 +- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 8b721fa7..04ce9274 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,12 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3 github.com/infobloxopen/protoc-gen-gorm v1.1.0 - github.com/klauspost/compress v1.14.2 + github.com/klauspost/compress v1.14.4 github.com/ory/dockertest/v3 v3.8.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/philip-bui/grpc-zerolog v1.0.1 github.com/prometheus/client_golang v1.12.1 - github.com/pterm/pterm v0.12.36 + github.com/pterm/pterm v0.12.37 github.com/rs/zerolog v1.26.1 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 @@ -27,9 +27,9 @@ require ( github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e github.com/zsais/go-gin-prometheus v0.1.0 golang.org/x/crypto v0.0.0-20220214200702-86341886e292 - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 + google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 google.golang.org/grpc v1.44.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 google.golang.org/protobuf v1.27.1 @@ -39,12 +39,12 @@ require ( gorm.io/driver/postgres v1.3.1 gorm.io/gorm v1.23.1 inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 - tailscale.com v1.20.4 + tailscale.com v1.22.0 ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/atomicgo/cursor v0.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -58,7 +58,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/glebarez/go-sqlite v1.14.7 // indirect + github.com/glebarez/go-sqlite v1.14.8 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.10.0 // indirect @@ -92,7 +92,7 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.3 // indirect - github.com/magiconair/properties v1.8.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -121,7 +121,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect - github.com/ugorji/go/codec v1.2.6 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -129,8 +129,8 @@ require ( go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect + golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -140,9 +140,9 @@ require ( gorm.io/driver/mysql v1.3.2 // indirect gorm.io/driver/sqlite v1.3.1 // indirect gorm.io/driver/sqlserver v1.3.1 // indirect - modernc.org/libc v1.14.3 // indirect + modernc.org/libc v1.14.5 // indirect modernc.org/mathutil v1.4.1 // indirect modernc.org/memory v1.0.5 // indirect - modernc.org/sqlite v1.14.5 // indirect + modernc.org/sqlite v1.14.7 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 20eec3f4..e35d6360 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,8 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -109,6 +111,7 @@ github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuD github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -214,6 +217,8 @@ github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/glebarez/go-sqlite v1.14.7 h1:eXrKp59O5eWBfxv2Xfq5d7uex4+clKrOtWfMzzGSkoM= github.com/glebarez/go-sqlite v1.14.7/go.mod h1:TKAw5tjyB/ocvVht7Xv4772qRAun5CG/xLCEbkDwNUc= +github.com/glebarez/go-sqlite v1.14.8 h1:30RsIS/olgfOMr7SxiCaYhpq50BTteA/CUKaWVOOHYg= +github.com/glebarez/go-sqlite v1.14.8/go.mod h1:gf9QVsKCYMcu+7nd+ZbDqvXnEXEb22qLcqRUQ9XEI34= github.com/glebarez/sqlite v1.3.5 h1:R9op5nxb9Z10t4VXQSdAVyqRalLhWdLrlaT/iuvOGHI= github.com/glebarez/sqlite v1.3.5/go.mod h1:ZffEtp/afVhV+jvIzQi8wlYEIkuGAYshr9OPKM/NmQc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -355,6 +360,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0/go.mod h1:d2gYTOTUQklu06xp0AJYYmRdTVU1VKrqhkYfYag2L08= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= @@ -499,6 +505,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -537,6 +545,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -673,6 +683,8 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36 h1:Ui5zZj7xA8lXR0CxWXlKGCQMW1cZVUMOS8jEXs6ur/g= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.37 h1:QGOyuaDUmY3yTbP0k6i0uPNqNHA9YofEBQDy0tIyKTA= +github.com/pterm/pterm v0.12.37/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -763,9 +775,13 @@ github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vF github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -942,6 +958,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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= @@ -961,6 +979,8 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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= @@ -1063,9 +1083,12 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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= @@ -1276,6 +1299,8 @@ google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 h1:RK2ysGpQApbI6U7xn+ROT2rrm08lE/t8AcGqG8XI1CY= google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I= +google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1450,7 +1475,10 @@ modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6 modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= +modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= +modernc.org/ccgo/v3 v3.15.13/go.mod h1:QHtvdpeODlXjdK3tsbpyK+7U9JV4PQsrPGIbtmc0KfY= modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/ccorpus v1.11.4/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= @@ -1496,6 +1524,8 @@ modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= modernc.org/libc v1.14.3 h1:ruQJ8VDhnWkUR/otUG/Ksw+sWHUw9cPAq6mjDaY/Y7c= modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= +modernc.org/libc v1.14.5 h1:DAHvwGoVRDZs5iJXnX9RJrgXSsorupCWmJ2ac964Owk= +modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= @@ -1507,8 +1537,11 @@ modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.14.5 h1:bYrrjwH9Y7QUGk1MbchZDhRfmpGuEAs/D45sVjNbfvs= modernc.org/sqlite v1.14.5/go.mod h1:YyX5Rx0WbXokitdWl2GJIDy4BrPxBP0PwwhpXOHCDLE= +modernc.org/sqlite v1.14.7 h1:A+6rGjtRQbt9SORXfV+hUyXOP3mDf7J5uz+EES/CNPE= +modernc.org/sqlite v1.14.7/go.mod h1:yiCvMv3HblGmzENNIaNtFhfaNIwcla4u2JQEwJPzfEc= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/tcl v1.10.0/go.mod h1:WzWapmP/7dHVhFoyPpEaNSVTL8xtewhouN/cqSJ5A2s= +modernc.org/tcl v1.11.0/go.mod h1:zsTUpbQ+NxQEjOjCUlImDLPv1sG8Ww0qp66ZvyOxCgw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.2.21/go.mod h1:uXrObx4pGqXWIMliC5MiKuwAyMrltzwpteOFUP1PWCc= modernc.org/z v1.3.0/go.mod h1:+mvgLH814oDjtATDdT3rs84JnUIpkvAF5B8AVkNlE2g= @@ -1519,3 +1552,5 @@ sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= tailscale.com v1.20.4 h1:7cl/Q2Sbo2Jb2dX7zA+Exbbl7DT5UGZ4iGhQ2xj23X0= tailscale.com v1.20.4/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= +tailscale.com v1.22.0 h1:/a1f6eKEl9vL/wGFP8mkhe7O1zDRGtWa9Ft2rGp5N80= +tailscale.com v1.22.0/go.mod h1:D2zuDnjHT7v4aCt71c4+ytQUUAGpnypW+DoubYLaHjg= diff --git a/machine.go b/machine.go index dac44591..3106d0b2 100644 --- a/machine.go +++ b/machine.go @@ -630,7 +630,7 @@ func (machine Machine) toNode( Endpoints: endpoints, DERP: derp, - Hostinfo: hostinfo, + Hostinfo: hostinfo.View(), Created: machine.CreatedAt, LastSeen: machine.LastSeen, From 8a95fe517a433d5f6df938a5d1f091e996d84d5e Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 1 Mar 2022 16:31:25 +0000 Subject: [PATCH 32/53] Use specific types for all fields on machine (no datatypes.json) This commit removes the need for datatypes.JSON and makes the code a bit cleaner by allowing us to use proper types throughout the code when it comes to hostinfo and other datatypes on the machine object. This allows us to remove alot of unmarshal/marshal operations and remove a lot of obsolete error checks. This following commits will clean away a lot of untyped data and uneccessary error checks. --- db.go | 76 ++++++++++++++++++++++++++++ go.mod | 7 +-- go.sum | 47 ----------------- machine.go | 144 +++++++++-------------------------------------------- 4 files changed, 101 insertions(+), 173 deletions(-) diff --git a/db.go b/db.go index 8db7bc69..5906402d 100644 --- a/db.go +++ b/db.go @@ -1,13 +1,18 @@ package headscale import ( + "database/sql/driver" + "encoding/json" "errors" + "fmt" "time" "github.com/glebarez/sqlite" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" + "inet.af/netaddr" + "tailscale.com/tailcfg" ) const ( @@ -141,3 +146,74 @@ func (h *Headscale) setValue(key string, value string) error { return nil } + +// This is a "wrapper" type around tailscales +// Hostinfo to allow us to add database "serialization" +// methods. This allows us to use a typed values throughout +// the code and not have to marshal/unmarshal and error +// check all over the code. +type HostInfo tailcfg.Hostinfo + +func (hi *HostInfo) Scan(destination interface{}) error { + switch value := destination.(type) { + case []byte: + return json.Unmarshal(value, hi) + + case string: + return json.Unmarshal([]byte(value), hi) + + default: + return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) + } +} + +// Value return json value, implement driver.Valuer interface. +func (hi HostInfo) Value() (driver.Value, error) { + bytes, err := json.Marshal(hi) + + return string(bytes), err +} + +type IPPrefixes []netaddr.IPPrefix + +func (i *IPPrefixes) Scan(destination interface{}) error { + switch value := destination.(type) { + case []byte: + return json.Unmarshal(value, i) + + case string: + return json.Unmarshal([]byte(value), i) + + default: + return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) + } +} + +// Value return json value, implement driver.Valuer interface. +func (i IPPrefixes) Value() (driver.Value, error) { + bytes, err := json.Marshal(i) + + return string(bytes), err +} + +type StringList []string + +func (i *StringList) Scan(destination interface{}) error { + switch value := destination.(type) { + case []byte: + return json.Unmarshal(value, i) + + case string: + return json.Unmarshal([]byte(value), i) + + default: + return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) + } +} + +// Value return json value, implement driver.Valuer interface. +func (i StringList) Value() (driver.Value, error) { + bytes, err := json.Marshal(i) + + return string(bytes), err +} diff --git a/go.mod b/go.mod index 04ce9274..a121826d 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( google.golang.org/protobuf v1.27.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/yaml.v2 v2.4.0 - gorm.io/datatypes v1.0.5 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b gorm.io/driver/postgres v1.3.1 gorm.io/gorm v1.23.1 inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 @@ -52,6 +52,7 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/containerd/continuity v0.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/denisenkom/go-mssqldb v0.12.0 // indirect github.com/docker/cli v20.10.12+incompatible // indirect github.com/docker/docker v20.10.12+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -136,10 +137,6 @@ require ( google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - gorm.io/driver/mysql v1.3.2 // indirect - gorm.io/driver/sqlite v1.3.1 // indirect - gorm.io/driver/sqlserver v1.3.1 // indirect modernc.org/libc v1.14.5 // indirect modernc.org/mathutil v1.4.1 // indirect modernc.org/memory v1.0.5 // indirect diff --git a/go.sum b/go.sum index e35d6360..558d3d7c 100644 --- a/go.sum +++ b/go.sum @@ -73,7 +73,6 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= @@ -111,7 +110,6 @@ github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuD github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -215,7 +213,6 @@ 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.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/glebarez/go-sqlite v1.14.7 h1:eXrKp59O5eWBfxv2Xfq5d7uex4+clKrOtWfMzzGSkoM= github.com/glebarez/go-sqlite v1.14.7/go.mod h1:TKAw5tjyB/ocvVht7Xv4772qRAun5CG/xLCEbkDwNUc= github.com/glebarez/go-sqlite v1.14.8 h1:30RsIS/olgfOMr7SxiCaYhpq50BTteA/CUKaWVOOHYg= github.com/glebarez/go-sqlite v1.14.8/go.mod h1:gf9QVsKCYMcu+7nd+ZbDqvXnEXEb22qLcqRUQ9XEI34= @@ -360,7 +357,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0/go.mod h1:d2gYTOTUQklu06xp0AJYYmRdTVU1VKrqhkYfYag2L08= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= @@ -418,7 +414,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/infobloxopen/atlas-app-toolkit v0.24.1-0.20210416193901-4c7518b07e08/go.mod h1:9BTHnpff654rY1J8KxSUOLJ+ZUDn2Vi3mmk26gQDo1M= github.com/infobloxopen/protoc-gen-gorm v1.1.0 h1:l6JKEkqMTFbtoGIfQmh/aOy7KfljgX4ql772LtG4kas= github.com/infobloxopen/protoc-gen-gorm v1.1.0/go.mod h1:ohzLmmFMWQztw2RBHunfjKSCjTPUW4JvbgU1Mdazwxg= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -440,7 +435,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -456,7 +450,6 @@ github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01C github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= @@ -464,7 +457,6 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8= github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= @@ -480,8 +472,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -503,8 +493,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW 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.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4= github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= @@ -543,7 +531,6 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8 github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -568,7 +555,6 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ= github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -681,8 +667,6 @@ github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= -github.com/pterm/pterm v0.12.36 h1:Ui5zZj7xA8lXR0CxWXlKGCQMW1cZVUMOS8jEXs6ur/g= -github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.37 h1:QGOyuaDUmY3yTbP0k6i0uPNqNHA9YofEBQDy0tIyKTA= github.com/pterm/pterm v0.12.37/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= @@ -774,12 +758,8 @@ github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9 github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -861,8 +841,6 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 h1:XdAboW3BNMv9ocSCOk/u1MFioZGzCNkiJZ19v9Oe3Ig= -golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -956,7 +934,6 @@ golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -977,7 +954,6 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= @@ -1085,8 +1061,6 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1297,8 +1271,6 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 h1:RK2ysGpQApbI6U7xn+ROT2rrm08lE/t8AcGqG8XI1CY= -google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I= google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1384,23 +1356,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/datatypes v1.0.5 h1:3vHCfg4Bz8SDx83zE+ASskF+g/j0kWrcKrY9jFUyAl0= -gorm.io/datatypes v1.0.5/go.mod h1:acG/OHGwod+1KrbwPL1t+aavb7jOBOETeyl5M8K5VQs= -gorm.io/driver/mysql v1.2.2/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo= -gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y= -gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo= -gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I= -gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= -gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To= -gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs= gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g= gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= -gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk= -gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg= -gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI= -gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ= -gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk= gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0= gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= @@ -1522,7 +1479,6 @@ modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= -modernc.org/libc v1.14.3 h1:ruQJ8VDhnWkUR/otUG/Ksw+sWHUw9cPAq6mjDaY/Y7c= modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= modernc.org/libc v1.14.5 h1:DAHvwGoVRDZs5iJXnX9RJrgXSsorupCWmJ2ac964Owk= modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= @@ -1535,7 +1491,6 @@ modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.14.5 h1:bYrrjwH9Y7QUGk1MbchZDhRfmpGuEAs/D45sVjNbfvs= modernc.org/sqlite v1.14.5/go.mod h1:YyX5Rx0WbXokitdWl2GJIDy4BrPxBP0PwwhpXOHCDLE= modernc.org/sqlite v1.14.7 h1:A+6rGjtRQbt9SORXfV+hUyXOP3mDf7J5uz+EES/CNPE= modernc.org/sqlite v1.14.7/go.mod h1:yiCvMv3HblGmzENNIaNtFhfaNIwcla4u2JQEwJPzfEc= @@ -1550,7 +1505,5 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -tailscale.com v1.20.4 h1:7cl/Q2Sbo2Jb2dX7zA+Exbbl7DT5UGZ4iGhQ2xj23X0= -tailscale.com v1.20.4/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= tailscale.com v1.22.0 h1:/a1f6eKEl9vL/wGFP8mkhe7O1zDRGtWa9Ft2rGp5N80= tailscale.com v1.22.0/go.mod h1:D2zuDnjHT7v4aCt71c4+ytQUUAGpnypW+DoubYLaHjg= diff --git a/machine.go b/machine.go index 3106d0b2..b53bd10e 100644 --- a/machine.go +++ b/machine.go @@ -2,7 +2,6 @@ package headscale import ( "database/sql/driver" - "encoding/json" "fmt" "sort" "strconv" @@ -13,7 +12,6 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/datatypes" "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/key" @@ -51,9 +49,9 @@ type Machine struct { LastSuccessfulUpdate *time.Time Expiry *time.Time - HostInfo datatypes.JSON - Endpoints datatypes.JSON - EnabledRoutes datatypes.JSON + HostInfo HostInfo + Endpoints StringList + EnabledRoutes IPPrefixes CreatedAt time.Time UpdatedAt time.Time @@ -393,20 +391,8 @@ func (h *Headscale) HardDeleteMachine(machine *Machine) error { } // GetHostInfo returns a Hostinfo struct for the machine. -func (machine *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { - hostinfo := tailcfg.Hostinfo{} - if len(machine.HostInfo) != 0 { - hi, err := machine.HostInfo.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(hi, &hostinfo) - if err != nil { - return nil, err - } - } - - return &hostinfo, nil +func (machine *Machine) GetHostInfo() tailcfg.Hostinfo { + return tailcfg.Hostinfo(machine.HostInfo) } func (h *Headscale) isOutdated(machine *Machine) bool { @@ -536,54 +522,12 @@ func (machine Machine) toNode( // TODO(kradalby): Needs investigation, We probably dont need this condition // now that we dont have shared nodes if includeRoutes { - routesStr := []string{} - if len(machine.EnabledRoutes) != 0 { - allwIps, err := machine.EnabledRoutes.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(allwIps, &routesStr) - if err != nil { - return nil, err - } - } - - for _, routeStr := range routesStr { - ip, err := netaddr.ParseIPPrefix(routeStr) - if err != nil { - return nil, err - } - allowedIPs = append(allowedIPs, ip) - } - } - - endpoints := []string{} - if len(machine.Endpoints) != 0 { - be, err := machine.Endpoints.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(be, &endpoints) - if err != nil { - return nil, err - } - } - - hostinfo := tailcfg.Hostinfo{} - if len(machine.HostInfo) != 0 { - hi, err := machine.HostInfo.MarshalJSON() - if err != nil { - return nil, err - } - err = json.Unmarshal(hi, &hostinfo) - if err != nil { - return nil, err - } + allowedIPs = append(allowedIPs, machine.EnabledRoutes...) } var derp string - if hostinfo.NetInfo != nil { - derp = fmt.Sprintf("127.3.3.40:%d", hostinfo.NetInfo.PreferredDERP) + if machine.HostInfo.NetInfo != nil { + derp = fmt.Sprintf("127.3.3.40:%d", machine.HostInfo.NetInfo.PreferredDERP) } else { derp = "127.3.3.40:0" // Zero means disconnected or unknown. } @@ -614,6 +558,8 @@ func (machine Machine) toNode( hostname = machine.Name } + hostInfo := machine.GetHostInfo() + node := tailcfg.Node{ ID: tailcfg.NodeID(machine.ID), // this is the actual ID StableID: tailcfg.StableNodeID( @@ -627,10 +573,10 @@ func (machine Machine) toNode( DiscoKey: discoKey, Addresses: addrs, AllowedIPs: allowedIPs, - Endpoints: endpoints, + Endpoints: machine.Endpoints, DERP: derp, - Hostinfo: hostinfo.View(), + Hostinfo: hostInfo.View(), Created: machine.CreatedAt, LastSeen: machine.LastSeen, @@ -786,37 +732,12 @@ func (h *Headscale) RegisterMachine( return machine, nil } -func (machine *Machine) GetAdvertisedRoutes() ([]netaddr.IPPrefix, error) { - hostInfo, err := machine.GetHostInfo() - if err != nil { - return nil, err - } - - return hostInfo.RoutableIPs, nil +func (machine *Machine) GetAdvertisedRoutes() []netaddr.IPPrefix { + return machine.HostInfo.RoutableIPs } -func (machine *Machine) GetEnabledRoutes() ([]netaddr.IPPrefix, error) { - data, err := machine.EnabledRoutes.MarshalJSON() - if err != nil { - return nil, err - } - - routesStr := []string{} - err = json.Unmarshal(data, &routesStr) - if err != nil { - return nil, err - } - - routes := make([]netaddr.IPPrefix, len(routesStr)) - for index, routeStr := range routesStr { - route, err := netaddr.ParseIPPrefix(routeStr) - if err != nil { - return nil, err - } - routes[index] = route - } - - return routes, nil +func (machine *Machine) GetEnabledRoutes() []netaddr.IPPrefix { + return machine.EnabledRoutes } func (machine *Machine) IsRoutesEnabled(routeStr string) bool { @@ -825,10 +746,7 @@ func (machine *Machine) IsRoutesEnabled(routeStr string) bool { return false } - enabledRoutes, err := machine.GetEnabledRoutes() - if err != nil { - return false - } + enabledRoutes := machine.GetEnabledRoutes() for _, enabledRoute := range enabledRoutes { if route == enabledRoute { @@ -852,13 +770,8 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error { newRoutes[index] = route } - availableRoutes, err := machine.GetAdvertisedRoutes() - if err != nil { - return err - } - for _, newRoute := range newRoutes { - if !containsIPPrefix(availableRoutes, newRoute) { + if !containsIPPrefix(machine.GetAdvertisedRoutes(), newRoute) { return fmt.Errorf( "route (%s) is not available on node %s: %w", machine.Name, @@ -867,30 +780,19 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error { } } - routes, err := json.Marshal(newRoutes) - if err != nil { - return err - } - - machine.EnabledRoutes = datatypes.JSON(routes) + machine.EnabledRoutes = newRoutes h.db.Save(&machine) return nil } -func (machine *Machine) RoutesToProto() (*v1.Routes, error) { - availableRoutes, err := machine.GetAdvertisedRoutes() - if err != nil { - return nil, err - } +func (machine *Machine) RoutesToProto() *v1.Routes { + availableRoutes := machine.GetAdvertisedRoutes() - enabledRoutes, err := machine.GetEnabledRoutes() - if err != nil { - return nil, err - } + enabledRoutes := machine.GetEnabledRoutes() return &v1.Routes{ AdvertisedRoutes: ipPrefixToString(availableRoutes), EnabledRoutes: ipPrefixToString(enabledRoutes), - }, nil + } } From 6477e6a5837b90ba2eb6002ae1137f8ded62042c Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 1 Mar 2022 16:34:24 +0000 Subject: [PATCH 33/53] Use new machine types --- acls.go | 17 ++--------------- grpcv1.go | 25 +++---------------------- poll.go | 21 ++------------------- routes.go | 39 +++------------------------------------ 4 files changed, 10 insertions(+), 92 deletions(-) diff --git a/acls.go b/acls.go index 1a597c24..194da12f 100644 --- a/acls.go +++ b/acls.go @@ -247,13 +247,7 @@ func expandAlias( for _, namespace := range owners { machines := filterMachinesByNamespace(machines, namespace) for _, machine := range machines { - if len(machine.HostInfo) == 0 { - continue - } - hi, err := machine.GetHostInfo() - if err != nil { - return ips, err - } + hi := machine.GetHostInfo() for _, t := range hi.RequestTags { if alias == t { ips = append(ips, machine.IPAddresses.ToStringSlice()...) @@ -315,15 +309,8 @@ func excludeCorrectlyTaggedNodes( } // for each machine if tag is in tags list, don't append it. for _, machine := range nodes { - if len(machine.HostInfo) == 0 { - out = append(out, machine) + hi := machine.GetHostInfo() - continue - } - hi, err := machine.GetHostInfo() - if err != nil { - return out, err - } found := false for _, t := range hi.RequestTags { if containsString(tags, t) { diff --git a/grpcv1.go b/grpcv1.go index 60e181df..bbda19fd 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -3,12 +3,10 @@ package headscale import ( "context" - "encoding/json" "time" "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" - "gorm.io/datatypes" "tailscale.com/tailcfg" ) @@ -262,13 +260,8 @@ func (api headscaleV1APIServer) GetMachineRoute( return nil, err } - routes, err := machine.RoutesToProto() - if err != nil { - return nil, err - } - return &v1.GetMachineRouteResponse{ - Routes: routes, + Routes: machine.RoutesToProto(), }, nil } @@ -286,13 +279,8 @@ func (api headscaleV1APIServer) EnableMachineRoutes( return nil, err } - routes, err := machine.RoutesToProto() - if err != nil { - return nil, err - } - return &v1.EnableMachineRoutesResponse{ - Routes: routes, + Routes: machine.RoutesToProto(), }, nil } @@ -379,13 +367,6 @@ func (api headscaleV1APIServer) DebugCreateMachine( Hostname: "DebugTestMachine", } - log.Trace().Caller().Interface("hostinfo", hostinfo).Msg("") - - hostinfoJson, err := json.Marshal(hostinfo) - if err != nil { - return nil, err - } - newMachine := Machine{ MachineKey: request.GetKey(), Name: request.GetName(), @@ -395,7 +376,7 @@ func (api headscaleV1APIServer) DebugCreateMachine( LastSeen: &time.Time{}, LastSuccessfulUpdate: &time.Time{}, - HostInfo: datatypes.JSON(hostinfoJson), + HostInfo: HostInfo(hostinfo), } // log.Trace().Caller().Interface("machine", newMachine).Msg("") diff --git a/poll.go b/poll.go index 21aa3b33..1ae8cd0d 100644 --- a/poll.go +++ b/poll.go @@ -2,7 +2,6 @@ package headscale import ( "context" - "encoding/json" "errors" "fmt" "io" @@ -11,7 +10,6 @@ import ( "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" - "gorm.io/datatypes" "gorm.io/gorm" "tailscale.com/tailcfg" "tailscale.com/types/key" @@ -85,12 +83,8 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("machine", machine.Name). Msg("Found machine in database") - hostinfo, err := json.Marshal(req.Hostinfo) - if err != nil { - return - } machine.Name = req.Hostinfo.Hostname - machine.HostInfo = datatypes.JSON(hostinfo) + machine.HostInfo = HostInfo(*req.Hostinfo) machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey) now := time.Now().UTC() @@ -114,18 +108,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { // The intended use is for clients to discover the DERP map at start-up // before their first real endpoint update. if !req.ReadOnly { - endpoints, err := json.Marshal(req.Endpoints) - if err != nil { - log.Error(). - Caller(). - Str("func", "PollNetMapHandler"). - Err(err). - Msg("Failed to mashal requested endpoints for the client") - ctx.String(http.StatusInternalServerError, ":(") - - return - } - machine.Endpoints = datatypes.JSON(endpoints) + machine.Endpoints = req.Endpoints machine.LastSeen = &now } h.db.Updates(machine) diff --git a/routes.go b/routes.go index 0065a03f..e8de299b 100644 --- a/routes.go +++ b/routes.go @@ -1,9 +1,6 @@ package headscale import ( - "encoding/json" - - "gorm.io/datatypes" "inet.af/netaddr" ) @@ -23,12 +20,7 @@ func (h *Headscale) GetAdvertisedNodeRoutes( return nil, err } - hostInfo, err := machine.GetHostInfo() - if err != nil { - return nil, err - } - - return &hostInfo.RoutableIPs, nil + return &machine.HostInfo.RoutableIPs, nil } // Deprecated: use machine function instead @@ -43,27 +35,7 @@ func (h *Headscale) GetEnabledNodeRoutes( return nil, err } - data, err := machine.EnabledRoutes.MarshalJSON() - if err != nil { - return nil, err - } - - routesStr := []string{} - err = json.Unmarshal(data, &routesStr) - if err != nil { - return nil, err - } - - routes := make([]netaddr.IPPrefix, len(routesStr)) - for index, routeStr := range routesStr { - route, err := netaddr.ParseIPPrefix(routeStr) - if err != nil { - return nil, err - } - routes[index] = route - } - - return routes, nil + return machine.EnabledRoutes, nil } // Deprecated: use machine function instead @@ -135,12 +107,7 @@ func (h *Headscale) EnableNodeRoute( return errRouteIsNotAvailable } - routes, err := json.Marshal(enabledRoutes) - if err != nil { - return err - } - - machine.EnabledRoutes = datatypes.JSON(routes) + machine.EnabledRoutes = enabledRoutes h.db.Save(&machine) return nil From 49cd761bf62851a62f4f61b2e610b7f66cef0059 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 1 Mar 2022 16:34:35 +0000 Subject: [PATCH 34/53] Use new machine types in tests --- acls_test.go | 132 ++++++++++++++++++++++++++++++------------------ cli_test.go | 3 -- machine_test.go | 16 +----- routes_test.go | 11 +--- 4 files changed, 87 insertions(+), 75 deletions(-) diff --git a/acls_test.go b/acls_test.go index 9f0432a7..b06255aa 100644 --- a/acls_test.go +++ b/acls_test.go @@ -6,7 +6,6 @@ import ( "testing" "gopkg.in/check.v1" - "gorm.io/datatypes" "inet.af/netaddr" "tailscale.com/tailcfg" ) @@ -108,9 +107,12 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) { _, err = app.GetMachine("user1", "testmachine") c.Assert(err, check.NotNil) - hostInfo := []byte( - "{\"OS\":\"centos\",\"Hostname\":\"testmachine\",\"RequestTags\":[\"tag:test\"]}", - ) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "testmachine", + RequestTags: []string{"tag:test"}, + } + machine := Machine{ ID: 0, MachineKey: "foo", @@ -122,7 +124,7 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostInfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) @@ -152,9 +154,12 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) { _, err = app.GetMachine("user1", "testmachine") c.Assert(err, check.NotNil) - hostInfo := []byte( - "{\"OS\":\"centos\",\"Hostname\":\"testmachine\",\"RequestTags\":[\"tag:test\"]}", - ) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "testmachine", + RequestTags: []string{"tag:test"}, + } + machine := Machine{ ID: 1, MachineKey: "12345", @@ -166,7 +171,7 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostInfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) @@ -196,9 +201,12 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { _, err = app.GetMachine("user1", "testmachine") c.Assert(err, check.NotNil) - hostInfo := []byte( - "{\"OS\":\"centos\",\"Hostname\":\"testmachine\",\"RequestTags\":[\"tag:foo\"]}", - ) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "testmachine", + RequestTags: []string{"tag:foo"}, + } + machine := Machine{ ID: 1, MachineKey: "12345", @@ -210,7 +218,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostInfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) @@ -239,9 +247,12 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { _, err = app.GetMachine("user1", "webserver") c.Assert(err, check.NotNil) - hostInfo := []byte( - "{\"OS\":\"centos\",\"Hostname\":\"webserver\",\"RequestTags\":[\"tag:webapp\"]}", - ) + hostInfo := tailcfg.Hostinfo{ + OS: "centos", + Hostname: "webserver", + RequestTags: []string{"tag:webapp"}, + } + machine := Machine{ ID: 1, MachineKey: "12345", @@ -253,11 +264,14 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostInfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) _, err = app.GetMachine("user1", "user") - hostInfo = []byte("{\"OS\":\"debian\",\"Hostname\":\"user\"}") + hostInfo2 := tailcfg.Hostinfo{ + OS: "debian", + Hostname: "Hostname", + } c.Assert(err, check.NotNil) machine = Machine{ ID: 2, @@ -270,7 +284,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostInfo), + HostInfo: HostInfo(hostInfo2), } app.db.Save(&machine) @@ -872,18 +886,22 @@ func Test_expandAlias(t *testing.T) { netaddr.MustParseIP("100.64.0.1"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:hr-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:hr-webserver"}, + }, }, { IPAddresses: MachineAddresses{ netaddr.MustParseIP("100.64.0.2"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:hr-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:hr-webserver"}, + }, }, { IPAddresses: MachineAddresses{ @@ -955,18 +973,22 @@ func Test_expandAlias(t *testing.T) { netaddr.MustParseIP("100.64.0.1"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, }, { IPAddresses: MachineAddresses{ netaddr.MustParseIP("100.64.0.2"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, }, { IPAddresses: MachineAddresses{ @@ -1032,18 +1054,22 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { netaddr.MustParseIP("100.64.0.1"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, }, { IPAddresses: MachineAddresses{ netaddr.MustParseIP("100.64.0.2"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "foo", + RequestTags: []string{"tag:accountant-webserver"}, + }, }, { IPAddresses: MachineAddresses{ @@ -1074,18 +1100,22 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { netaddr.MustParseIP("100.64.0.1"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"hr-web1\",\"RequestTags\":[\"tag:hr-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web1", + RequestTags: []string{"tag:hr-webserver"}, + }, }, { IPAddresses: MachineAddresses{ netaddr.MustParseIP("100.64.0.2"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"hr-web2\",\"RequestTags\":[\"tag:hr-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web2", + RequestTags: []string{"tag:hr-webserver"}, + }, }, { IPAddresses: MachineAddresses{ @@ -1102,18 +1132,22 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { netaddr.MustParseIP("100.64.0.1"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"hr-web1\",\"RequestTags\":[\"tag:hr-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web1", + RequestTags: []string{"tag:hr-webserver"}, + }, }, { IPAddresses: MachineAddresses{ netaddr.MustParseIP("100.64.0.2"), }, Namespace: Namespace{Name: "joe"}, - HostInfo: []byte( - "{\"OS\":\"centos\",\"Hostname\":\"hr-web2\",\"RequestTags\":[\"tag:hr-webserver\"]}", - ), + HostInfo: HostInfo{ + OS: "centos", + Hostname: "hr-web2", + RequestTags: []string{"tag:hr-webserver"}, + }, }, { IPAddresses: MachineAddresses{ diff --git a/cli_test.go b/cli_test.go index 71f2bea5..ab981671 100644 --- a/cli_test.go +++ b/cli_test.go @@ -35,7 +35,4 @@ func (s *Suite) TestRegisterMachine(c *check.C) { ) c.Assert(err, check.IsNil) c.Assert(machineAfterRegistering.Registered, check.Equals, true) - - _, err = machineAfterRegistering.GetHostInfo() - c.Assert(err, check.IsNil) } diff --git a/machine_test.go b/machine_test.go index e9c91f8b..e20308fd 100644 --- a/machine_test.go +++ b/machine_test.go @@ -35,10 +35,7 @@ func (s *Suite) TestGetMachine(c *check.C) { } app.db.Save(machine) - machineFromDB, err := app.GetMachine("test", "testmachine") - c.Assert(err, check.IsNil) - - _, err = machineFromDB.GetHostInfo() + _, err = app.GetMachine("test", "testmachine") c.Assert(err, check.IsNil) } @@ -65,10 +62,7 @@ func (s *Suite) TestGetMachineByID(c *check.C) { } app.db.Save(&machine) - machineByID, err := app.GetMachineByID(0) - c.Assert(err, check.IsNil) - - _, err = machineByID.GetHostInfo() + _, err = app.GetMachineByID(0) c.Assert(err, check.IsNil) } @@ -146,9 +140,6 @@ func (s *Suite) TestListPeers(c *check.C) { machine0ByID, err := app.GetMachineByID(0) c.Assert(err, check.IsNil) - _, err = machine0ByID.GetHostInfo() - c.Assert(err, check.IsNil) - peersOfMachine0, err := app.ListPeers(machine0ByID) c.Assert(err, check.IsNil) @@ -219,9 +210,6 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { c.Logf("Machine(%v), namespace: %v", testMachine.Name, testMachine.Namespace) c.Assert(err, check.IsNil) - _, err = testMachine.GetHostInfo() - c.Assert(err, check.IsNil) - machines, err := app.ListMachines() c.Assert(err, check.IsNil) diff --git a/routes_test.go b/routes_test.go index 94bda45b..87fc0795 100644 --- a/routes_test.go +++ b/routes_test.go @@ -1,10 +1,7 @@ package headscale import ( - "encoding/json" - "gopkg.in/check.v1" - "gorm.io/datatypes" "inet.af/netaddr" "tailscale.com/tailcfg" ) @@ -25,8 +22,6 @@ func (s *Suite) TestGetRoutes(c *check.C) { hostInfo := tailcfg.Hostinfo{ RoutableIPs: []netaddr.IPPrefix{route}, } - hostinfo, err := json.Marshal(hostInfo) - c.Assert(err, check.IsNil) machine := Machine{ ID: 0, @@ -38,7 +33,7 @@ func (s *Suite) TestGetRoutes(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostinfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) @@ -79,8 +74,6 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { hostInfo := tailcfg.Hostinfo{ RoutableIPs: []netaddr.IPPrefix{route, route2}, } - hostinfo, err := json.Marshal(hostInfo) - c.Assert(err, check.IsNil) machine := Machine{ ID: 0, @@ -92,7 +85,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { Registered: true, RegisterMethod: RegisterMethodAuthKey, AuthKeyID: uint(pak.ID), - HostInfo: datatypes.JSON(hostinfo), + HostInfo: HostInfo(hostInfo), } app.db.Save(&machine) From a455a874add8b559eaa287c4d380ecda0196b357 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Tue, 1 Mar 2022 21:01:46 +0100 Subject: [PATCH 35/53] feat(acls): normalize the group name --- acls.go | 46 ++++++++++++++++----- acls_test.go | 115 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 124 insertions(+), 37 deletions(-) diff --git a/acls.go b/acls.go index 1a597c24..7b93ad76 100644 --- a/acls.go +++ b/acls.go @@ -160,7 +160,7 @@ func (h *Headscale) generateACLPolicySrcIP( aclPolicy ACLPolicy, u string, ) ([]string, error) { - return expandAlias(machines, aclPolicy, u) + return expandAlias(machines, aclPolicy, u, h.cfg.OIDC.StripEmaildomain) } func (h *Headscale) generateACLPolicyDestPorts( @@ -186,7 +186,12 @@ func (h *Headscale) generateACLPolicyDestPorts( alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1]) } - expanded, err := expandAlias(machines, aclPolicy, alias) + expanded, err := expandAlias( + machines, + aclPolicy, + alias, + h.cfg.OIDC.StripEmaildomain, + ) if err != nil { return nil, err } @@ -218,6 +223,7 @@ func expandAlias( machines []Machine, aclPolicy ACLPolicy, alias string, + stripEmailDomain bool, ) ([]string, error) { ips := []string{} if alias == "*" { @@ -225,7 +231,7 @@ func expandAlias( } if strings.HasPrefix(alias, "group:") { - namespaces, err := expandGroup(aclPolicy, alias) + namespaces, err := expandGroup(aclPolicy, alias, stripEmailDomain) if err != nil { return ips, err } @@ -240,7 +246,7 @@ func expandAlias( } if strings.HasPrefix(alias, "tag:") { - owners, err := expandTagOwners(aclPolicy, alias) + owners, err := expandTagOwners(aclPolicy, alias, stripEmailDomain) if err != nil { return ips, err } @@ -396,7 +402,11 @@ func filterMachinesByNamespace(machines []Machine, namespace string) []Machine { // expandTagOwners will return a list of namespace. An owner can be either a namespace or a group // a group cannot be composed of groups. -func expandTagOwners(aclPolicy ACLPolicy, tag string) ([]string, error) { +func expandTagOwners( + aclPolicy ACLPolicy, + tag string, + stripEmailDomain bool, +) ([]string, error) { var owners []string ows, ok := aclPolicy.TagOwners[tag] if !ok { @@ -408,7 +418,7 @@ func expandTagOwners(aclPolicy ACLPolicy, tag string) ([]string, error) { } for _, owner := range ows { if strings.HasPrefix(owner, "group:") { - gs, err := expandGroup(aclPolicy, owner) + gs, err := expandGroup(aclPolicy, owner, stripEmailDomain) if err != nil { return []string{}, err } @@ -423,8 +433,13 @@ func expandTagOwners(aclPolicy ACLPolicy, tag string) ([]string, error) { // expandGroup will return the list of namespace inside the group // after some validation. -func expandGroup(aclPolicy ACLPolicy, group string) ([]string, error) { - groups, ok := aclPolicy.Groups[group] +func expandGroup( + aclPolicy ACLPolicy, + group string, + stripEmailDomain bool, +) ([]string, error) { + outGroups := []string{} + aclGroups, ok := aclPolicy.Groups[group] if !ok { return []string{}, fmt.Errorf( "group %v isn't registered. %w", @@ -432,14 +447,23 @@ func expandGroup(aclPolicy ACLPolicy, group string) ([]string, error) { errInvalidGroup, ) } - for _, g := range groups { - if strings.HasPrefix(g, "group:") { + for _, group := range aclGroups { + if strings.HasPrefix(group, "group:") { return []string{}, fmt.Errorf( "%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups", errInvalidGroup, ) } + grp, err := NormalizeNamespaceName(group, stripEmailDomain) + if err != nil { + return []string{}, fmt.Errorf( + "failed to normalize group %q, err: %w", + group, + errInvalidGroup, + ) + } + outGroups = append(outGroups, grp) } - return groups, nil + return outGroups, nil } diff --git a/acls_test.go b/acls_test.go index 9f0432a7..6cee02c9 100644 --- a/acls_test.go +++ b/acls_test.go @@ -430,8 +430,9 @@ func (s *Suite) TestPortGroup(c *check.C) { func Test_expandGroup(t *testing.T) { type args struct { - aclPolicy ACLPolicy - group string + aclPolicy ACLPolicy + group string + stripEmailDomain bool } tests := []struct { name string @@ -448,7 +449,8 @@ func Test_expandGroup(t *testing.T) { "group:foo": []string{"user2", "user3"}, }, }, - group: "group:test", + group: "group:test", + stripEmailDomain: true, }, want: []string{"user1", "user2", "user3"}, wantErr: false, @@ -462,15 +464,54 @@ func Test_expandGroup(t *testing.T) { "group:foo": []string{"user2", "user3"}, }, }, - group: "group:undefined", + group: "group:undefined", + stripEmailDomain: true, }, want: []string{}, wantErr: true, }, + { + name: "Expand emails in group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{ + "group:admin": []string{ + "joe.bar@gmail.com", + "john.doe@yahoo.fr", + }, + }, + }, + group: "group:admin", + stripEmailDomain: true, + }, + want: []string{"joe.bar", "john.doe"}, + wantErr: false, + }, + { + name: "Expand emails in group", + args: args{ + aclPolicy: ACLPolicy{ + Groups: Groups{ + "group:admin": []string{ + "joe.bar@gmail.com", + "john.doe@yahoo.fr", + }, + }, + }, + group: "group:admin", + stripEmailDomain: false, + }, + want: []string{"joe.bar.gmail.com", "john.doe.yahoo.fr"}, + wantErr: false, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := expandGroup(test.args.aclPolicy, test.args.group) + got, err := expandGroup( + test.args.aclPolicy, + test.args.group, + test.args.stripEmailDomain, + ) if (err != nil) != test.wantErr { t.Errorf("expandGroup() error = %v, wantErr %v", err, test.wantErr) @@ -485,8 +526,9 @@ func Test_expandGroup(t *testing.T) { func Test_expandTagOwners(t *testing.T) { type args struct { - aclPolicy ACLPolicy - tag string + aclPolicy ACLPolicy + tag string + stripEmailDomain bool } tests := []struct { name string @@ -500,7 +542,8 @@ func Test_expandTagOwners(t *testing.T) { aclPolicy: ACLPolicy{ TagOwners: TagOwners{"tag:test": []string{"user1"}}, }, - tag: "tag:test", + tag: "tag:test", + stripEmailDomain: true, }, want: []string{"user1"}, wantErr: false, @@ -512,7 +555,8 @@ func Test_expandTagOwners(t *testing.T) { Groups: Groups{"group:foo": []string{"user1", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo"}}, }, - tag: "tag:test", + tag: "tag:test", + stripEmailDomain: true, }, want: []string{"user1", "user2"}, wantErr: false, @@ -524,7 +568,8 @@ func Test_expandTagOwners(t *testing.T) { Groups: Groups{"group:foo": []string{"user1", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo", "user3"}}, }, - tag: "tag:test", + tag: "tag:test", + stripEmailDomain: true, }, want: []string{"user1", "user2", "user3"}, wantErr: false, @@ -535,7 +580,8 @@ func Test_expandTagOwners(t *testing.T) { aclPolicy: ACLPolicy{ TagOwners: TagOwners{"tag:foo": []string{"group:foo", "user1"}}, }, - tag: "tag:test", + tag: "tag:test", + stripEmailDomain: true, }, want: []string{}, wantErr: true, @@ -547,7 +593,8 @@ func Test_expandTagOwners(t *testing.T) { Groups: Groups{"group:bar": []string{"user1", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo", "user2"}}, }, - tag: "tag:test", + tag: "tag:test", + stripEmailDomain: true, }, want: []string{}, wantErr: true, @@ -555,7 +602,11 @@ func Test_expandTagOwners(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := expandTagOwners(test.args.aclPolicy, test.args.tag) + got, err := expandTagOwners( + test.args.aclPolicy, + test.args.tag, + test.args.stripEmailDomain, + ) if (err != nil) != test.wantErr { t.Errorf("expandTagOwners() error = %v, wantErr %v", err, test.wantErr) @@ -717,9 +768,10 @@ func Test_listMachinesInNamespace(t *testing.T) { // nolint func Test_expandAlias(t *testing.T) { type args struct { - machines []Machine - aclPolicy ACLPolicy - alias string + machines []Machine + aclPolicy ACLPolicy + alias string + stripEmailDomain bool } tests := []struct { name string @@ -739,7 +791,8 @@ func Test_expandAlias(t *testing.T) { }, }, }, - aclPolicy: ACLPolicy{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, }, want: []string{"*"}, wantErr: false, @@ -777,6 +830,7 @@ func Test_expandAlias(t *testing.T) { aclPolicy: ACLPolicy{ Groups: Groups{"group:accountant": []string{"joe", "marc"}}, }, + stripEmailDomain: true, }, want: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, wantErr: false, @@ -814,6 +868,7 @@ func Test_expandAlias(t *testing.T) { aclPolicy: ACLPolicy{ Groups: Groups{"group:accountant": []string{"joe", "marc"}}, }, + stripEmailDomain: true, }, want: []string{}, wantErr: true, @@ -821,9 +876,10 @@ func Test_expandAlias(t *testing.T) { { name: "simple ipaddress", args: args{ - alias: "10.0.0.3", - machines: []Machine{}, - aclPolicy: ACLPolicy{}, + alias: "10.0.0.3", + machines: []Machine{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, }, want: []string{"10.0.0.3"}, wantErr: false, @@ -838,6 +894,7 @@ func Test_expandAlias(t *testing.T) { "homeNetwork": netaddr.MustParseIPPrefix("192.168.1.0/24"), }, }, + stripEmailDomain: true, }, want: []string{"192.168.1.0/24"}, wantErr: false, @@ -845,9 +902,10 @@ func Test_expandAlias(t *testing.T) { { name: "simple host", args: args{ - alias: "10.0.0.1", - machines: []Machine{}, - aclPolicy: ACLPolicy{}, + alias: "10.0.0.1", + machines: []Machine{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, }, want: []string{"10.0.0.1"}, wantErr: false, @@ -855,9 +913,10 @@ func Test_expandAlias(t *testing.T) { { name: "simple CIDR", args: args{ - alias: "10.0.0.0/16", - machines: []Machine{}, - aclPolicy: ACLPolicy{}, + alias: "10.0.0.0/16", + machines: []Machine{}, + aclPolicy: ACLPolicy{}, + stripEmailDomain: true, }, want: []string{"10.0.0.0/16"}, wantErr: false, @@ -901,6 +960,7 @@ func Test_expandAlias(t *testing.T) { aclPolicy: ACLPolicy{ TagOwners: TagOwners{"tag:hr-webserver": []string{"joe"}}, }, + stripEmailDomain: true, }, want: []string{"100.64.0.1", "100.64.0.2"}, wantErr: false, @@ -941,6 +1001,7 @@ func Test_expandAlias(t *testing.T) { "tag:accountant-webserver": []string{"group:accountant"}, }, }, + stripEmailDomain: true, }, want: []string{}, wantErr: true, @@ -984,6 +1045,7 @@ func Test_expandAlias(t *testing.T) { aclPolicy: ACLPolicy{ TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, }, + stripEmailDomain: true, }, want: []string{"100.64.0.4"}, wantErr: false, @@ -995,6 +1057,7 @@ func Test_expandAlias(t *testing.T) { test.args.machines, test.args.aclPolicy, test.args.alias, + test.args.stripEmailDomain, ) if (err != nil) != test.wantErr { t.Errorf("expandAlias() error = %v, wantErr %v", err, test.wantErr) From b2dca80e7a2f0364d476972dc0d6de2e653939e5 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Tue, 1 Mar 2022 21:16:33 +0100 Subject: [PATCH 36/53] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b93ef6..c1b3ee8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ **Features**: - Add support for writing ACL files with YAML [#359](https://github.com/juanfont/headscale/pull/359) +- Users can now use emails in ACL's groups [#372](https://github.com/juanfont/headscale/issues/372) **Changes**: From 361b4f7f4f48fc399da149ffc2bf4b8d5e76c074 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Tue, 1 Mar 2022 22:43:25 +0100 Subject: [PATCH 37/53] fix(machine): allow to use * in ACL sources --- machine.go | 20 ++++- machine_test.go | 205 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 2 deletions(-) diff --git a/machine.go b/machine.go index 3106d0b2..479380a0 100644 --- a/machine.go +++ b/machine.go @@ -173,6 +173,12 @@ func getFilteredByACLPeers( machine.IPAddresses.ToStringSlice(), peer.IPAddresses.ToStringSlice(), ) || // match source and destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + peer.IPAddresses.ToStringSlice(), + machine.IPAddresses.ToStringSlice(), + ) || // match return path matchSourceAndDestinationWithRule( rule.SrcIPs, dst, @@ -182,9 +188,21 @@ func getFilteredByACLPeers( matchSourceAndDestinationWithRule( rule.SrcIPs, dst, + []string{"*"}, + []string{"*"}, + ) || // match source and all destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + []string{"*"}, peer.IPAddresses.ToStringSlice(), + ) || // match source and all destination + matchSourceAndDestinationWithRule( + rule.SrcIPs, + dst, + []string{"*"}, machine.IPAddresses.ToStringSlice(), - ) { // match return path + ) { // match all sources and source peers[peer.ID] = peer } } diff --git a/machine_test.go b/machine_test.go index e9c91f8b..ed95aaf7 100644 --- a/machine_test.go +++ b/machine_test.go @@ -296,6 +296,7 @@ func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) { } } +// nolint func Test_getFilteredByACLPeers(t *testing.T) { type args struct { machines []Machine @@ -443,7 +444,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { }, }, machine: &Machine{ // current machine - ID: 1, + ID: 2, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, Namespace: Namespace{Name: "marc"}, }, @@ -456,6 +457,208 @@ func Test_getFilteredByACLPeers(t *testing.T) { }, }, }, + { + name: "rules allows all hosts to reach one destination", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"*"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "100.64.0.2"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + }, + want: Machines{ + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + }, + }, + { + name: "rules allows all hosts to reach one destination, destination can reach all hosts", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"*"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "100.64.0.2"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{ + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + }, + { + name: "rule allows all hosts to reach all destinations", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + { + SrcIPs: []string{"*"}, + DstPorts: []tailcfg.NetPortRange{ + {IP: "*"}, + }, + }, + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{ + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")}, + Namespace: Namespace{Name: "mickael"}, + }, + }, + }, + { + name: "without rule all communications are forbidden", + args: args{ + machines: []Machine{ // list of all machines in the database + { + ID: 1, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.1"), + }, + Namespace: Namespace{Name: "joe"}, + }, + { + ID: 2, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.2"), + }, + Namespace: Namespace{Name: "marc"}, + }, + { + ID: 3, + IPAddresses: MachineAddresses{ + netaddr.MustParseIP("100.64.0.3"), + }, + Namespace: Namespace{Name: "mickael"}, + }, + }, + rules: []tailcfg.FilterRule{ // list of all ACLRules registered + }, + machine: &Machine{ // current machine + ID: 2, + IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, + Namespace: Namespace{Name: "marc"}, + }, + }, + want: Machines{}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 77fe0b01f7d42f51e67ea0307cf52c2c975844ab Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Tue, 1 Mar 2022 22:50:22 +0100 Subject: [PATCH 38/53] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b3ee8f..d75388b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ **Changes**: - Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) +- Fix a limitation in the ACLs that prevented users to write rules with `*` as source [#374](https://github.com/juanfont/headscale/issues/374) **0.14.0 (2022-02-24):** From dbb2af0238161ed4cbdf3096fc893910bb1bbfbe Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 14:20:04 +1000 Subject: [PATCH 39/53] feat(aliases): add aliases for `route` command - `r` is alias for `route` command - `ls`, or `show` is alias for `list` subcommand --- cmd/headscale/cli/routes.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/headscale/cli/routes.go b/cmd/headscale/cli/routes.go index ced1a0bf..dc060fba 100644 --- a/cmd/headscale/cli/routes.go +++ b/cmd/headscale/cli/routes.go @@ -35,13 +35,15 @@ func init() { } var routesCmd = &cobra.Command{ - Use: "routes", - Short: "Manage the routes of Headscale", + Use: "routes", + Short: "Manage the routes of Headscale", + Aliases: []string{"r", "route"}, } var listRoutesCmd = &cobra.Command{ - Use: "list", - Short: "List routes advertised and enabled by a given node", + Use: "list", + Short: "List routes advertised and enabled by a given node", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") From 21eee912a39ad4a9898fba5de340a5df0db16ca4 Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 14:27:34 +1000 Subject: [PATCH 40/53] feat(aliases): add aliases for `nodes` command - `node`, `machine`, `machines` are aliases for `nodes` command - `ls`, `show` aliases for `list` subcommand - `logout`, `exp`, `e` are aliases for `expire` subcommand - `del` is an alias for `delete` subcommand --- cmd/headscale/cli/nodes.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index a05339c4..0abe87b7 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -49,8 +49,9 @@ func init() { } var nodeCmd = &cobra.Command{ - Use: "nodes", - Short: "Manage the nodes of Headscale", + Use: "nodes", + Short: "Manage the nodes of Headscale", + Aliases: []string{"node", "machine", "machines"}, } var registerNodeCmd = &cobra.Command{ @@ -104,8 +105,9 @@ var registerNodeCmd = &cobra.Command{ } var listNodesCmd = &cobra.Command{ - Use: "list", - Short: "List nodes", + Use: "list", + Short: "List nodes", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") namespace, err := cmd.Flags().GetString("namespace") @@ -164,7 +166,7 @@ var expireNodeCmd = &cobra.Command{ Use: "expire", Short: "Expire (log out) a machine in your network", Long: "Expiring a node will keep the node in the database and force it to reauthenticate.", - Aliases: []string{"logout"}, + Aliases: []string{"logout", "exp", "e"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -206,8 +208,9 @@ var expireNodeCmd = &cobra.Command{ } var deleteNodeCmd = &cobra.Command{ - Use: "delete", - Short: "Delete a node", + Use: "delete", + Short: "Delete a node", + Aliases: []string{"del"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") From aff6b84250868beee6a8df2aa764b8f19b0ad156 Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 14:29:33 +1000 Subject: [PATCH 41/53] feat(aliases): add 'gen' alias for 'generate' command --- cmd/headscale/cli/generate.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/headscale/cli/generate.go b/cmd/headscale/cli/generate.go index 24844146..35906411 100644 --- a/cmd/headscale/cli/generate.go +++ b/cmd/headscale/cli/generate.go @@ -13,8 +13,9 @@ func init() { } var generateCmd = &cobra.Command{ - Use: "generate", - Short: "Generate commands", + Use: "generate", + Short: "Generate commands", + Aliases: []string{"gen"}, } var generatePrivateKeyCmd = &cobra.Command{ From 5310f8692bf071a20dcda81e96c9321b5ecdb53b Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 14:35:20 +1000 Subject: [PATCH 42/53] feat(aliases): add aliases for `namespaces` command - `namespace`, `ns`, `user`, `users` are aliases for `namespaces` command - `c`, `new` are aliases for the `create` subcommand - `delete` is an alias for the `destroy` subcommand - `mv` is an alias for the `rename` subcommand - `ls`, `show` are aliases for the `list` subcommand --- cmd/headscale/cli/namespaces.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/cmd/headscale/cli/namespaces.go b/cmd/headscale/cli/namespaces.go index 361e9be3..729e213e 100644 --- a/cmd/headscale/cli/namespaces.go +++ b/cmd/headscale/cli/namespaces.go @@ -25,13 +25,15 @@ const ( ) var namespaceCmd = &cobra.Command{ - Use: "namespaces", - Short: "Manage the namespaces of Headscale", + Use: "namespaces", + Short: "Manage the namespaces of Headscale", + Aliases: []string{"namespace", "ns", "user", "users"}, } var createNamespaceCmd = &cobra.Command{ - Use: "create NAME", - Short: "Creates a new namespace", + Use: "create NAME", + Short: "Creates a new namespace", + Aliases: []string{"c", "new"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errMissingParameter @@ -72,8 +74,9 @@ var createNamespaceCmd = &cobra.Command{ } var destroyNamespaceCmd = &cobra.Command{ - Use: "destroy NAME", - Short: "Destroys a namespace", + Use: "destroy NAME", + Short: "Destroys a namespace", + Aliases: []string{"delete"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errMissingParameter @@ -144,8 +147,9 @@ var destroyNamespaceCmd = &cobra.Command{ } var listNamespacesCmd = &cobra.Command{ - Use: "list", - Short: "List all the namespaces", + Use: "list", + Short: "List all the namespaces", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -197,8 +201,9 @@ var listNamespacesCmd = &cobra.Command{ } var renameNamespaceCmd = &cobra.Command{ - Use: "rename OLD_NAME NEW_NAME", - Short: "Renames a namespace", + Use: "rename OLD_NAME NEW_NAME", + Short: "Renames a namespace", + Aliases: []string{"mv"}, Args: func(cmd *cobra.Command, args []string) error { expectedArguments := 2 if len(args) < expectedArguments { From 052dbfe4403028b6a7356a3b5daf78392b4e14f7 Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 15:31:11 +1000 Subject: [PATCH 43/53] feat(aliases): add aliases for `apikeys` command - `apikey`, `api` are aliases for `apikeys` command - `ls`, `show` are aliases for `list` subcommand - `c`, `new` are aliases for `create` subcommand - `revoke`, `exp`, `e` are aliases for the `expire` subcommand --- cmd/headscale/cli/api_key.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/headscale/cli/api_key.go b/cmd/headscale/cli/api_key.go index 975149ff..06099aa1 100644 --- a/cmd/headscale/cli/api_key.go +++ b/cmd/headscale/cli/api_key.go @@ -36,13 +36,15 @@ func init() { } var apiKeysCmd = &cobra.Command{ - Use: "apikeys", - Short: "Handle the Api keys in Headscale", + Use: "apikeys", + Short: "Handle the Api keys in Headscale", + Aliases: []string{"apikey", "api"}, } var listAPIKeys = &cobra.Command{ - Use: "list", - Short: "List the Api keys for headscale", + Use: "list", + Short: "List the Api keys for headscale", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -107,6 +109,7 @@ var createAPIKeyCmd = &cobra.Command{ Creates a new Api key, the Api key is only visible on creation and cannot be retrieved again. If you loose a key, create a new one and revoke (expire) the old one.`, + Aliases: []string{"c", "new"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -144,7 +147,7 @@ If you loose a key, create a new one and revoke (expire) the old one.`, var expireAPIKeyCmd = &cobra.Command{ Use: "expire", Short: "Expire an ApiKey", - Aliases: []string{"revoke"}, + Aliases: []string{"revoke", "exp", "e"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") From 12b3b5f8f10ae9d4ff2044e0552a493c16d4739b Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 15:39:30 +1000 Subject: [PATCH 44/53] feat(aliases): add aliases for `preauthkeys` command - `preauthkey`, `authkey`, `pre` are aliases for `preauthkey` command - `ls`, `show` are aliases for `list` subcommand - `c`, `new` are aliases for `create` subcommand - `revoke`, `exp`, `e` are aliases for `expire` subcommand --- cmd/headscale/cli/preauthkeys.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index 580184f1..950cbcc5 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -35,13 +35,15 @@ func init() { } var preauthkeysCmd = &cobra.Command{ - Use: "preauthkeys", - Short: "Handle the preauthkeys in Headscale", + Use: "preauthkeys", + Short: "Handle the preauthkeys in Headscale", + Aliases: []string{"preauthkey", "authkey", "pre"}, } var listPreAuthKeys = &cobra.Command{ - Use: "list", - Short: "List the preauthkeys for this namespace", + Use: "list", + Short: "List the preauthkeys for this namespace", + Aliases: []string{"ls", "show"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -118,8 +120,9 @@ var listPreAuthKeys = &cobra.Command{ } var createPreAuthKeyCmd = &cobra.Command{ - Use: "create", - Short: "Creates a new preauthkey in the specified namespace", + Use: "create", + Short: "Creates a new preauthkey in the specified namespace", + Aliases: []string{"c", "new"}, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") @@ -172,8 +175,9 @@ var createPreAuthKeyCmd = &cobra.Command{ } var expirePreAuthKeyCmd = &cobra.Command{ - Use: "expire KEY", - Short: "Expire a preauthkey", + Use: "expire KEY", + Short: "Expire a preauthkey", + Aliases: []string{"revoke", "exp", "e"}, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errMissingParameter From 86ade72c193626560ea80b327510af91f1a12890 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 1 Mar 2022 18:51:56 +0000 Subject: [PATCH 45/53] Remove err check --- oidc.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/oidc.go b/oidc.go index 8e89e27f..f7cb2492 100644 --- a/oidc.go +++ b/oidc.go @@ -216,16 +216,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { } // retrieve machine information if it exist - machine, err := h.GetMachineByMachineKey(machineKey) - if err != nil { - log.Error().Msg("machine key not found in database") - ctx.String( - http.StatusInternalServerError, - "could not get machine info from database", - ) - - return - } + machine, _ := h.GetMachineByMachineKey(machineKey) if machine != nil { log.Trace(). From ec4dc68524b5396443e3a6b25d546d3855ad2c2f Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 06:55:21 +0000 Subject: [PATCH 46/53] Use correct machinekey format for oidc reg --- oidc.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/oidc.go b/oidc.go index f7cb2492..c200f5e1 100644 --- a/oidc.go +++ b/oidc.go @@ -193,10 +193,12 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - machineKeyStr, machineKeyOK := machineKeyIf.(string) + machineKeyFromCache, machineKeyOK := machineKeyIf.(string) var machineKey key.MachinePublic - err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) + err = machineKey.UnmarshalText( + []byte(MachinePublicKeyEnsurePrefix(machineKeyFromCache)), + ) if err != nil { log.Error(). Msg("could not parse machine public key") @@ -295,6 +297,8 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } + machineKeyStr := MachinePublicKeyStripPrefix(machineKey) + _, err = h.RegisterMachineFromAuthCallback( machineKeyStr, namespace.Name, From ef422e69882f4c1ba0e04bc8019350e41fdabd59 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 07:15:20 +0000 Subject: [PATCH 47/53] Protect against expiry nil --- api.go | 1 + machine.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api.go b/api.go index 9b8fafd2..3b3b6757 100644 --- a/api.go +++ b/api.go @@ -144,6 +144,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { Name: req.Hostinfo.Hostname, NodeKey: NodePublicKeyStripPrefix(req.NodeKey), LastSeen: &now, + Expiry: &time.Time{}, } if !req.Expiry.IsZero() { diff --git a/machine.go b/machine.go index bd842ae3..c9370c27 100644 --- a/machine.go +++ b/machine.go @@ -117,7 +117,7 @@ func (machine Machine) isExpired() bool { // If Expiry is not set, the client has not indicated that // it wants an expiry time, it is therefor considered // to mean "not expired" - if machine.Expiry.IsZero() { + if machine.Expiry == nil || machine.Expiry.IsZero() { return false } From 1f8c7f427b82f6ff1259c4b35eb9969e90880d1d Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 07:29:40 +0000 Subject: [PATCH 48/53] Add comment --- oidc.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/oidc.go b/oidc.go index c200f5e1..987bc8b2 100644 --- a/oidc.go +++ b/oidc.go @@ -218,6 +218,9 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { } // retrieve machine information if it exist + // The error is not important, because if it does not + // exist, then this is a new machine and we will move + // on to registration. machine, _ := h.GetMachineByMachineKey(machineKey) if machine != nil { From c80e364f02e2dee7cab996ad13f14a82a7e65fff Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 08:15:14 +0000 Subject: [PATCH 49/53] Remove always nil error --- acls.go | 10 ++++------ acls_test.go | 13 +------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/acls.go b/acls.go index 2666a85c..84063a17 100644 --- a/acls.go +++ b/acls.go @@ -267,10 +267,8 @@ func expandAlias( // if alias is a namespace nodes := filterMachinesByNamespace(machines, alias) - nodes, err := excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias) - if err != nil { - return ips, err - } + nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias) + for _, n := range nodes { ips = append(ips, n.IPAddresses.ToStringSlice()...) } @@ -305,7 +303,7 @@ func excludeCorrectlyTaggedNodes( aclPolicy ACLPolicy, nodes []Machine, namespace string, -) ([]Machine, error) { +) []Machine { out := []Machine{} tags := []string{} for tag, ns := range aclPolicy.TagOwners { @@ -330,7 +328,7 @@ func excludeCorrectlyTaggedNodes( } } - return out, nil + return out } func expandPorts(portsStr string) (*[]tailcfg.PortRange, error) { diff --git a/acls_test.go b/acls_test.go index c434ab74..9dcc40ba 100644 --- a/acls_test.go +++ b/acls_test.go @@ -1142,7 +1142,6 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { Namespace: Namespace{Name: "joe"}, }, }, - wantErr: false, }, { name: "all nodes have invalid tags, don't exclude them", @@ -1212,25 +1211,15 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { Namespace: Namespace{Name: "joe"}, }, }, - wantErr: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := excludeCorrectlyTaggedNodes( + got := excludeCorrectlyTaggedNodes( test.args.aclPolicy, test.args.nodes, test.args.namespace, ) - if (err != nil) != test.wantErr { - t.Errorf( - "excludeCorrectlyTaggedNodes() error = %v, wantErr %v", - err, - test.wantErr, - ) - - return - } if !reflect.DeepEqual(got, test.want) { t.Errorf("excludeCorrectlyTaggedNodes() = %v, want %v", got, test.want) } From 0835bffc3c711901f45f3aac54f0f2a3c4ef9d57 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 08:15:21 +0000 Subject: [PATCH 50/53] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a992b0..b6d0dc72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Simplify the code behind registration of machines [#366](https://github.com/juanfont/headscale/pull/366) - Nodes are now only written to database if they are registrated successfully - Fix a limitation in the ACLs that prevented users to write rules with `*` as source [#374](https://github.com/juanfont/headscale/issues/374) +- Reduce the overhead of marshal/unmarshal for Hostinfo, routes and endpoints by using specific types in Machine [#371](https://github.com/juanfont/headscale/pull/371) ## 0.14.0 (2022-02-24) From 32ac690494bc54ade569da9b44f23990e6b8d875 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 09:08:30 +0000 Subject: [PATCH 51/53] Update contributors.yml --- .github/workflows/contributors.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index 4a73009b..5419129b 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -10,6 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Delete upstream contributor branch + run: git push origin --delete update-contributors + - name: Create up-to-date contributors branch + run: git checkout -B update-contributors - uses: BobAnkh/add-contributors@v0.2.2 with: CONTRIBUTOR: "## Contributors" From 2b6a5173da1253150369515518dab017d9763296 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 2 Mar 2022 09:12:00 +0000 Subject: [PATCH 52/53] Allow upstream delete continue on failure --- .github/workflows/contributors.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index 5419129b..832abb9e 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -11,6 +11,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: Delete upstream contributor branch + # Allow continue on failure to account for when the + # upstream branch is deleted or does not exist. + continue-on-error: true run: git push origin --delete update-contributors - name: Create up-to-date contributors branch run: git checkout -B update-contributors From a614f158be3c3bea3193a572a7ddf220142718e5 Mon Sep 17 00:00:00 2001 From: zakaria Date: Wed, 2 Mar 2022 19:53:07 +1000 Subject: [PATCH 53/53] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b3ee8f..eaf7ade0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Add support for writing ACL files with YAML [#359](https://github.com/juanfont/headscale/pull/359) - Users can now use emails in ACL's groups [#372](https://github.com/juanfont/headscale/issues/372) +- Add shorthand aliases for commands and subcommands [#376](https://github.com/juanfont/headscale/pull/376) **Changes**: