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) diff --git a/acls.go b/acls.go index 24aadf5b..63c3955c 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 ( @@ -445,7 +444,7 @@ func expandGroup( errInvalidGroup, ) } - grp, err := NormalizeNamespaceName(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 3b3b6757..b5e885a1 100644 --- a/api.go +++ b/api.go @@ -134,6 +134,19 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) { return } + hname, err := NormalizeToFQDNRules( + 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 +154,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{}, diff --git a/namespaces.go b/namespaces.go index 31517841..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 := CheckNamespaceName(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 = CheckNamespaceName(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 := CheckNamespaceName(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 := CheckNamespaceName(namespaceName) + err := CheckForFQDNRules(namespaceName) if err != nil { return err } @@ -237,9 +237,9 @@ func (n *Namespace) toProto() *v1.Namespace { } } -// NormalizeNamespaceName 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 NormalizeNamespaceName(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 NormalizeNamespaceName(name string, stripEmailDomain bool) (string, error) return name, nil } -func CheckNamespaceName(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 1710f7d6..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 TestNormalizeNamespaceName(t *testing.T) { +func TestNormalizeToFQDNRules(t *testing.T) { type args struct { name string stripEmailDomain bool @@ -310,10 +310,10 @@ 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 := 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 TestNormalizeNamespaceName(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 TestCheckNamespaceName(t *testing.T) { +func TestCheckForFQDNRules(t *testing.T) { type args struct { name string } @@ -366,8 +366,8 @@ 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 { - 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 987bc8b2..29ce351f 100644 --- a/oidc.go +++ b/oidc.go @@ -253,7 +253,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { return } - namespaceName, err := NormalizeNamespaceName( + namespaceName, err := NormalizeToFQDNRules( claims.Email, h.cfg.OIDC.StripEmaildomain, ) diff --git a/poll.go b/poll.go index 1ae8cd0d..15945a9b 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 := NormalizeToFQDNRules( + 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()