Skip to content

Commit

Permalink
Merge pull request #560 from kradalby/rename-fixess
Browse files Browse the repository at this point in the history
  • Loading branch information
kradalby authored May 31, 2022
2 parents b472e5a + 7f7cd73 commit be25bbc
Show file tree
Hide file tree
Showing 34 changed files with 1,480 additions and 541 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
- Add command to set tags on a node [#525](https://github.com/juanfont/headscale/issues/525)
- Add command to view tags of nodes [#356](https://github.com/juanfont/headscale/issues/356)
- Add --all (-a) flag to enable routes command [#360](https://github.com/juanfont/headscale/issues/360)
- Fix issue where nodes was not updated across namespaces [#560](https://github.com/juanfont/headscale/pull/560)
- Add the ability to rename a nodes name [#560](https://github.com/juanfont/headscale/pull/560)
- Node DNS names are now unique, a random suffix will be added when a node joins
- This change contains database changes, remember to **backup** your database before upgrading
- Add option to enable/disable logtail (Tailscale's logging infrastructure) [#596](https://github.com/juanfont/headscale/pull/596)
- This change disables the logs by default
- Use [Prometheus]'s duration parser, supporting days (`d`), weeks (`w`) and years (`y`) [#598](https://github.com/juanfont/headscale/pull/598)
Expand Down
14 changes: 7 additions & 7 deletions acls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
Hostname: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
Expand Down Expand Up @@ -164,7 +164,7 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
MachineKey: "12345",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
Hostname: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
Expand Down Expand Up @@ -210,7 +210,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
MachineKey: "12345",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
Hostname: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
Expand Down Expand Up @@ -255,7 +255,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
MachineKey: "12345",
NodeKey: "bar",
DiscoKey: "faa",
Name: "webserver",
Hostname: "webserver",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
Expand All @@ -274,7 +274,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
MachineKey: "56789",
NodeKey: "bar2",
DiscoKey: "faab",
Name: "user",
Hostname: "user",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
Expand Down Expand Up @@ -368,7 +368,7 @@ func (s *Suite) TestPortNamespace(c *check.C) {
MachineKey: "12345",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
Hostname: "testmachine",
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: ips,
Expand Down Expand Up @@ -410,7 +410,7 @@ func (s *Suite) TestPortGroup(c *check.C) {
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
Hostname: "testmachine",
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: ips,
Expand Down
34 changes: 23 additions & 11 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,8 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {

return
}
hname, err := NormalizeToFQDNRules(
req.Hostinfo.Hostname,
h.cfg.OIDC.StripEmaildomain,
)

givenName, err := h.GenerateGivenName(req.Hostinfo.Hostname)
if err != nil {
log.Error().
Caller().
Expand All @@ -153,7 +151,8 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
// happens
newMachine := Machine{
MachineKey: machineKeyStr,
Name: hname,
Hostname: req.Hostinfo.Hostname,
GivenName: givenName,
NodeKey: NodePublicKeyStripPrefix(req.NodeKey),
LastSeen: &now,
Expiry: &time.Time{},
Expand Down Expand Up @@ -364,7 +363,7 @@ func (h *Headscale) handleMachineLogOut(
resp := tailcfg.RegisterResponse{}

log.Info().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("Client requested logout")

h.ExpireMachine(&machine)
Expand Down Expand Up @@ -394,7 +393,7 @@ func (h *Headscale) handleMachineValidRegistration(

// The machine registration is valid, respond with redirect to /map
log.Debug().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("Client is registered and we have the current NodeKey. All clear to /map")

resp.AuthURL = ""
Expand Down Expand Up @@ -429,7 +428,7 @@ func (h *Headscale) handleMachineExpired(

// The client has registered before, but has expired
log.Debug().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("Machine registration has expired. Sending a authurl to register")

if registerRequest.Auth.AuthKey != "" {
Expand Down Expand Up @@ -472,7 +471,7 @@ func (h *Headscale) handleMachineRefreshKey(
resp := tailcfg.RegisterResponse{}

log.Debug().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("We have the OldNodeKey in the database. This is a key refresh")
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
h.db.Save(&machine)
Expand Down Expand Up @@ -597,16 +596,29 @@ func (h *Headscale) handleAuthKey(
if machine != nil {
log.Trace().
Caller().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("machine already registered, refreshing with new auth key")

machine.NodeKey = nodeKey
machine.AuthKeyID = uint(pak.ID)
h.RefreshMachine(machine, registerRequest.Expiry)
} else {
now := time.Now().UTC()

givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname)
if err != nil {
log.Error().
Caller().
Str("func", "RegistrationHandler").
Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
Err(err)

return
}

machineToRegister := Machine{
Name: registerRequest.Hostinfo.Hostname,
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
NamespaceID: pak.Namespace.ID,
MachineKey: machineKeyStr,
RegisterMethod: RegisterMethodAuthKey,
Expand Down
28 changes: 20 additions & 8 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/patrickmn/go-cache"
zerolog "github.com/philip-bui/grpc-zerolog"
"github.com/puzpuzpuz/xsync"
zl "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
ginprometheus "github.com/zsais/go-gin-prometheus"
Expand Down Expand Up @@ -166,7 +167,7 @@ type Headscale struct {
aclPolicy *ACLPolicy
aclRules []tailcfg.FilterRule

lastStateChange sync.Map
lastStateChange *xsync.MapOf[time.Time]

oidcProvider *oidc.Provider
oauth2Config *oauth2.Config
Expand Down Expand Up @@ -310,14 +311,14 @@ func (h *Headscale) expireEphemeralNodesWorker() {
After(machine.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
expiredFound = true
log.Info().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("Ephemeral client removed from database")

err = h.db.Unscoped().Delete(machine).Error
if err != nil {
log.Error().
Err(err).
Str("machine", machine.Name).
Str("machine", machine.Hostname).
Msg("🤮 Cannot delete ephemeral machine from the database")
}
}
Expand Down Expand Up @@ -799,18 +800,29 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
func (h *Headscale) setLastStateChangeToNow(namespace string) {
now := time.Now().UTC()
lastStateUpdate.WithLabelValues("", "headscale").Set(float64(now.Unix()))
if h.lastStateChange == nil {
h.lastStateChange = xsync.NewMapOf[time.Time]()
}
h.lastStateChange.Store(namespace, now)
}

func (h *Headscale) getLastStateChange(namespaces ...string) time.Time {
times := []time.Time{}

for _, namespace := range namespaces {
if wrapped, ok := h.lastStateChange.Load(namespace); ok {
lastChange, _ := wrapped.(time.Time)

times = append(times, lastChange)
// getLastStateChange takes a list of namespaces as a "filter", if no namespaces
// are past, then use the entier list of namespaces and look for the last update
if len(namespaces) > 0 {
for _, namespace := range namespaces {
if lastChange, ok := h.lastStateChange.Load(namespace); ok {
times = append(times, lastChange)
}
}
} else {
h.lastStateChange.Range(func(key string, value time.Time) bool {
times = append(times, value)

return true
})
}

sort.Slice(times, func(i, j int) bool {
Expand Down
55 changes: 55 additions & 0 deletions cmd/headscale/cli/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ func init() {
}
nodeCmd.AddCommand(expireNodeCmd)

renameNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
err = renameNodeCmd.MarkFlagRequired("identifier")
if err != nil {
log.Fatalf(err.Error())
}
nodeCmd.AddCommand(renameNodeCmd)

deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
err = deleteNodeCmd.MarkFlagRequired("identifier")
if err != nil {
Expand Down Expand Up @@ -240,6 +247,54 @@ var expireNodeCmd = &cobra.Command{
},
}

var renameNodeCmd = &cobra.Command{
Use: "rename NEW_NAME",
Short: "Renames a machine in your network",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")

identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)

return
}

ctx, client, conn, cancel := getHeadscaleCLIClient()
defer cancel()
defer conn.Close()

newName := ""
if len(args) > 0 {
newName = args[0]
}
request := &v1.RenameMachineRequest{
MachineId: identifier,
NewName: newName,
}

response, err := client.RenameMachine(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf(
"Cannot rename machine: %s\n",
status.Convert(err).Message(),
),
output,
)

return
}

SuccessOutput(response.Machine, "Machine renamed", output)
},
}

var deleteNodeCmd = &cobra.Command{
Use: "delete",
Short: "Delete a node",
Expand Down
43 changes: 41 additions & 2 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ func (h *Headscale) initDB() error {
}

_ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses")
_ = db.Migrator().RenameColumn(&Machine{}, "name", "hostname")

// GivenName is used as the primary source of DNS names, make sure
// the field is populated and normalized if it was not when the
// machine was registered.
_ = db.Migrator().RenameColumn(&Machine{}, "nickname", "given_name")

// If the Machine table has a column for registered,
// find all occourences of "false" and drop them. Then
Expand All @@ -54,13 +60,13 @@ func (h *Headscale) initDB() error {

for _, machine := range machines {
log.Info().
Str("machine", machine.Name).
Str("machine", machine.Hostname).
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", machine.Hostname).
Str("machine_key", machine.MachineKey).
Msg("Error deleting unregistered machine")
}
Expand All @@ -77,6 +83,39 @@ func (h *Headscale) initDB() error {
return err
}

if db.Migrator().HasColumn(&Machine{}, "given_name") {
machines := Machines{}
if err := h.db.Find(&machines).Error; err != nil {
log.Error().Err(err).Msg("Error accessing db")
}

for _, machine := range machines {
if machine.GivenName == "" {
normalizedHostname, err := NormalizeToFQDNRules(
machine.Hostname,
h.cfg.OIDC.StripEmaildomain,
)
if err != nil {
log.Error().
Caller().
Str("hostname", machine.Hostname).
Err(err).
Msg("Failed to normalize machine hostname in DB migration")
}

err = h.RenameMachine(&machine, normalizedHostname)
if err != nil {
log.Error().
Caller().
Str("hostname", machine.Hostname).
Err(err).
Msg("Failed to save normalized machine name in DB migration")
}

}
}
}

err = db.AutoMigrate(&KV{})
if err != nil {
return err
Expand Down
Loading

0 comments on commit be25bbc

Please sign in to comment.