From 44a5372c531d8118a3b2b01167640003f8f16365 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Thu, 3 Mar 2022 23:52:25 +0100 Subject: [PATCH 1/7] fix(poll): Normalize hostname This function is called often. Normalization of the hostname will be written in database. --- poll.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/poll.go b/poll.go index 1ae8cd0d..5723a3d6 100644 --- a/poll.go +++ b/poll.go @@ -83,7 +83,18 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("machine", machine.Name). Msg("Found machine in database") - machine.Name = req.Hostinfo.Hostname + hname, err := NormalizeNamespaceName( + req.Hostinfo.Hostname, + h.cfg.OIDC.StripEmaildomain, + ) + if err != nil { + log.Error(). + Caller(). + Str("func", "handleAuthKey"). + Str("hostinfo.name", req.Hostinfo.Hostname). + Err(err) + } + machine.Name = hname machine.HostInfo = HostInfo(*req.Hostinfo) machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey) now := time.Now().UTC() From 6f172a6e4ccb71e94c4daa7617408e0b5dc16e92 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Thu, 3 Mar 2022 23:53:08 +0100 Subject: [PATCH 2/7] fix(acls): remove dead error code --- acls.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/acls.go b/acls.go index 24aadf5b..95484806 100644 --- a/acls.go +++ b/acls.go @@ -17,12 +17,11 @@ import ( ) const ( - errEmptyPolicy = Error("empty policy") - errInvalidAction = Error("invalid action") - errInvalidUserSection = Error("invalid user section") - errInvalidGroup = Error("invalid group") - errInvalidTag = Error("invalid tag") - errInvalidPortFormat = Error("invalid port format") + errEmptyPolicy = Error("empty policy") + errInvalidAction = Error("invalid action") + errInvalidGroup = Error("invalid group") + errInvalidTag = Error("invalid tag") + errInvalidPortFormat = Error("invalid port format") ) const ( From 11144496015a2ee6dfd80046aace3c5e68c3d12b Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Sun, 6 Mar 2022 20:46:17 +0100 Subject: [PATCH 3/7] change: update name of method to check and normalize Domain name --- acls.go | 2 +- namespaces.go | 14 +++++++------- namespaces_test.go | 8 ++++---- oidc.go | 2 +- poll.go | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/acls.go b/acls.go index 95484806..e542a0f3 100644 --- a/acls.go +++ b/acls.go @@ -444,7 +444,7 @@ func expandGroup( errInvalidGroup, ) } - grp, err := NormalizeNamespaceName(group, stripEmailDomain) + grp, err := NormalizeName(group, stripEmailDomain) if err != nil { return []string{}, fmt.Errorf( "failed to normalize group %q, err: %w", diff --git a/namespaces.go b/namespaces.go index 31517841..74b125d3 100644 --- a/namespaces.go +++ b/namespaces.go @@ -41,7 +41,7 @@ type Namespace struct { // CreateNamespace creates a new Namespace. Returns error if could not be created // or another namespace already exists. func (h *Headscale) CreateNamespace(name string) (*Namespace, error) { - err := CheckNamespaceName(name) + err := CheckName(name) if err != nil { return nil, err } @@ -104,7 +104,7 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error { if err != nil { return err } - err = CheckNamespaceName(newName) + err = CheckName(newName) if err != nil { return err } @@ -150,7 +150,7 @@ func (h *Headscale) ListNamespaces() ([]Namespace, error) { // ListMachinesInNamespace gets all the nodes in a given namespace. func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { - err := CheckNamespaceName(name) + err := CheckName(name) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { // SetMachineNamespace assigns a Machine to a namespace. func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) error { - err := CheckNamespaceName(namespaceName) + err := CheckName(namespaceName) if err != nil { return err } @@ -237,9 +237,9 @@ func (n *Namespace) toProto() *v1.Namespace { } } -// NormalizeNamespaceName will replace forbidden chars in namespace +// NormalizeName will replace forbidden chars in namespace // it can also return an error if the namespace doesn't respect RFC 952 and 1123. -func NormalizeNamespaceName(name string, stripEmailDomain bool) (string, error) { +func NormalizeName(name string, stripEmailDomain bool) (string, error) { name = strings.ToLower(name) name = strings.ReplaceAll(name, "'", "") atIdx := strings.Index(name, "@") @@ -263,7 +263,7 @@ func NormalizeNamespaceName(name string, stripEmailDomain bool) (string, error) return name, nil } -func CheckNamespaceName(name string) error { +func CheckName(name string) error { if len(name) > labelHostnameLength { return fmt.Errorf( "Namespace must not be over 63 chars. %v doesn't comply with this rule: %w", diff --git a/namespaces_test.go b/namespaces_test.go index 1710f7d6..406dccfc 100644 --- a/namespaces_test.go +++ b/namespaces_test.go @@ -233,7 +233,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { c.Assert(found, check.Equals, true) } -func TestNormalizeNamespaceName(t *testing.T) { +func TestNormalizeName(t *testing.T) { type args struct { name string stripEmailDomain bool @@ -310,7 +310,7 @@ func TestNormalizeNamespaceName(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NormalizeNamespaceName(tt.args.name, tt.args.stripEmailDomain) + got, err := NormalizeName(tt.args.name, tt.args.stripEmailDomain) if (err != nil) != tt.wantErr { t.Errorf( "NormalizeNamespaceName() error = %v, wantErr %v", @@ -327,7 +327,7 @@ func TestNormalizeNamespaceName(t *testing.T) { } } -func TestCheckNamespaceName(t *testing.T) { +func TestCheckName(t *testing.T) { type args struct { name string } @@ -366,7 +366,7 @@ func TestCheckNamespaceName(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := CheckNamespaceName(tt.args.name); (err != nil) != tt.wantErr { + if err := CheckName(tt.args.name); (err != nil) != tt.wantErr { t.Errorf("CheckNamespaceName() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/oidc.go b/oidc.go index 987bc8b2..a81c4b59 100644 --- a/oidc.go +++ b/oidc.go @@ -253,7 +253,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - namespaceName, err := NormalizeNamespaceName( + namespaceName, err := NormalizeName( claims.Email, h.cfg.OIDC.StripEmaildomain, ) diff --git a/poll.go b/poll.go index 5723a3d6..0bdd80c6 100644 --- a/poll.go +++ b/poll.go @@ -83,7 +83,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("machine", machine.Name). Msg("Found machine in database") - hname, err := NormalizeNamespaceName( + hname, err := NormalizeName( req.Hostinfo.Hostname, h.cfg.OIDC.StripEmaildomain, ) From 6cc8bbc24fb0666b68d3ea9ba44dab83ec0d69a8 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Mon, 7 Mar 2022 22:46:29 +0100 Subject: [PATCH 4/7] feat(api): add normalisation at machine register step --- api.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/api.go b/api.go index 3b3b6757..b4ad92f3 100644 --- a/api.go +++ b/api.go @@ -134,6 +134,18 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { return } + hname, err := NormalizeName( + req.Hostinfo.Hostname, + h.cfg.OIDC.StripEmaildomain, + ) + if err != nil { + log.Error(). + Caller(). + Str("func", "RegistrationHandler"). + Str("hostinfo.name", req.Hostinfo.Hostname). + Err(err) + return + } // 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) @@ -141,7 +153,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { // happens newMachine := Machine{ MachineKey: machineKeyStr, - Name: req.Hostinfo.Hostname, + Name: hname, NodeKey: NodePublicKeyStripPrefix(req.NodeKey), LastSeen: &now, Expiry: &time.Time{}, From f19c0485698b6e7a2131c7f4534e79a796962a62 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Mon, 7 Mar 2022 22:55:54 +0100 Subject: [PATCH 5/7] fix: change normalization function name --- api.go | 2 +- namespaces.go | 14 +++++++------- namespaces_test.go | 14 +++++++------- oidc.go | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api.go b/api.go index b4ad92f3..fec79a2f 100644 --- a/api.go +++ b/api.go @@ -134,7 +134,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { return } - hname, err := NormalizeName( + hname, err := NormalizeToFQDNRules( req.Hostinfo.Hostname, h.cfg.OIDC.StripEmaildomain, ) diff --git a/namespaces.go b/namespaces.go index 74b125d3..bb32795a 100644 --- a/namespaces.go +++ b/namespaces.go @@ -41,7 +41,7 @@ type Namespace struct { // CreateNamespace creates a new Namespace. Returns error if could not be created // or another namespace already exists. func (h *Headscale) CreateNamespace(name string) (*Namespace, error) { - err := CheckName(name) + err := CheckForFQDNRules(name) if err != nil { return nil, err } @@ -104,7 +104,7 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error { if err != nil { return err } - err = CheckName(newName) + err = CheckForFQDNRules(newName) if err != nil { return err } @@ -150,7 +150,7 @@ func (h *Headscale) ListNamespaces() ([]Namespace, error) { // ListMachinesInNamespace gets all the nodes in a given namespace. func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { - err := CheckName(name) + err := CheckForFQDNRules(name) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { // SetMachineNamespace assigns a Machine to a namespace. func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) error { - err := CheckName(namespaceName) + err := CheckForFQDNRules(namespaceName) if err != nil { return err } @@ -237,9 +237,9 @@ func (n *Namespace) toProto() *v1.Namespace { } } -// NormalizeName will replace forbidden chars in namespace +// NormalizeToFQDNRules will replace forbidden chars in namespace // it can also return an error if the namespace doesn't respect RFC 952 and 1123. -func NormalizeName(name string, stripEmailDomain bool) (string, error) { +func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) { name = strings.ToLower(name) name = strings.ReplaceAll(name, "'", "") atIdx := strings.Index(name, "@") @@ -263,7 +263,7 @@ func NormalizeName(name string, stripEmailDomain bool) (string, error) { return name, nil } -func CheckName(name string) error { +func CheckForFQDNRules(name string) error { if len(name) > labelHostnameLength { return fmt.Errorf( "Namespace must not be over 63 chars. %v doesn't comply with this rule: %w", diff --git a/namespaces_test.go b/namespaces_test.go index 406dccfc..5d873bd6 100644 --- a/namespaces_test.go +++ b/namespaces_test.go @@ -233,7 +233,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { c.Assert(found, check.Equals, true) } -func TestNormalizeName(t *testing.T) { +func TestNormalizeToFQDNRules(t *testing.T) { type args struct { name string stripEmailDomain bool @@ -310,10 +310,10 @@ func TestNormalizeName(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NormalizeName(tt.args.name, tt.args.stripEmailDomain) + got, err := NormalizeToFQDNRules(tt.args.name, tt.args.stripEmailDomain) if (err != nil) != tt.wantErr { t.Errorf( - "NormalizeNamespaceName() error = %v, wantErr %v", + "NormalizeToFQDNRules() error = %v, wantErr %v", err, tt.wantErr, ) @@ -321,13 +321,13 @@ func TestNormalizeName(t *testing.T) { return } if got != tt.want { - t.Errorf("NormalizeNamespaceName() = %v, want %v", got, tt.want) + t.Errorf("NormalizeToFQDNRules() = %v, want %v", got, tt.want) } }) } } -func TestCheckName(t *testing.T) { +func TestCheckForFQDNRules(t *testing.T) { type args struct { name string } @@ -366,8 +366,8 @@ func TestCheckName(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := CheckName(tt.args.name); (err != nil) != tt.wantErr { - t.Errorf("CheckNamespaceName() error = %v, wantErr %v", err, tt.wantErr) + if err := CheckForFQDNRules(tt.args.name); (err != nil) != tt.wantErr { + t.Errorf("CheckForFQDNRules() error = %v, wantErr %v", err, tt.wantErr) } }) } diff --git a/oidc.go b/oidc.go index a81c4b59..29ce351f 100644 --- a/oidc.go +++ b/oidc.go @@ -253,7 +253,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - namespaceName, err := NormalizeName( + namespaceName, err := NormalizeToFQDNRules( claims.Email, h.cfg.OIDC.StripEmaildomain, ) From 2b68c90778c89335bef35eb355aff432da9592db Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Mon, 7 Mar 2022 23:14:39 +0100 Subject: [PATCH 6/7] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc34ab99..8535b45f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - 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) +- Apply normalization function to FQDN on hostnames when hosts registers and retrieve informations [#363](https://github.com/juanfont/headscale/issues/363) ## 0.14.0 (2022-02-24) From 41efe98953388f62120f7ea3f7d94d1cccb3de32 Mon Sep 17 00:00:00 2001 From: Adrien Raffin-Caboisse Date: Mon, 7 Mar 2022 23:20:30 +0100 Subject: [PATCH 7/7] fix: apply fmt and fix missing name changes --- acls.go | 2 +- api.go | 1 + poll.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/acls.go b/acls.go index e542a0f3..63c3955c 100644 --- a/acls.go +++ b/acls.go @@ -444,7 +444,7 @@ func expandGroup( errInvalidGroup, ) } - grp, err := NormalizeName(group, stripEmailDomain) + grp, err := NormalizeToFQDNRules(group, stripEmailDomain) if err != nil { return []string{}, fmt.Errorf( "failed to normalize group %q, err: %w", diff --git a/api.go b/api.go index fec79a2f..b5e885a1 100644 --- a/api.go +++ b/api.go @@ -144,6 +144,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { Str("func", "RegistrationHandler"). Str("hostinfo.name", req.Hostinfo.Hostname). Err(err) + return } diff --git a/poll.go b/poll.go index 0bdd80c6..15945a9b 100644 --- a/poll.go +++ b/poll.go @@ -83,7 +83,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("machine", machine.Name). Msg("Found machine in database") - hname, err := NormalizeName( + hname, err := NormalizeToFQDNRules( req.Hostinfo.Hostname, h.cfg.OIDC.StripEmaildomain, )