mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Handle client sending new NodeKey (fixes #32)
This commit is contained in:
		
							parent
							
								
									0fcd92fcce
								
							
						
					
					
						commit
						d4c2870d7e
					
				
							
								
								
									
										132
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								api.go
									
									
									
									
									
								
							@ -81,51 +81,65 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer db.Close()
 | 
			
		||||
	var m Machine
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	var m Machine
 | 
			
		||||
	if db.First(&m, "machine_key = ?", mKey.HexString()).RecordNotFound() {
 | 
			
		||||
		log.Println("New Machine!")
 | 
			
		||||
		h.handleNewServer(c, db, mKey, req)
 | 
			
		||||
		m = Machine{
 | 
			
		||||
			Expiry:     &req.Expiry,
 | 
			
		||||
			MachineKey: mKey.HexString(),
 | 
			
		||||
			Name:       req.Hostinfo.Hostname,
 | 
			
		||||
			NodeKey:    wgcfg.Key(req.NodeKey).HexString(),
 | 
			
		||||
		}
 | 
			
		||||
		if err := db.Create(&m).Error; err != nil {
 | 
			
		||||
			log.Printf("Could not create row: %s", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !m.Registered && req.Auth.AuthKey != "" {
 | 
			
		||||
		h.handleAuthKey(c, db, mKey, req, m)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We do have the updated key!
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	// We have the updated key!
 | 
			
		||||
	if m.NodeKey == wgcfg.Key(req.NodeKey).HexString() {
 | 
			
		||||
		if m.Registered {
 | 
			
		||||
			log.Printf("[%s] Client is registered and we have the current key. All clear to /map\n", m.Name)
 | 
			
		||||
			log.Printf("[%s] Client is registered and we have the current NodeKey. All clear to /map", m.Name)
 | 
			
		||||
			resp.AuthURL = ""
 | 
			
		||||
			resp.User = *m.Namespace.toUser()
 | 
			
		||||
			resp.MachineAuthorized = true
 | 
			
		||||
			resp.User = *m.Namespace.toUser()
 | 
			
		||||
			respBody, err := encode(resp, &mKey, h.privateKey)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
				c.String(http.StatusInternalServerError, "Extremely sad!")
 | 
			
		||||
				c.String(http.StatusInternalServerError, "")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Println("Hey! Not registered. Not asking for key rotation. Send a passive-aggressive authurl to register")
 | 
			
		||||
		log.Printf("[%s] Not registered and not NodeKey rotation. Sending a authurl to register", m.Name)
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
			h.cfg.ServerURL, mKey.HexString())
 | 
			
		||||
		respBody, err := encode(resp, &mKey, h.privateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
			c.String(http.StatusInternalServerError, "Extremely sad!")
 | 
			
		||||
			c.String(http.StatusInternalServerError, "")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We dont have the updated key in the DB. Lets try with the old one.
 | 
			
		||||
	// The NodeKey we have matches OldNodeKey, which means this is a refresh after an key expiration
 | 
			
		||||
	if m.NodeKey == wgcfg.Key(req.OldNodeKey).HexString() {
 | 
			
		||||
		log.Println("Key rotation!")
 | 
			
		||||
		log.Printf("[%s] We have the NodeKey in the database. This is a key refresh", m.Name)
 | 
			
		||||
		m.NodeKey = wgcfg.Key(req.NodeKey).HexString()
 | 
			
		||||
		db.Save(&m)
 | 
			
		||||
 | 
			
		||||
		resp.AuthURL = ""
 | 
			
		||||
		resp.User = *m.Namespace.toUser()
 | 
			
		||||
		respBody, err := encode(resp, &mKey, h.privateKey)
 | 
			
		||||
@ -138,8 +152,32 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Println("We dont know anything about the new key. WTF")
 | 
			
		||||
	// spew.Dump(req)
 | 
			
		||||
	// We arrive here after a client is restarted without finalizing the authentication flow or
 | 
			
		||||
	// when headscale is stopped in the middle of the auth process.
 | 
			
		||||
	if m.Registered {
 | 
			
		||||
		log.Printf("[%s] The node is sending us a new NodeKey, but machine is registered. All clear for /map", m.Name)
 | 
			
		||||
		resp.AuthURL = ""
 | 
			
		||||
		resp.MachineAuthorized = true
 | 
			
		||||
		resp.User = *m.Namespace.toUser()
 | 
			
		||||
		respBody, err := encode(resp, &mKey, h.privateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
			c.String(http.StatusInternalServerError, "")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Printf("[%s] The node is sending us a new NodeKey, sending auth url", m.Name)
 | 
			
		||||
	resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
		h.cfg.ServerURL, mKey.HexString())
 | 
			
		||||
	respBody, err := encode(resp, &mKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
		c.String(http.StatusInternalServerError, "")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PollNetMapHandler takes care of /machine/:id/map
 | 
			
		||||
@ -390,61 +428,37 @@ func (h *Headscale) getMapKeepAliveResponse(mKey wgcfg.Key, req tailcfg.MapReque
 | 
			
		||||
	return &data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleNewServer(c *gin.Context, db *gorm.DB, idKey wgcfg.Key, req tailcfg.RegisterRequest) {
 | 
			
		||||
	m := Machine{
 | 
			
		||||
		MachineKey: idKey.HexString(),
 | 
			
		||||
		NodeKey:    wgcfg.Key(req.NodeKey).HexString(),
 | 
			
		||||
		Expiry:     &req.Expiry,
 | 
			
		||||
		Name:       req.Hostinfo.Hostname,
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Create(&m).Error; err != nil {
 | 
			
		||||
		log.Printf("Could not create row: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgcfg.Key, req tailcfg.RegisterRequest, m Machine) {
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	if req.Auth.AuthKey != "" {
 | 
			
		||||
		pak, err := h.checkKeyValidity(req.Auth.AuthKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			resp.MachineAuthorized = false
 | 
			
		||||
			respBody, err := encode(resp, &idKey, h.privateKey)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
				c.String(http.StatusInternalServerError, "")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ip, err := h.getAvailableIP()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Println(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m.IPAddress = ip.String()
 | 
			
		||||
		m.NamespaceID = pak.NamespaceID
 | 
			
		||||
		m.AuthKeyID = uint(pak.ID)
 | 
			
		||||
		m.RegisterMethod = "authKey"
 | 
			
		||||
		m.Registered = true
 | 
			
		||||
		db.Save(&m)
 | 
			
		||||
 | 
			
		||||
		resp.MachineAuthorized = true
 | 
			
		||||
		resp.User = *pak.Namespace.toUser()
 | 
			
		||||
	pak, err := h.checkKeyValidity(req.Auth.AuthKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.MachineAuthorized = false
 | 
			
		||||
		respBody, err := encode(resp, &idKey, h.privateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
			c.String(http.StatusInternalServerError, "Extremely sad!")
 | 
			
		||||
			c.String(http.StatusInternalServerError, "")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
		log.Printf("[%s] Failed authentication via AuthKey", m.Name)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ip, err := h.getAvailableIP()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
		h.cfg.ServerURL, idKey.HexString())
 | 
			
		||||
	m.AuthKeyID = uint(pak.ID)
 | 
			
		||||
	m.IPAddress = ip.String()
 | 
			
		||||
	m.NamespaceID = pak.NamespaceID
 | 
			
		||||
	m.NodeKey = wgcfg.Key(req.NodeKey).HexString() // we update it just in case
 | 
			
		||||
	m.Registered = true
 | 
			
		||||
	m.RegisterMethod = "authKey"
 | 
			
		||||
	db.Save(&m)
 | 
			
		||||
 | 
			
		||||
	resp.MachineAuthorized = true
 | 
			
		||||
	resp.User = *pak.Namespace.toUser()
 | 
			
		||||
	respBody, err := encode(resp, &idKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Cannot encode message: %s", err)
 | 
			
		||||
@ -452,4 +466,6 @@ func (h *Headscale) handleNewServer(c *gin.Context, db *gorm.DB, idKey wgcfg.Key
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.Data(200, "application/json; charset=utf-8", respBody)
 | 
			
		||||
	log.Printf("[%s] Successfully authenticated via AuthKey", m.Name)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user