Skip to content

Commit f2e1e42

Browse files
authored
Merge pull request #33 from juanfont/fix-nodekey-change
Handle client sending new NodeKey
2 parents 0fcd92f + aab0bfe commit f2e1e42

File tree

1 file changed

+73
-58
lines changed

1 file changed

+73
-58
lines changed

api.go

Lines changed: 73 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -81,51 +81,65 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) {
8181
return
8282
}
8383
defer db.Close()
84-
var m Machine
85-
resp := tailcfg.RegisterResponse{}
8684

85+
var m Machine
8786
if db.First(&m, "machine_key = ?", mKey.HexString()).RecordNotFound() {
8887
log.Println("New Machine!")
89-
h.handleNewServer(c, db, mKey, req)
88+
m = Machine{
89+
Expiry: &req.Expiry,
90+
MachineKey: mKey.HexString(),
91+
Name: req.Hostinfo.Hostname,
92+
NodeKey: wgcfg.Key(req.NodeKey).HexString(),
93+
}
94+
if err := db.Create(&m).Error; err != nil {
95+
log.Printf("Could not create row: %s", err)
96+
return
97+
}
98+
}
99+
100+
if !m.Registered && req.Auth.AuthKey != "" {
101+
h.handleAuthKey(c, db, mKey, req, m)
90102
return
91103
}
92104

93-
// We do have the updated key!
105+
resp := tailcfg.RegisterResponse{}
106+
107+
// We have the updated key!
94108
if m.NodeKey == wgcfg.Key(req.NodeKey).HexString() {
95109
if m.Registered {
96-
log.Printf("[%s] Client is registered and we have the current key. All clear to /map\n", m.Name)
110+
log.Printf("[%s] Client is registered and we have the current NodeKey. All clear to /map", m.Name)
97111
resp.AuthURL = ""
98-
resp.User = *m.Namespace.toUser()
99112
resp.MachineAuthorized = true
113+
resp.User = *m.Namespace.toUser()
100114
respBody, err := encode(resp, &mKey, h.privateKey)
101115
if err != nil {
102116
log.Printf("Cannot encode message: %s", err)
103-
c.String(http.StatusInternalServerError, "Extremely sad!")
117+
c.String(http.StatusInternalServerError, "")
104118
return
105119
}
106120
c.Data(200, "application/json; charset=utf-8", respBody)
107121
return
108122
}
109123

110-
log.Println("Hey! Not registered. Not asking for key rotation. Send a passive-aggressive authurl to register")
124+
log.Printf("[%s] Not registered and not NodeKey rotation. Sending a authurl to register", m.Name)
111125
resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
112126
h.cfg.ServerURL, mKey.HexString())
113127
respBody, err := encode(resp, &mKey, h.privateKey)
114128
if err != nil {
115129
log.Printf("Cannot encode message: %s", err)
116-
c.String(http.StatusInternalServerError, "Extremely sad!")
130+
c.String(http.StatusInternalServerError, "")
117131
return
118132
}
119133
c.Data(200, "application/json; charset=utf-8", respBody)
120134
return
121-
122135
}
123136

124-
// We dont have the updated key in the DB. Lets try with the old one.
137+
// The NodeKey we have matches OldNodeKey, which means this is a refresh after an key expiration
125138
if m.NodeKey == wgcfg.Key(req.OldNodeKey).HexString() {
126-
log.Println("Key rotation!")
139+
log.Printf("[%s] We have the OldNodeKey in the database. This is a key refresh", m.Name)
127140
m.NodeKey = wgcfg.Key(req.NodeKey).HexString()
128141
db.Save(&m)
142+
129143
resp.AuthURL = ""
130144
resp.User = *m.Namespace.toUser()
131145
respBody, err := encode(resp, &mKey, h.privateKey)
@@ -138,8 +152,32 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) {
138152
return
139153
}
140154

141-
log.Println("We dont know anything about the new key. WTF")
142-
// spew.Dump(req)
155+
// We arrive here after a client is restarted without finalizing the authentication flow or
156+
// when headscale is stopped in the middle of the auth process.
157+
if m.Registered {
158+
log.Printf("[%s] The node is sending us a new NodeKey, but machine is registered. All clear for /map", m.Name)
159+
resp.AuthURL = ""
160+
resp.MachineAuthorized = true
161+
resp.User = *m.Namespace.toUser()
162+
respBody, err := encode(resp, &mKey, h.privateKey)
163+
if err != nil {
164+
log.Printf("Cannot encode message: %s", err)
165+
c.String(http.StatusInternalServerError, "")
166+
return
167+
}
168+
c.Data(200, "application/json; charset=utf-8", respBody)
169+
return
170+
}
171+
log.Printf("[%s] The node is sending us a new NodeKey, sending auth url", m.Name)
172+
resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
173+
h.cfg.ServerURL, mKey.HexString())
174+
respBody, err := encode(resp, &mKey, h.privateKey)
175+
if err != nil {
176+
log.Printf("Cannot encode message: %s", err)
177+
c.String(http.StatusInternalServerError, "")
178+
return
179+
}
180+
c.Data(200, "application/json; charset=utf-8", respBody)
143181
}
144182

145183
// PollNetMapHandler takes care of /machine/:id/map
@@ -390,66 +428,43 @@ func (h *Headscale) getMapKeepAliveResponse(mKey wgcfg.Key, req tailcfg.MapReque
390428
return &data, nil
391429
}
392430

393-
func (h *Headscale) handleNewServer(c *gin.Context, db *gorm.DB, idKey wgcfg.Key, req tailcfg.RegisterRequest) {
394-
m := Machine{
395-
MachineKey: idKey.HexString(),
396-
NodeKey: wgcfg.Key(req.NodeKey).HexString(),
397-
Expiry: &req.Expiry,
398-
Name: req.Hostinfo.Hostname,
399-
}
400-
if err := db.Create(&m).Error; err != nil {
401-
log.Printf("Could not create row: %s", err)
402-
return
403-
}
404-
431+
func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgcfg.Key, req tailcfg.RegisterRequest, m Machine) {
405432
resp := tailcfg.RegisterResponse{}
406-
407-
if req.Auth.AuthKey != "" {
408-
pak, err := h.checkKeyValidity(req.Auth.AuthKey)
409-
if err != nil {
410-
resp.MachineAuthorized = false
411-
respBody, err := encode(resp, &idKey, h.privateKey)
412-
if err != nil {
413-
log.Printf("Cannot encode message: %s", err)
414-
c.String(http.StatusInternalServerError, "")
415-
return
416-
}
417-
c.Data(200, "application/json; charset=utf-8", respBody)
418-
return
419-
}
420-
ip, err := h.getAvailableIP()
421-
if err != nil {
422-
log.Println(err)
423-
return
424-
}
425-
426-
m.IPAddress = ip.String()
427-
m.NamespaceID = pak.NamespaceID
428-
m.AuthKeyID = uint(pak.ID)
429-
m.RegisterMethod = "authKey"
430-
m.Registered = true
431-
db.Save(&m)
432-
433-
resp.MachineAuthorized = true
434-
resp.User = *pak.Namespace.toUser()
433+
pak, err := h.checkKeyValidity(req.Auth.AuthKey)
434+
if err != nil {
435+
resp.MachineAuthorized = false
435436
respBody, err := encode(resp, &idKey, h.privateKey)
436437
if err != nil {
437438
log.Printf("Cannot encode message: %s", err)
438-
c.String(http.StatusInternalServerError, "Extremely sad!")
439+
c.String(http.StatusInternalServerError, "")
439440
return
440441
}
441442
c.Data(200, "application/json; charset=utf-8", respBody)
443+
log.Printf("[%s] Failed authentication via AuthKey", m.Name)
444+
return
445+
}
446+
ip, err := h.getAvailableIP()
447+
if err != nil {
448+
log.Println(err)
442449
return
443450
}
444451

445-
resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
446-
h.cfg.ServerURL, idKey.HexString())
452+
m.AuthKeyID = uint(pak.ID)
453+
m.IPAddress = ip.String()
454+
m.NamespaceID = pak.NamespaceID
455+
m.NodeKey = wgcfg.Key(req.NodeKey).HexString() // we update it just in case
456+
m.Registered = true
457+
m.RegisterMethod = "authKey"
458+
db.Save(&m)
447459

460+
resp.MachineAuthorized = true
461+
resp.User = *pak.Namespace.toUser()
448462
respBody, err := encode(resp, &idKey, h.privateKey)
449463
if err != nil {
450464
log.Printf("Cannot encode message: %s", err)
451465
c.String(http.StatusInternalServerError, "Extremely sad!")
452466
return
453467
}
454468
c.Data(200, "application/json; charset=utf-8", respBody)
469+
log.Printf("[%s] Successfully authenticated via AuthKey", m.Name)
455470
}

0 commit comments

Comments
 (0)