mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-11-04 01:51:04 +01:00 
			
		
		
		
	Merge branch 'main' into kradalby-patch-2
This commit is contained in:
		
						commit
						aab4a6043a
					
				@ -25,9 +25,6 @@ linters:
 | 
			
		||||
    - godox
 | 
			
		||||
    - ireturn
 | 
			
		||||
 | 
			
		||||
    # In progress
 | 
			
		||||
    - gocritic
 | 
			
		||||
 | 
			
		||||
    # We should strive to enable these:
 | 
			
		||||
    - wrapcheck
 | 
			
		||||
    - dupl
 | 
			
		||||
@ -51,3 +48,9 @@ linters-settings:
 | 
			
		||||
      - ip
 | 
			
		||||
      - ok
 | 
			
		||||
      - c
 | 
			
		||||
 | 
			
		||||
  gocritic:
 | 
			
		||||
    disabled-checks:
 | 
			
		||||
      - appendAssign
 | 
			
		||||
      # TODO(kradalby): Remove this
 | 
			
		||||
      - ifElseChain
 | 
			
		||||
 | 
			
		||||
@ -100,7 +100,7 @@ func (s *Suite) TestPortNamespace(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      ip.String(),
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -142,7 +142,7 @@ func (s *Suite) TestPortGroup(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      ip.String(),
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										466
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										466
									
								
								api.go
									
									
									
									
									
								
							@ -18,7 +18,15 @@ import (
 | 
			
		||||
	"tailscale.com/types/wgkey"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const reservedResponseHeaderSize = 4
 | 
			
		||||
const (
 | 
			
		||||
	reservedResponseHeaderSize               = 4
 | 
			
		||||
	RegisterMethodAuthKey                    = "authKey"
 | 
			
		||||
	RegisterMethodOIDC                       = "oidc"
 | 
			
		||||
	RegisterMethodCLI                        = "cli"
 | 
			
		||||
	ErrRegisterMethodCLIDoesNotSupportExpire = Error(
 | 
			
		||||
		"machines registered with CLI does not support expire",
 | 
			
		||||
	)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// KeyHandler provides the Headscale pub key
 | 
			
		||||
// Listens in /key.
 | 
			
		||||
@ -111,178 +119,52 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
 | 
			
		||||
		machine = &newMachine
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !machine.Registered && req.Auth.AuthKey != "" {
 | 
			
		||||
		h.handleAuthKey(ctx, h.db, machineKey, req, *machine)
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	// We have the updated key!
 | 
			
		||||
	if machine.NodeKey == wgkey.Key(req.NodeKey).HexString() {
 | 
			
		||||
		// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
 | 
			
		||||
		//   https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
 | 
			
		||||
		if !req.Expiry.IsZero() && req.Expiry.UTC().Before(now) {
 | 
			
		||||
			log.Info().
 | 
			
		||||
				Str("handler", "Registration").
 | 
			
		||||
				Str("machine", machine.Name).
 | 
			
		||||
				Msg("Client requested logout")
 | 
			
		||||
 | 
			
		||||
			machine.Expiry = &req.Expiry // save the expiry so that the machine is marked as expired
 | 
			
		||||
			h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
			resp.AuthURL = ""
 | 
			
		||||
			resp.MachineAuthorized = false
 | 
			
		||||
			resp.User = *machine.Namespace.toUser()
 | 
			
		||||
			respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error().
 | 
			
		||||
					Str("handler", "Registration").
 | 
			
		||||
					Err(err).
 | 
			
		||||
					Msg("Cannot encode message")
 | 
			
		||||
				ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
	if machine.Registered {
 | 
			
		||||
		// If the NodeKey stored in headscale is the same as the key presented in a registration
 | 
			
		||||
		// request, then we have a node that is either:
 | 
			
		||||
		// - Trying to log out (sending a expiry in the past)
 | 
			
		||||
		// - A valid, registered machine, looking for the node map
 | 
			
		||||
		// - Expired machine wanting to reauthenticate
 | 
			
		||||
		if machine.NodeKey == wgkey.Key(req.NodeKey).HexString() {
 | 
			
		||||
			// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
 | 
			
		||||
			//   https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
 | 
			
		||||
			if !req.Expiry.IsZero() && req.Expiry.UTC().Before(now) {
 | 
			
		||||
				h.handleMachineLogOut(ctx, machineKey, *machine)
 | 
			
		||||
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if machine.Registered && machine.Expiry.UTC().After(now) {
 | 
			
		||||
			// The machine registration is valid, respond with redirect to /map
 | 
			
		||||
			log.Debug().
 | 
			
		||||
				Str("handler", "Registration").
 | 
			
		||||
				Str("machine", machine.Name).
 | 
			
		||||
				Msg("Client is registered and we have the current NodeKey. All clear to /map")
 | 
			
		||||
 | 
			
		||||
			resp.AuthURL = ""
 | 
			
		||||
			resp.MachineAuthorized = true
 | 
			
		||||
			resp.User = *machine.Namespace.toUser()
 | 
			
		||||
			resp.Login = *machine.Namespace.toLogin()
 | 
			
		||||
 | 
			
		||||
			respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error().
 | 
			
		||||
					Str("handler", "Registration").
 | 
			
		||||
					Err(err).
 | 
			
		||||
					Msg("Cannot encode message")
 | 
			
		||||
				machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name).
 | 
			
		||||
					Inc()
 | 
			
		||||
				ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
			// If machine is not expired, and is register, we have a already accepted this machine,
 | 
			
		||||
			// let it proceed with a valid registration
 | 
			
		||||
			if !machine.isExpired() {
 | 
			
		||||
				h.handleMachineValidRegistration(ctx, machineKey, *machine)
 | 
			
		||||
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name).
 | 
			
		||||
				Inc()
 | 
			
		||||
			ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
 | 
			
		||||
		if machine.NodeKey == wgkey.Key(req.OldNodeKey).HexString() &&
 | 
			
		||||
			!machine.isExpired() {
 | 
			
		||||
			h.handleMachineRefreshKey(ctx, machineKey, req, *machine)
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The client has registered before, but has expired
 | 
			
		||||
		log.Debug().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("Machine registration has expired. Sending a authurl to register")
 | 
			
		||||
 | 
			
		||||
		if h.cfg.OIDC.Issuer != "" {
 | 
			
		||||
			resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
 | 
			
		||||
				strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.HexString())
 | 
			
		||||
		} else {
 | 
			
		||||
			resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
				strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.HexString())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// When a client connects, it may request a specific expiry time in its
 | 
			
		||||
		// RegisterRequest (https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L634)
 | 
			
		||||
		// RequestedExpiry is used to store the clients requested expiry time since the authentication flow is broken
 | 
			
		||||
		// into two steps (which cant pass arbitrary data between them easily) and needs to be
 | 
			
		||||
		// retrieved again after the user has authenticated. After the authentication flow
 | 
			
		||||
		// completes, RequestedExpiry is copied into Expiry.
 | 
			
		||||
		machine.RequestedExpiry = &req.Expiry
 | 
			
		||||
 | 
			
		||||
		h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
		respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error().
 | 
			
		||||
				Str("handler", "Registration").
 | 
			
		||||
				Err(err).
 | 
			
		||||
				Msg("Cannot encode message")
 | 
			
		||||
			machineRegistrations.WithLabelValues("new", "web", "error", machine.Namespace.Name).
 | 
			
		||||
				Inc()
 | 
			
		||||
			ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		machineRegistrations.WithLabelValues("new", "web", "success", machine.Namespace.Name).
 | 
			
		||||
			Inc()
 | 
			
		||||
		ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
		// The machine has expired
 | 
			
		||||
		h.handleMachineExpired(ctx, machineKey, req, *machine)
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
 | 
			
		||||
	if machine.NodeKey == wgkey.Key(req.OldNodeKey).HexString() &&
 | 
			
		||||
		machine.Expiry.UTC().After(now) {
 | 
			
		||||
		log.Debug().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("We have the OldNodeKey in the database. This is a key refresh")
 | 
			
		||||
		machine.NodeKey = wgkey.Key(req.NodeKey).HexString()
 | 
			
		||||
		h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
		resp.AuthURL = ""
 | 
			
		||||
		resp.User = *machine.Namespace.toUser()
 | 
			
		||||
		respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error().
 | 
			
		||||
				Str("handler", "Registration").
 | 
			
		||||
				Err(err).
 | 
			
		||||
				Msg("Cannot encode message")
 | 
			
		||||
			ctx.String(http.StatusInternalServerError, "Extremely sad!")
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
	// If the machine has AuthKey set, handle registration via PreAuthKeys
 | 
			
		||||
	if req.Auth.AuthKey != "" {
 | 
			
		||||
		h.handleAuthKey(ctx, machineKey, req, *machine)
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The machine registration is new, redirect the client to the registration URL
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("handler", "Registration").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("The node is sending us a new NodeKey, sending auth url")
 | 
			
		||||
	if h.cfg.OIDC.Issuer != "" {
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf(
 | 
			
		||||
			"%s/oidc/register/%s",
 | 
			
		||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
			
		||||
			machineKey.HexString(),
 | 
			
		||||
		)
 | 
			
		||||
	} else {
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.HexString())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// save the requested expiry time for retrieval later in the authentication flow
 | 
			
		||||
	machine.RequestedExpiry = &req.Expiry
 | 
			
		||||
	machine.NodeKey = wgkey.Key(req.NodeKey).HexString() // save the NodeKey
 | 
			
		||||
	h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Cannot encode message")
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
	h.handleMachineRegistrationNew(ctx, machineKey, req, *machine)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) getMapResponse(
 | 
			
		||||
@ -304,7 +186,7 @@ func (h *Headscale) getMapResponse(
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	peers, err := h.getPeers(machine)
 | 
			
		||||
	peers, err := h.getValidPeers(machine)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("func", "getMapResponse").
 | 
			
		||||
@ -404,19 +286,211 @@ func (h *Headscale) getMapKeepAliveResponse(
 | 
			
		||||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleMachineLogOut(
 | 
			
		||||
	ctx *gin.Context,
 | 
			
		||||
	machineKey wgkey.Key,
 | 
			
		||||
	machine Machine,
 | 
			
		||||
) {
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	log.Info().
 | 
			
		||||
		Str("handler", "Registration").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("Client requested logout")
 | 
			
		||||
 | 
			
		||||
	h.ExpireMachine(&machine)
 | 
			
		||||
 | 
			
		||||
	resp.AuthURL = ""
 | 
			
		||||
	resp.MachineAuthorized = false
 | 
			
		||||
	resp.User = *machine.Namespace.toUser()
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Cannot encode message")
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleMachineValidRegistration(
 | 
			
		||||
	ctx *gin.Context,
 | 
			
		||||
	machineKey wgkey.Key,
 | 
			
		||||
	machine Machine,
 | 
			
		||||
) {
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	// The machine registration is valid, respond with redirect to /map
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("handler", "Registration").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("Client is registered and we have the current NodeKey. All clear to /map")
 | 
			
		||||
 | 
			
		||||
	resp.AuthURL = ""
 | 
			
		||||
	resp.MachineAuthorized = true
 | 
			
		||||
	resp.User = *machine.Namespace.toUser()
 | 
			
		||||
	resp.Login = *machine.Namespace.toLogin()
 | 
			
		||||
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Cannot encode message")
 | 
			
		||||
		machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name).
 | 
			
		||||
			Inc()
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name).
 | 
			
		||||
		Inc()
 | 
			
		||||
	ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleMachineExpired(
 | 
			
		||||
	ctx *gin.Context,
 | 
			
		||||
	machineKey wgkey.Key,
 | 
			
		||||
	registerRequest tailcfg.RegisterRequest,
 | 
			
		||||
	machine Machine,
 | 
			
		||||
) {
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	// The client has registered before, but has expired
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("handler", "Registration").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("Machine registration has expired. Sending a authurl to register")
 | 
			
		||||
 | 
			
		||||
	if registerRequest.Auth.AuthKey != "" {
 | 
			
		||||
		h.handleAuthKey(ctx, machineKey, registerRequest, machine)
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if h.cfg.OIDC.Issuer != "" {
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
 | 
			
		||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.HexString())
 | 
			
		||||
	} else {
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.HexString())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Cannot encode message")
 | 
			
		||||
		machineRegistrations.WithLabelValues("reauth", "web", "error", machine.Namespace.Name).
 | 
			
		||||
			Inc()
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	machineRegistrations.WithLabelValues("reauth", "web", "success", machine.Namespace.Name).
 | 
			
		||||
		Inc()
 | 
			
		||||
	ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleMachineRefreshKey(
 | 
			
		||||
	ctx *gin.Context,
 | 
			
		||||
	machineKey wgkey.Key,
 | 
			
		||||
	registerRequest tailcfg.RegisterRequest,
 | 
			
		||||
	machine Machine,
 | 
			
		||||
) {
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("handler", "Registration").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("We have the OldNodeKey in the database. This is a key refresh")
 | 
			
		||||
	machine.NodeKey = wgkey.Key(registerRequest.NodeKey).HexString()
 | 
			
		||||
	h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
	resp.AuthURL = ""
 | 
			
		||||
	resp.User = *machine.Namespace.toUser()
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Cannot encode message")
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "Extremely sad!")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleMachineRegistrationNew(
 | 
			
		||||
	ctx *gin.Context,
 | 
			
		||||
	machineKey wgkey.Key,
 | 
			
		||||
	registerRequest tailcfg.RegisterRequest,
 | 
			
		||||
	machine Machine,
 | 
			
		||||
) {
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
 | 
			
		||||
	// The machine registration is new, redirect the client to the registration URL
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("handler", "Registration").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("The node is sending us a new NodeKey, sending auth url")
 | 
			
		||||
	if h.cfg.OIDC.Issuer != "" {
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf(
 | 
			
		||||
			"%s/oidc/register/%s",
 | 
			
		||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"),
 | 
			
		||||
			machineKey.HexString(),
 | 
			
		||||
		)
 | 
			
		||||
	} else {
 | 
			
		||||
		resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
 | 
			
		||||
			strings.TrimSuffix(h.cfg.ServerURL, "/"), machineKey.HexString())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !registerRequest.Expiry.IsZero() {
 | 
			
		||||
		log.Trace().
 | 
			
		||||
			Caller().
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Time("expiry", registerRequest.Expiry).
 | 
			
		||||
			Msg("Non-zero expiry time requested, adding to cache")
 | 
			
		||||
		h.requestedExpiryCache.Set(
 | 
			
		||||
			machineKey.HexString(),
 | 
			
		||||
			registerRequest.Expiry,
 | 
			
		||||
			requestedExpiryCacheExpiration,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	machine.NodeKey = wgkey.Key(registerRequest.NodeKey).HexString() // save the NodeKey
 | 
			
		||||
	h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("handler", "Registration").
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Cannot encode message")
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) handleAuthKey(
 | 
			
		||||
	ctx *gin.Context,
 | 
			
		||||
	db *gorm.DB,
 | 
			
		||||
	idKey wgkey.Key,
 | 
			
		||||
	reqisterRequest tailcfg.RegisterRequest,
 | 
			
		||||
	machineKey wgkey.Key,
 | 
			
		||||
	registerRequest tailcfg.RegisterRequest,
 | 
			
		||||
	machine Machine,
 | 
			
		||||
) {
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("func", "handleAuthKey").
 | 
			
		||||
		Str("machine", reqisterRequest.Hostinfo.Hostname).
 | 
			
		||||
		Msgf("Processing auth key for %s", reqisterRequest.Hostinfo.Hostname)
 | 
			
		||||
		Str("machine", registerRequest.Hostinfo.Hostname).
 | 
			
		||||
		Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
 | 
			
		||||
	resp := tailcfg.RegisterResponse{}
 | 
			
		||||
	pak, err := h.checkKeyValidity(reqisterRequest.Auth.AuthKey)
 | 
			
		||||
	pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("func", "handleAuthKey").
 | 
			
		||||
@ -424,7 +498,7 @@ func (h *Headscale) handleAuthKey(
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Msg("Failed authentication via AuthKey")
 | 
			
		||||
		resp.MachineAuthorized = false
 | 
			
		||||
		respBody, err := encode(resp, &idKey, h.privateKey)
 | 
			
		||||
		respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error().
 | 
			
		||||
				Str("func", "handleAuthKey").
 | 
			
		||||
@ -448,43 +522,53 @@ func (h *Headscale) handleAuthKey(
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debug().
 | 
			
		||||
		Str("func", "handleAuthKey").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("Authentication key was valid, proceeding to acquire an IP address")
 | 
			
		||||
	ip, err := h.getAvailableIP()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
	if machine.isRegistered() {
 | 
			
		||||
		log.Trace().
 | 
			
		||||
			Caller().
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("machine already registered, reauthenticating")
 | 
			
		||||
 | 
			
		||||
		h.RefreshMachine(&machine, registerRequest.Expiry)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Debug().
 | 
			
		||||
			Str("func", "handleAuthKey").
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("Failed to find an available IP")
 | 
			
		||||
		machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name).
 | 
			
		||||
			Inc()
 | 
			
		||||
			Msg("Authentication key was valid, proceeding to acquire an IP address")
 | 
			
		||||
		ip, err := h.getAvailableIP()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error().
 | 
			
		||||
				Str("func", "handleAuthKey").
 | 
			
		||||
				Str("machine", machine.Name).
 | 
			
		||||
				Msg("Failed to find an available IP")
 | 
			
		||||
			machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name).
 | 
			
		||||
				Inc()
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		log.Info().
 | 
			
		||||
			Str("func", "handleAuthKey").
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Str("ip", ip.String()).
 | 
			
		||||
			Msgf("Assigning %s to %s", ip, machine.Name)
 | 
			
		||||
 | 
			
		||||
		machine.Expiry = ®isterRequest.Expiry
 | 
			
		||||
		machine.AuthKeyID = uint(pak.ID)
 | 
			
		||||
		machine.IPAddress = ip.String()
 | 
			
		||||
		machine.NamespaceID = pak.NamespaceID
 | 
			
		||||
		machine.NodeKey = wgkey.Key(registerRequest.NodeKey).
 | 
			
		||||
			HexString()
 | 
			
		||||
			// we update it just in case
 | 
			
		||||
		machine.Registered = true
 | 
			
		||||
		machine.RegisterMethod = RegisterMethodAuthKey
 | 
			
		||||
		h.db.Save(&machine)
 | 
			
		||||
	}
 | 
			
		||||
	log.Info().
 | 
			
		||||
		Str("func", "handleAuthKey").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Str("ip", ip.String()).
 | 
			
		||||
		Msgf("Assigning %s to %s", ip, machine.Name)
 | 
			
		||||
 | 
			
		||||
	machine.AuthKeyID = uint(pak.ID)
 | 
			
		||||
	machine.IPAddress = ip.String()
 | 
			
		||||
	machine.NamespaceID = pak.NamespaceID
 | 
			
		||||
	machine.NodeKey = wgkey.Key(reqisterRequest.NodeKey).
 | 
			
		||||
		HexString()
 | 
			
		||||
		// we update it just in case
 | 
			
		||||
	machine.Registered = true
 | 
			
		||||
	machine.RegisterMethod = "authKey"
 | 
			
		||||
	db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
	pak.Used = true
 | 
			
		||||
	db.Save(&pak)
 | 
			
		||||
	h.db.Save(&pak)
 | 
			
		||||
 | 
			
		||||
	resp.MachineAuthorized = true
 | 
			
		||||
	resp.User = *pak.Namespace.toUser()
 | 
			
		||||
	respBody, err := encode(resp, &idKey, h.privateKey)
 | 
			
		||||
	respBody, err := encode(resp, &machineKey, h.privateKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Str("func", "handleAuthKey").
 | 
			
		||||
@ -503,6 +587,6 @@ func (h *Headscale) handleAuthKey(
 | 
			
		||||
	log.Info().
 | 
			
		||||
		Str("func", "handleAuthKey").
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Str("ip", ip.String()).
 | 
			
		||||
		Str("ip", machine.IPAddress).
 | 
			
		||||
		Msg("Successfully authenticated via AuthKey")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								app.go
									
									
									
									
									
								
							@ -53,6 +53,9 @@ const (
 | 
			
		||||
	updateInterval  = 5000
 | 
			
		||||
	HTTPReadTimeout = 30 * time.Second
 | 
			
		||||
 | 
			
		||||
	requestedExpiryCacheExpiration      = time.Minute * 5
 | 
			
		||||
	requestedExpiryCacheCleanupInterval = time.Minute * 10
 | 
			
		||||
 | 
			
		||||
	errUnsupportedDatabase                 = Error("unsupported DB")
 | 
			
		||||
	errUnsupportedLetsEncryptChallengeType = Error(
 | 
			
		||||
		"unknown value for Lets Encrypt challenge type",
 | 
			
		||||
@ -96,9 +99,6 @@ type Config struct {
 | 
			
		||||
	OIDC OIDCConfig
 | 
			
		||||
 | 
			
		||||
	CLI CLIConfig
 | 
			
		||||
 | 
			
		||||
	MaxMachineRegistrationDuration     time.Duration
 | 
			
		||||
	DefaultMachineRegistrationDuration time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OIDCConfig struct {
 | 
			
		||||
@ -142,6 +142,8 @@ type Headscale struct {
 | 
			
		||||
	oidcProvider   *oidc.Provider
 | 
			
		||||
	oauth2Config   *oauth2.Config
 | 
			
		||||
	oidcStateCache *cache.Cache
 | 
			
		||||
 | 
			
		||||
	requestedExpiryCache *cache.Cache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHeadscale returns the Headscale app.
 | 
			
		||||
@ -174,13 +176,19 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
 | 
			
		||||
		return nil, errUnsupportedDatabase
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requestedExpiryCache := cache.New(
 | 
			
		||||
		requestedExpiryCacheExpiration,
 | 
			
		||||
		requestedExpiryCacheCleanupInterval,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	app := Headscale{
 | 
			
		||||
		cfg:        cfg,
 | 
			
		||||
		dbType:     cfg.DBtype,
 | 
			
		||||
		dbString:   dbString,
 | 
			
		||||
		privateKey: privKey,
 | 
			
		||||
		publicKey:  &pubKey,
 | 
			
		||||
		aclRules:   tailcfg.FilterAllowAll, // default allowall
 | 
			
		||||
		cfg:                  cfg,
 | 
			
		||||
		dbType:               cfg.DBtype,
 | 
			
		||||
		dbString:             dbString,
 | 
			
		||||
		privateKey:           privKey,
 | 
			
		||||
		publicKey:            &pubKey,
 | 
			
		||||
		aclRules:             tailcfg.FilterAllowAll, // default allowall
 | 
			
		||||
		requestedExpiryCache: requestedExpiryCache,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = app.initDB()
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/patrickmn/go-cache"
 | 
			
		||||
	"gopkg.in/check.v1"
 | 
			
		||||
	"inet.af/netaddr"
 | 
			
		||||
)
 | 
			
		||||
@ -47,6 +48,10 @@ func (s *Suite) ResetDB(c *check.C) {
 | 
			
		||||
		cfg:      cfg,
 | 
			
		||||
		dbType:   "sqlite3",
 | 
			
		||||
		dbString: tmpDir + "/headscale_test.db",
 | 
			
		||||
		requestedExpiryCache: cache.New(
 | 
			
		||||
			requestedExpiryCacheExpiration,
 | 
			
		||||
			requestedExpiryCacheCleanupInterval,
 | 
			
		||||
		),
 | 
			
		||||
	}
 | 
			
		||||
	err = app.initDB()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								cli_test.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								cli_test.go
									
									
									
									
									
								
							@ -13,15 +13,14 @@ func (s *Suite) TestRegisterMachine(c *check.C) {
 | 
			
		||||
	now := time.Now().UTC()
 | 
			
		||||
 | 
			
		||||
	machine := Machine{
 | 
			
		||||
		ID:              0,
 | 
			
		||||
		MachineKey:      "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e",
 | 
			
		||||
		NodeKey:         "bar",
 | 
			
		||||
		DiscoKey:        "faa",
 | 
			
		||||
		Name:            "testmachine",
 | 
			
		||||
		NamespaceID:     namespace.ID,
 | 
			
		||||
		IPAddress:       "10.0.0.1",
 | 
			
		||||
		Expiry:          &now,
 | 
			
		||||
		RequestedExpiry: &now,
 | 
			
		||||
		ID:          0,
 | 
			
		||||
		MachineKey:  "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e",
 | 
			
		||||
		NodeKey:     "bar",
 | 
			
		||||
		DiscoKey:    "faa",
 | 
			
		||||
		Name:        "testmachine",
 | 
			
		||||
		NamespaceID: namespace.ID,
 | 
			
		||||
		IPAddress:   "10.0.0.1",
 | 
			
		||||
		Expiry:      &now,
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,14 @@ func init() {
 | 
			
		||||
	}
 | 
			
		||||
	nodeCmd.AddCommand(registerNodeCmd)
 | 
			
		||||
 | 
			
		||||
	deleteNodeCmd.Flags().IntP("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	err = expireNodeCmd.MarkFlagRequired("identifier")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	nodeCmd.AddCommand(expireNodeCmd)
 | 
			
		||||
 | 
			
		||||
	deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	err = deleteNodeCmd.MarkFlagRequired("identifier")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf(err.Error())
 | 
			
		||||
@ -45,7 +52,7 @@ func init() {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	shareMachineCmd.Flags().IntP("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	shareMachineCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	err = shareMachineCmd.MarkFlagRequired("identifier")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf(err.Error())
 | 
			
		||||
@ -57,7 +64,7 @@ func init() {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	unshareMachineCmd.Flags().IntP("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	unshareMachineCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
 | 
			
		||||
	err = unshareMachineCmd.MarkFlagRequired("identifier")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf(err.Error())
 | 
			
		||||
@ -177,13 +184,58 @@ var listNodesCmd = &cobra.Command{
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var expireNodeCmd = &cobra.Command{
 | 
			
		||||
	Use:     "expire",
 | 
			
		||||
	Short:   "Expire (log out) a machine in your network",
 | 
			
		||||
	Long:    "Expiring a node will keep the node in the database and force it to reauthenticate.",
 | 
			
		||||
	Aliases: []string{"logout"},
 | 
			
		||||
	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()
 | 
			
		||||
 | 
			
		||||
		request := &v1.ExpireMachineRequest{
 | 
			
		||||
			MachineId: identifier,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		response, err := client.ExpireMachine(ctx, request)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ErrorOutput(
 | 
			
		||||
				err,
 | 
			
		||||
				fmt.Sprintf(
 | 
			
		||||
					"Cannot expire machine: %s\n",
 | 
			
		||||
					status.Convert(err).Message(),
 | 
			
		||||
				),
 | 
			
		||||
				output,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SuccessOutput(response.Machine, "Machine expired", output)
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var deleteNodeCmd = &cobra.Command{
 | 
			
		||||
	Use:   "delete",
 | 
			
		||||
	Short: "Delete a node",
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		output, _ := cmd.Flags().GetString("output")
 | 
			
		||||
 | 
			
		||||
		identifier, err := cmd.Flags().GetInt("identifier")
 | 
			
		||||
		identifier, err := cmd.Flags().GetUint64("identifier")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ErrorOutput(
 | 
			
		||||
				err,
 | 
			
		||||
@ -199,7 +251,7 @@ var deleteNodeCmd = &cobra.Command{
 | 
			
		||||
		defer conn.Close()
 | 
			
		||||
 | 
			
		||||
		getRequest := &v1.GetMachineRequest{
 | 
			
		||||
			MachineId: uint64(identifier),
 | 
			
		||||
			MachineId: identifier,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		getResponse, err := client.GetMachine(ctx, getRequest)
 | 
			
		||||
@ -217,7 +269,7 @@ var deleteNodeCmd = &cobra.Command{
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		deleteRequest := &v1.DeleteMachineRequest{
 | 
			
		||||
			MachineId: uint64(identifier),
 | 
			
		||||
			MachineId: identifier,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		confirm := false
 | 
			
		||||
@ -280,7 +332,7 @@ func sharingWorker(
 | 
			
		||||
	defer cancel()
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	identifier, err := cmd.Flags().GetInt("identifier")
 | 
			
		||||
	identifier, err := cmd.Flags().GetUint64("identifier")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ErrorOutput(err, fmt.Sprintf("Error converting ID to integer: %s", err), output)
 | 
			
		||||
 | 
			
		||||
@ -288,7 +340,7 @@ func sharingWorker(
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	machineRequest := &v1.GetMachineRequest{
 | 
			
		||||
		MachineId: uint64(identifier),
 | 
			
		||||
		MachineId: identifier,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	machineResponse, err := client.GetMachine(ctx, machineRequest)
 | 
			
		||||
@ -412,6 +464,7 @@ func nodesToPtables(
 | 
			
		||||
			"Ephemeral",
 | 
			
		||||
			"Last seen",
 | 
			
		||||
			"Online",
 | 
			
		||||
			"Expired",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -420,12 +473,19 @@ func nodesToPtables(
 | 
			
		||||
		if machine.PreAuthKey != nil && machine.PreAuthKey.Ephemeral {
 | 
			
		||||
			ephemeral = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var lastSeen time.Time
 | 
			
		||||
		var lastSeenTime string
 | 
			
		||||
		if machine.LastSeen != nil {
 | 
			
		||||
			lastSeen = machine.LastSeen.AsTime()
 | 
			
		||||
			lastSeenTime = lastSeen.Format("2006-01-02 15:04:05")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var expiry time.Time
 | 
			
		||||
		if machine.Expiry != nil {
 | 
			
		||||
			expiry = machine.Expiry.AsTime()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nKey, err := wgkey.ParseHex(machine.NodeKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
@ -436,9 +496,16 @@ func nodesToPtables(
 | 
			
		||||
		if lastSeen.After(
 | 
			
		||||
			time.Now().Add(-5 * time.Minute),
 | 
			
		||||
		) { // TODO: Find a better way to reliably show if online
 | 
			
		||||
			online = pterm.LightGreen("true")
 | 
			
		||||
			online = pterm.LightGreen("online")
 | 
			
		||||
		} else {
 | 
			
		||||
			online = pterm.LightRed("false")
 | 
			
		||||
			online = pterm.LightRed("offline")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var expired string
 | 
			
		||||
		if expiry.IsZero() || expiry.After(time.Now()) {
 | 
			
		||||
			expired = pterm.LightGreen("no")
 | 
			
		||||
		} else {
 | 
			
		||||
			expired = pterm.LightRed("yes")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var namespace string
 | 
			
		||||
@ -459,6 +526,7 @@ func nodesToPtables(
 | 
			
		||||
				strconv.FormatBool(ephemeral),
 | 
			
		||||
				lastSeenTime,
 | 
			
		||||
				online,
 | 
			
		||||
				expired,
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -218,30 +218,6 @@ func absPath(path string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getHeadscaleConfig() headscale.Config {
 | 
			
		||||
	// maxMachineRegistrationDuration is the maximum time headscale will allow a client to (optionally) request for
 | 
			
		||||
	// the machine key expiry time. RegisterRequests with Expiry times that are more than
 | 
			
		||||
	// maxMachineRegistrationDuration in the future will be clamped to (now + maxMachineRegistrationDuration)
 | 
			
		||||
	maxMachineRegistrationDuration, _ := time.ParseDuration(
 | 
			
		||||
		"10h",
 | 
			
		||||
	) // use 10h here because it is the length of a standard business day plus a small amount of leeway
 | 
			
		||||
	if viper.GetDuration("max_machine_registration_duration") >= time.Second {
 | 
			
		||||
		maxMachineRegistrationDuration = viper.GetDuration(
 | 
			
		||||
			"max_machine_registration_duration",
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// defaultMachineRegistrationDuration is the default time assigned to a machine registration if one is not
 | 
			
		||||
	// specified by the tailscale client. It is the default amount of time a machine registration is valid for
 | 
			
		||||
	// (ie the amount of time before the user has to re-authenticate when requesting a connection)
 | 
			
		||||
	defaultMachineRegistrationDuration, _ := time.ParseDuration(
 | 
			
		||||
		"8h",
 | 
			
		||||
	) // use 8h here because it's the length of a standard business day
 | 
			
		||||
	if viper.GetDuration("default_machine_registration_duration") >= time.Second {
 | 
			
		||||
		defaultMachineRegistrationDuration = viper.GetDuration(
 | 
			
		||||
			"default_machine_registration_duration",
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dnsConfig, baseDomain := GetDNSConfig()
 | 
			
		||||
	derpConfig := GetDERPConfig()
 | 
			
		||||
 | 
			
		||||
@ -295,9 +271,6 @@ func getHeadscaleConfig() headscale.Config {
 | 
			
		||||
			Insecure: viper.GetBool("cli.insecure"),
 | 
			
		||||
			Timeout:  viper.GetDuration("cli.timeout"),
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		MaxMachineRegistrationDuration:     maxMachineRegistrationDuration,
 | 
			
		||||
		DefaultMachineRegistrationDuration: defaultMachineRegistrationDuration,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,8 @@ regions:
 | 
			
		||||
    regioncode: custom
 | 
			
		||||
    regionname: My Region
 | 
			
		||||
    nodes:
 | 
			
		||||
      - name: 1a
 | 
			
		||||
        regionid: 1
 | 
			
		||||
      - name: 900a
 | 
			
		||||
        regionid: 900
 | 
			
		||||
        hostname: myderp.mydomain.no
 | 
			
		||||
        ipv4: 123.123.123.123
 | 
			
		||||
        ipv6: "2604:a880:400:d1::828:b001"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								dns_test.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								dns_test.go
									
									
									
									
									
								
							@ -123,7 +123,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared1.ID,
 | 
			
		||||
		Namespace:      *namespaceShared1,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.1",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyInShared1.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -141,7 +141,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared2.ID,
 | 
			
		||||
		Namespace:      *namespaceShared2,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.2",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyInShared2.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -159,7 +159,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared3.ID,
 | 
			
		||||
		Namespace:      *namespaceShared3,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.3",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyInShared3.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -177,7 +177,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared1.ID,
 | 
			
		||||
		Namespace:      *namespaceShared1,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.4",
 | 
			
		||||
		AuthKeyID:      uint(PreAuthKey2InShared1.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -272,7 +272,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared1.ID,
 | 
			
		||||
		Namespace:      *namespaceShared1,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.1",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyInShared1.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -290,7 +290,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared2.ID,
 | 
			
		||||
		Namespace:      *namespaceShared2,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.2",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyInShared2.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -308,7 +308,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared3.ID,
 | 
			
		||||
		Namespace:      *namespaceShared3,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.3",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyInShared3.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -326,7 +326,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared1.ID,
 | 
			
		||||
		Namespace:      *namespaceShared1,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.4",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKey2InShared1.ID),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@
 | 
			
		||||
 | 
			
		||||
   ```shell
 | 
			
		||||
   docker run --name headscale \
 | 
			
		||||
     -e POSTGRES_DB=headscale
 | 
			
		||||
     -e POSTGRES_DB=headscale \
 | 
			
		||||
     -e POSTGRES_USER=foo \
 | 
			
		||||
     -e POSTGRES_PASSWORD=bar \
 | 
			
		||||
     -p 5432:5432 \
 | 
			
		||||
 | 
			
		||||
@ -34,8 +34,8 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{
 | 
			
		||||
	0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76,
 | 
			
		||||
	0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xec,
 | 
			
		||||
	0x11, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76,
 | 
			
		||||
	0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xf4,
 | 
			
		||||
	0x12, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76,
 | 
			
		||||
	0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70,
 | 
			
		||||
	0x61, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
 | 
			
		||||
	0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52,
 | 
			
		||||
@ -133,54 +133,63 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{
 | 
			
		||||
	0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52,
 | 
			
		||||
	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a,
 | 
			
		||||
	0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6e, 0x0a,
 | 
			
		||||
	0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x21, 0x2e,
 | 
			
		||||
	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73,
 | 
			
		||||
	0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
 | 
			
		||||
	0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
 | 
			
		||||
	0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
 | 
			
		||||
	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61,
 | 
			
		||||
	0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x8d, 0x01,
 | 
			
		||||
	0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x21,
 | 
			
		||||
	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68,
 | 
			
		||||
	0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
 | 
			
		||||
	0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
 | 
			
		||||
	0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
 | 
			
		||||
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x22, 0x2e, 0x2f,
 | 
			
		||||
	0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b,
 | 
			
		||||
	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x68, 0x61, 0x72,
 | 
			
		||||
	0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x95, 0x01,
 | 
			
		||||
	0x0a, 0x0e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
 | 
			
		||||
	0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65,
 | 
			
		||||
	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
 | 
			
		||||
	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4,
 | 
			
		||||
	0x93, 0x02, 0x32, 0x22, 0x30, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64,
 | 
			
		||||
	0x7d, 0x2f, 0x75, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73,
 | 
			
		||||
	0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64,
 | 
			
		||||
	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
 | 
			
		||||
	0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47,
 | 
			
		||||
	0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65,
 | 
			
		||||
	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23,
 | 
			
		||||
	0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f,
 | 
			
		||||
	0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75,
 | 
			
		||||
	0x74, 0x65, 0x73, 0x12, 0x97, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x68, 0x65,
 | 
			
		||||
	0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c,
 | 
			
		||||
	0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65,
 | 
			
		||||
	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
 | 
			
		||||
	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
 | 
			
		||||
	0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
 | 
			
		||||
	0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
 | 
			
		||||
	0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69,
 | 
			
		||||
	0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x29, 0x5a,
 | 
			
		||||
	0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e,
 | 
			
		||||
	0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67,
 | 
			
		||||
	0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
	0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x85, 0x01,
 | 
			
		||||
	0x0a, 0x0d, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12,
 | 
			
		||||
	0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45,
 | 
			
		||||
	0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75,
 | 
			
		||||
	0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
 | 
			
		||||
	0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25,
 | 
			
		||||
	0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
 | 
			
		||||
	0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x65,
 | 
			
		||||
	0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x6e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
 | 
			
		||||
	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
 | 
			
		||||
	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3,
 | 
			
		||||
	0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
 | 
			
		||||
	0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64,
 | 
			
		||||
	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82,
 | 
			
		||||
	0xd3, 0xe4, 0x93, 0x02, 0x30, 0x22, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f,
 | 
			
		||||
	0x69, 0x64, 0x7d, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73,
 | 
			
		||||
	0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x95, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72,
 | 
			
		||||
	0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
 | 
			
		||||
	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e,
 | 
			
		||||
	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73,
 | 
			
		||||
	0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
 | 
			
		||||
	0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x30, 0x2f, 0x61, 0x70,
 | 
			
		||||
	0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x73, 0x68, 0x61, 0x72,
 | 
			
		||||
	0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x8b, 0x01,
 | 
			
		||||
	0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74,
 | 
			
		||||
	0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
 | 
			
		||||
	0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65,
 | 
			
		||||
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
 | 
			
		||||
	0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
 | 
			
		||||
	0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b,
 | 
			
		||||
	0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f,
 | 
			
		||||
	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x97, 0x01, 0x0a, 0x13,
 | 
			
		||||
	0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75,
 | 
			
		||||
	0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
 | 
			
		||||
	0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e,
 | 
			
		||||
	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61,
 | 
			
		||||
	0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
 | 
			
		||||
	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25,
 | 
			
		||||
	0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
 | 
			
		||||
	0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72,
 | 
			
		||||
	0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
 | 
			
		||||
	0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61,
 | 
			
		||||
	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31,
 | 
			
		||||
	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var file_headscale_v1_headscale_proto_goTypes = []interface{}{
 | 
			
		||||
@ -196,28 +205,30 @@ var file_headscale_v1_headscale_proto_goTypes = []interface{}{
 | 
			
		||||
	(*GetMachineRequest)(nil),           // 9: headscale.v1.GetMachineRequest
 | 
			
		||||
	(*RegisterMachineRequest)(nil),      // 10: headscale.v1.RegisterMachineRequest
 | 
			
		||||
	(*DeleteMachineRequest)(nil),        // 11: headscale.v1.DeleteMachineRequest
 | 
			
		||||
	(*ListMachinesRequest)(nil),         // 12: headscale.v1.ListMachinesRequest
 | 
			
		||||
	(*ShareMachineRequest)(nil),         // 13: headscale.v1.ShareMachineRequest
 | 
			
		||||
	(*UnshareMachineRequest)(nil),       // 14: headscale.v1.UnshareMachineRequest
 | 
			
		||||
	(*GetMachineRouteRequest)(nil),      // 15: headscale.v1.GetMachineRouteRequest
 | 
			
		||||
	(*EnableMachineRoutesRequest)(nil),  // 16: headscale.v1.EnableMachineRoutesRequest
 | 
			
		||||
	(*GetNamespaceResponse)(nil),        // 17: headscale.v1.GetNamespaceResponse
 | 
			
		||||
	(*CreateNamespaceResponse)(nil),     // 18: headscale.v1.CreateNamespaceResponse
 | 
			
		||||
	(*RenameNamespaceResponse)(nil),     // 19: headscale.v1.RenameNamespaceResponse
 | 
			
		||||
	(*DeleteNamespaceResponse)(nil),     // 20: headscale.v1.DeleteNamespaceResponse
 | 
			
		||||
	(*ListNamespacesResponse)(nil),      // 21: headscale.v1.ListNamespacesResponse
 | 
			
		||||
	(*CreatePreAuthKeyResponse)(nil),    // 22: headscale.v1.CreatePreAuthKeyResponse
 | 
			
		||||
	(*ExpirePreAuthKeyResponse)(nil),    // 23: headscale.v1.ExpirePreAuthKeyResponse
 | 
			
		||||
	(*ListPreAuthKeysResponse)(nil),     // 24: headscale.v1.ListPreAuthKeysResponse
 | 
			
		||||
	(*DebugCreateMachineResponse)(nil),  // 25: headscale.v1.DebugCreateMachineResponse
 | 
			
		||||
	(*GetMachineResponse)(nil),          // 26: headscale.v1.GetMachineResponse
 | 
			
		||||
	(*RegisterMachineResponse)(nil),     // 27: headscale.v1.RegisterMachineResponse
 | 
			
		||||
	(*DeleteMachineResponse)(nil),       // 28: headscale.v1.DeleteMachineResponse
 | 
			
		||||
	(*ListMachinesResponse)(nil),        // 29: headscale.v1.ListMachinesResponse
 | 
			
		||||
	(*ShareMachineResponse)(nil),        // 30: headscale.v1.ShareMachineResponse
 | 
			
		||||
	(*UnshareMachineResponse)(nil),      // 31: headscale.v1.UnshareMachineResponse
 | 
			
		||||
	(*GetMachineRouteResponse)(nil),     // 32: headscale.v1.GetMachineRouteResponse
 | 
			
		||||
	(*EnableMachineRoutesResponse)(nil), // 33: headscale.v1.EnableMachineRoutesResponse
 | 
			
		||||
	(*ExpireMachineRequest)(nil),        // 12: headscale.v1.ExpireMachineRequest
 | 
			
		||||
	(*ListMachinesRequest)(nil),         // 13: headscale.v1.ListMachinesRequest
 | 
			
		||||
	(*ShareMachineRequest)(nil),         // 14: headscale.v1.ShareMachineRequest
 | 
			
		||||
	(*UnshareMachineRequest)(nil),       // 15: headscale.v1.UnshareMachineRequest
 | 
			
		||||
	(*GetMachineRouteRequest)(nil),      // 16: headscale.v1.GetMachineRouteRequest
 | 
			
		||||
	(*EnableMachineRoutesRequest)(nil),  // 17: headscale.v1.EnableMachineRoutesRequest
 | 
			
		||||
	(*GetNamespaceResponse)(nil),        // 18: headscale.v1.GetNamespaceResponse
 | 
			
		||||
	(*CreateNamespaceResponse)(nil),     // 19: headscale.v1.CreateNamespaceResponse
 | 
			
		||||
	(*RenameNamespaceResponse)(nil),     // 20: headscale.v1.RenameNamespaceResponse
 | 
			
		||||
	(*DeleteNamespaceResponse)(nil),     // 21: headscale.v1.DeleteNamespaceResponse
 | 
			
		||||
	(*ListNamespacesResponse)(nil),      // 22: headscale.v1.ListNamespacesResponse
 | 
			
		||||
	(*CreatePreAuthKeyResponse)(nil),    // 23: headscale.v1.CreatePreAuthKeyResponse
 | 
			
		||||
	(*ExpirePreAuthKeyResponse)(nil),    // 24: headscale.v1.ExpirePreAuthKeyResponse
 | 
			
		||||
	(*ListPreAuthKeysResponse)(nil),     // 25: headscale.v1.ListPreAuthKeysResponse
 | 
			
		||||
	(*DebugCreateMachineResponse)(nil),  // 26: headscale.v1.DebugCreateMachineResponse
 | 
			
		||||
	(*GetMachineResponse)(nil),          // 27: headscale.v1.GetMachineResponse
 | 
			
		||||
	(*RegisterMachineResponse)(nil),     // 28: headscale.v1.RegisterMachineResponse
 | 
			
		||||
	(*DeleteMachineResponse)(nil),       // 29: headscale.v1.DeleteMachineResponse
 | 
			
		||||
	(*ExpireMachineResponse)(nil),       // 30: headscale.v1.ExpireMachineResponse
 | 
			
		||||
	(*ListMachinesResponse)(nil),        // 31: headscale.v1.ListMachinesResponse
 | 
			
		||||
	(*ShareMachineResponse)(nil),        // 32: headscale.v1.ShareMachineResponse
 | 
			
		||||
	(*UnshareMachineResponse)(nil),      // 33: headscale.v1.UnshareMachineResponse
 | 
			
		||||
	(*GetMachineRouteResponse)(nil),     // 34: headscale.v1.GetMachineRouteResponse
 | 
			
		||||
	(*EnableMachineRoutesResponse)(nil), // 35: headscale.v1.EnableMachineRoutesResponse
 | 
			
		||||
}
 | 
			
		||||
var file_headscale_v1_headscale_proto_depIdxs = []int32{
 | 
			
		||||
	0,  // 0: headscale.v1.HeadscaleService.GetNamespace:input_type -> headscale.v1.GetNamespaceRequest
 | 
			
		||||
@ -232,30 +243,32 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{
 | 
			
		||||
	9,  // 9: headscale.v1.HeadscaleService.GetMachine:input_type -> headscale.v1.GetMachineRequest
 | 
			
		||||
	10, // 10: headscale.v1.HeadscaleService.RegisterMachine:input_type -> headscale.v1.RegisterMachineRequest
 | 
			
		||||
	11, // 11: headscale.v1.HeadscaleService.DeleteMachine:input_type -> headscale.v1.DeleteMachineRequest
 | 
			
		||||
	12, // 12: headscale.v1.HeadscaleService.ListMachines:input_type -> headscale.v1.ListMachinesRequest
 | 
			
		||||
	13, // 13: headscale.v1.HeadscaleService.ShareMachine:input_type -> headscale.v1.ShareMachineRequest
 | 
			
		||||
	14, // 14: headscale.v1.HeadscaleService.UnshareMachine:input_type -> headscale.v1.UnshareMachineRequest
 | 
			
		||||
	15, // 15: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest
 | 
			
		||||
	16, // 16: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest
 | 
			
		||||
	17, // 17: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse
 | 
			
		||||
	18, // 18: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse
 | 
			
		||||
	19, // 19: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse
 | 
			
		||||
	20, // 20: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse
 | 
			
		||||
	21, // 21: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse
 | 
			
		||||
	22, // 22: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
 | 
			
		||||
	23, // 23: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
 | 
			
		||||
	24, // 24: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
 | 
			
		||||
	25, // 25: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse
 | 
			
		||||
	26, // 26: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse
 | 
			
		||||
	27, // 27: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse
 | 
			
		||||
	28, // 28: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse
 | 
			
		||||
	29, // 29: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse
 | 
			
		||||
	30, // 30: headscale.v1.HeadscaleService.ShareMachine:output_type -> headscale.v1.ShareMachineResponse
 | 
			
		||||
	31, // 31: headscale.v1.HeadscaleService.UnshareMachine:output_type -> headscale.v1.UnshareMachineResponse
 | 
			
		||||
	32, // 32: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse
 | 
			
		||||
	33, // 33: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse
 | 
			
		||||
	17, // [17:34] is the sub-list for method output_type
 | 
			
		||||
	0,  // [0:17] is the sub-list for method input_type
 | 
			
		||||
	12, // 12: headscale.v1.HeadscaleService.ExpireMachine:input_type -> headscale.v1.ExpireMachineRequest
 | 
			
		||||
	13, // 13: headscale.v1.HeadscaleService.ListMachines:input_type -> headscale.v1.ListMachinesRequest
 | 
			
		||||
	14, // 14: headscale.v1.HeadscaleService.ShareMachine:input_type -> headscale.v1.ShareMachineRequest
 | 
			
		||||
	15, // 15: headscale.v1.HeadscaleService.UnshareMachine:input_type -> headscale.v1.UnshareMachineRequest
 | 
			
		||||
	16, // 16: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest
 | 
			
		||||
	17, // 17: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest
 | 
			
		||||
	18, // 18: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse
 | 
			
		||||
	19, // 19: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse
 | 
			
		||||
	20, // 20: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse
 | 
			
		||||
	21, // 21: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse
 | 
			
		||||
	22, // 22: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse
 | 
			
		||||
	23, // 23: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
 | 
			
		||||
	24, // 24: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
 | 
			
		||||
	25, // 25: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
 | 
			
		||||
	26, // 26: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse
 | 
			
		||||
	27, // 27: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse
 | 
			
		||||
	28, // 28: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse
 | 
			
		||||
	29, // 29: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse
 | 
			
		||||
	30, // 30: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse
 | 
			
		||||
	31, // 31: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse
 | 
			
		||||
	32, // 32: headscale.v1.HeadscaleService.ShareMachine:output_type -> headscale.v1.ShareMachineResponse
 | 
			
		||||
	33, // 33: headscale.v1.HeadscaleService.UnshareMachine:output_type -> headscale.v1.UnshareMachineResponse
 | 
			
		||||
	34, // 34: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse
 | 
			
		||||
	35, // 35: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse
 | 
			
		||||
	18, // [18:36] is the sub-list for method output_type
 | 
			
		||||
	0,  // [0:18] is the sub-list for method input_type
 | 
			
		||||
	0,  // [0:0] is the sub-list for extension type_name
 | 
			
		||||
	0,  // [0:0] is the sub-list for extension extendee
 | 
			
		||||
	0,  // [0:0] is the sub-list for field type_name
 | 
			
		||||
 | 
			
		||||
@ -537,6 +537,58 @@ func local_request_HeadscaleService_DeleteMachine_0(ctx context.Context, marshal
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func request_HeadscaleService_ExpireMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
 | 
			
		||||
	var protoReq ExpireMachineRequest
 | 
			
		||||
	var metadata runtime.ServerMetadata
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		val string
 | 
			
		||||
		ok  bool
 | 
			
		||||
		err error
 | 
			
		||||
		_   = err
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	val, ok = pathParams["machine_id"]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protoReq.MachineId, err = runtime.Uint64(val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg, err := client.ExpireMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
 | 
			
		||||
	return msg, metadata, err
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func local_request_HeadscaleService_ExpireMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
 | 
			
		||||
	var protoReq ExpireMachineRequest
 | 
			
		||||
	var metadata runtime.ServerMetadata
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		val string
 | 
			
		||||
		ok  bool
 | 
			
		||||
		err error
 | 
			
		||||
		_   = err
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	val, ok = pathParams["machine_id"]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protoReq.MachineId, err = runtime.Uint64(val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg, err := server.ExpireMachine(ctx, &protoReq)
 | 
			
		||||
	return msg, metadata, err
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	filter_HeadscaleService_ListMachines_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
 | 
			
		||||
)
 | 
			
		||||
@ -1121,6 +1173,29 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser
 | 
			
		||||
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	mux.Handle("POST", pattern_HeadscaleService_ExpireMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 | 
			
		||||
		ctx, cancel := context.WithCancel(req.Context())
 | 
			
		||||
		defer cancel()
 | 
			
		||||
		var stream runtime.ServerTransportStream
 | 
			
		||||
		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
 | 
			
		||||
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
 | 
			
		||||
		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ExpireMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/expire"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		resp, md, err := local_request_HeadscaleService_ExpireMachine_0(rctx, inboundMarshaler, server, req, pathParams)
 | 
			
		||||
		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
 | 
			
		||||
		ctx = runtime.NewServerMetadataContext(ctx, md)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		forward_HeadscaleService_ExpireMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
 | 
			
		||||
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	mux.Handle("GET", pattern_HeadscaleService_ListMachines_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 | 
			
		||||
		ctx, cancel := context.WithCancel(req.Context())
 | 
			
		||||
		defer cancel()
 | 
			
		||||
@ -1517,6 +1592,26 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser
 | 
			
		||||
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	mux.Handle("POST", pattern_HeadscaleService_ExpireMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 | 
			
		||||
		ctx, cancel := context.WithCancel(req.Context())
 | 
			
		||||
		defer cancel()
 | 
			
		||||
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
 | 
			
		||||
		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ExpireMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/expire"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		resp, md, err := request_HeadscaleService_ExpireMachine_0(rctx, inboundMarshaler, client, req, pathParams)
 | 
			
		||||
		ctx = runtime.NewServerMetadataContext(ctx, md)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		forward_HeadscaleService_ExpireMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
 | 
			
		||||
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	mux.Handle("GET", pattern_HeadscaleService_ListMachines_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 | 
			
		||||
		ctx, cancel := context.WithCancel(req.Context())
 | 
			
		||||
		defer cancel()
 | 
			
		||||
@ -1645,6 +1740,8 @@ var (
 | 
			
		||||
 | 
			
		||||
	pattern_HeadscaleService_DeleteMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "machine", "machine_id"}, ""))
 | 
			
		||||
 | 
			
		||||
	pattern_HeadscaleService_ExpireMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "expire"}, ""))
 | 
			
		||||
 | 
			
		||||
	pattern_HeadscaleService_ListMachines_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "machine"}, ""))
 | 
			
		||||
 | 
			
		||||
	pattern_HeadscaleService_ShareMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "machine", "machine_id", "share", "namespace"}, ""))
 | 
			
		||||
@ -1681,6 +1778,8 @@ var (
 | 
			
		||||
 | 
			
		||||
	forward_HeadscaleService_DeleteMachine_0 = runtime.ForwardResponseMessage
 | 
			
		||||
 | 
			
		||||
	forward_HeadscaleService_ExpireMachine_0 = runtime.ForwardResponseMessage
 | 
			
		||||
 | 
			
		||||
	forward_HeadscaleService_ListMachines_0 = runtime.ForwardResponseMessage
 | 
			
		||||
 | 
			
		||||
	forward_HeadscaleService_ShareMachine_0 = runtime.ForwardResponseMessage
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,7 @@ type HeadscaleServiceClient interface {
 | 
			
		||||
	GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error)
 | 
			
		||||
	RegisterMachine(ctx context.Context, in *RegisterMachineRequest, opts ...grpc.CallOption) (*RegisterMachineResponse, error)
 | 
			
		||||
	DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error)
 | 
			
		||||
	ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error)
 | 
			
		||||
	ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error)
 | 
			
		||||
	ShareMachine(ctx context.Context, in *ShareMachineRequest, opts ...grpc.CallOption) (*ShareMachineResponse, error)
 | 
			
		||||
	UnshareMachine(ctx context.Context, in *UnshareMachineRequest, opts ...grpc.CallOption) (*UnshareMachineResponse, error)
 | 
			
		||||
@ -157,6 +158,15 @@ func (c *headscaleServiceClient) DeleteMachine(ctx context.Context, in *DeleteMa
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *headscaleServiceClient) ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error) {
 | 
			
		||||
	out := new(ExpireMachineResponse)
 | 
			
		||||
	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpireMachine", in, out, opts...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) {
 | 
			
		||||
	out := new(ListMachinesResponse)
 | 
			
		||||
	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListMachines", in, out, opts...)
 | 
			
		||||
@ -221,6 +231,7 @@ type HeadscaleServiceServer interface {
 | 
			
		||||
	GetMachine(context.Context, *GetMachineRequest) (*GetMachineResponse, error)
 | 
			
		||||
	RegisterMachine(context.Context, *RegisterMachineRequest) (*RegisterMachineResponse, error)
 | 
			
		||||
	DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error)
 | 
			
		||||
	ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error)
 | 
			
		||||
	ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error)
 | 
			
		||||
	ShareMachine(context.Context, *ShareMachineRequest) (*ShareMachineResponse, error)
 | 
			
		||||
	UnshareMachine(context.Context, *UnshareMachineRequest) (*UnshareMachineResponse, error)
 | 
			
		||||
@ -270,6 +281,9 @@ func (UnimplementedHeadscaleServiceServer) RegisterMachine(context.Context, *Reg
 | 
			
		||||
func (UnimplementedHeadscaleServiceServer) DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error) {
 | 
			
		||||
	return nil, status.Errorf(codes.Unimplemented, "method DeleteMachine not implemented")
 | 
			
		||||
}
 | 
			
		||||
func (UnimplementedHeadscaleServiceServer) ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error) {
 | 
			
		||||
	return nil, status.Errorf(codes.Unimplemented, "method ExpireMachine not implemented")
 | 
			
		||||
}
 | 
			
		||||
func (UnimplementedHeadscaleServiceServer) ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error) {
 | 
			
		||||
	return nil, status.Errorf(codes.Unimplemented, "method ListMachines not implemented")
 | 
			
		||||
}
 | 
			
		||||
@ -514,6 +528,24 @@ func _HeadscaleService_DeleteMachine_Handler(srv interface{}, ctx context.Contex
 | 
			
		||||
	return interceptor(ctx, in, info, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func _HeadscaleService_ExpireMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 | 
			
		||||
	in := new(ExpireMachineRequest)
 | 
			
		||||
	if err := dec(in); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if interceptor == nil {
 | 
			
		||||
		return srv.(HeadscaleServiceServer).ExpireMachine(ctx, in)
 | 
			
		||||
	}
 | 
			
		||||
	info := &grpc.UnaryServerInfo{
 | 
			
		||||
		Server:     srv,
 | 
			
		||||
		FullMethod: "/headscale.v1.HeadscaleService/ExpireMachine",
 | 
			
		||||
	}
 | 
			
		||||
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 | 
			
		||||
		return srv.(HeadscaleServiceServer).ExpireMachine(ctx, req.(*ExpireMachineRequest))
 | 
			
		||||
	}
 | 
			
		||||
	return interceptor(ctx, in, info, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func _HeadscaleService_ListMachines_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 | 
			
		||||
	in := new(ListMachinesRequest)
 | 
			
		||||
	if err := dec(in); err != nil {
 | 
			
		||||
@ -659,6 +691,10 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{
 | 
			
		||||
			MethodName: "DeleteMachine",
 | 
			
		||||
			Handler:    _HeadscaleService_DeleteMachine_Handler,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			MethodName: "ExpireMachine",
 | 
			
		||||
			Handler:    _HeadscaleService_ExpireMachine_Handler,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			MethodName: "ListMachines",
 | 
			
		||||
			Handler:    _HeadscaleService_ListMachines_Handler,
 | 
			
		||||
 | 
			
		||||
@ -505,6 +505,100 @@ func (*DeleteMachineResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{6}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ExpireMachineRequest struct {
 | 
			
		||||
	state         protoimpl.MessageState
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
 | 
			
		||||
	MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineRequest) Reset() {
 | 
			
		||||
	*x = ExpireMachineRequest{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[7]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineRequest) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*ExpireMachineRequest) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[7]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use ExpireMachineRequest.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*ExpireMachineRequest) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{7}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineRequest) GetMachineId() uint64 {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.MachineId
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ExpireMachineResponse struct {
 | 
			
		||||
	state         protoimpl.MessageState
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
 | 
			
		||||
	Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineResponse) Reset() {
 | 
			
		||||
	*x = ExpireMachineResponse{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[8]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineResponse) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*ExpireMachineResponse) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[8]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use ExpireMachineResponse.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*ExpireMachineResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{8}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ExpireMachineResponse) GetMachine() *Machine {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Machine
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ListMachinesRequest struct {
 | 
			
		||||
	state         protoimpl.MessageState
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
@ -516,7 +610,7 @@ type ListMachinesRequest struct {
 | 
			
		||||
func (x *ListMachinesRequest) Reset() {
 | 
			
		||||
	*x = ListMachinesRequest{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[7]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[9]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -529,7 +623,7 @@ func (x *ListMachinesRequest) String() string {
 | 
			
		||||
func (*ListMachinesRequest) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *ListMachinesRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[7]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[9]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -542,7 +636,7 @@ func (x *ListMachinesRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use ListMachinesRequest.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*ListMachinesRequest) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{7}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{9}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ListMachinesRequest) GetNamespace() string {
 | 
			
		||||
@ -563,7 +657,7 @@ type ListMachinesResponse struct {
 | 
			
		||||
func (x *ListMachinesResponse) Reset() {
 | 
			
		||||
	*x = ListMachinesResponse{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[8]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[10]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -576,7 +670,7 @@ func (x *ListMachinesResponse) String() string {
 | 
			
		||||
func (*ListMachinesResponse) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[8]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[10]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -589,7 +683,7 @@ func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use ListMachinesResponse.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*ListMachinesResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{8}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{10}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ListMachinesResponse) GetMachines() []*Machine {
 | 
			
		||||
@ -611,7 +705,7 @@ type ShareMachineRequest struct {
 | 
			
		||||
func (x *ShareMachineRequest) Reset() {
 | 
			
		||||
	*x = ShareMachineRequest{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[9]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[11]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -624,7 +718,7 @@ func (x *ShareMachineRequest) String() string {
 | 
			
		||||
func (*ShareMachineRequest) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *ShareMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[9]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[11]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -637,7 +731,7 @@ func (x *ShareMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use ShareMachineRequest.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*ShareMachineRequest) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{9}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{11}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ShareMachineRequest) GetMachineId() uint64 {
 | 
			
		||||
@ -665,7 +759,7 @@ type ShareMachineResponse struct {
 | 
			
		||||
func (x *ShareMachineResponse) Reset() {
 | 
			
		||||
	*x = ShareMachineResponse{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[10]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[12]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -678,7 +772,7 @@ func (x *ShareMachineResponse) String() string {
 | 
			
		||||
func (*ShareMachineResponse) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *ShareMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[10]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[12]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -691,7 +785,7 @@ func (x *ShareMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use ShareMachineResponse.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*ShareMachineResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{10}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{12}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *ShareMachineResponse) GetMachine() *Machine {
 | 
			
		||||
@ -713,7 +807,7 @@ type UnshareMachineRequest struct {
 | 
			
		||||
func (x *UnshareMachineRequest) Reset() {
 | 
			
		||||
	*x = UnshareMachineRequest{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[11]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[13]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -726,7 +820,7 @@ func (x *UnshareMachineRequest) String() string {
 | 
			
		||||
func (*UnshareMachineRequest) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *UnshareMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[11]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[13]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -739,7 +833,7 @@ func (x *UnshareMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use UnshareMachineRequest.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*UnshareMachineRequest) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{11}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{13}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *UnshareMachineRequest) GetMachineId() uint64 {
 | 
			
		||||
@ -767,7 +861,7 @@ type UnshareMachineResponse struct {
 | 
			
		||||
func (x *UnshareMachineResponse) Reset() {
 | 
			
		||||
	*x = UnshareMachineResponse{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[12]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[14]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -780,7 +874,7 @@ func (x *UnshareMachineResponse) String() string {
 | 
			
		||||
func (*UnshareMachineResponse) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *UnshareMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[12]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[14]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -793,7 +887,7 @@ func (x *UnshareMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use UnshareMachineResponse.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*UnshareMachineResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{12}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{14}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *UnshareMachineResponse) GetMachine() *Machine {
 | 
			
		||||
@ -817,7 +911,7 @@ type DebugCreateMachineRequest struct {
 | 
			
		||||
func (x *DebugCreateMachineRequest) Reset() {
 | 
			
		||||
	*x = DebugCreateMachineRequest{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[13]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[15]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -830,7 +924,7 @@ func (x *DebugCreateMachineRequest) String() string {
 | 
			
		||||
func (*DebugCreateMachineRequest) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[13]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[15]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -843,7 +937,7 @@ func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use DebugCreateMachineRequest.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*DebugCreateMachineRequest) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{13}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{15}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *DebugCreateMachineRequest) GetNamespace() string {
 | 
			
		||||
@ -885,7 +979,7 @@ type DebugCreateMachineResponse struct {
 | 
			
		||||
func (x *DebugCreateMachineResponse) Reset() {
 | 
			
		||||
	*x = DebugCreateMachineResponse{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[14]
 | 
			
		||||
		mi := &file_headscale_v1_machine_proto_msgTypes[16]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
@ -898,7 +992,7 @@ func (x *DebugCreateMachineResponse) String() string {
 | 
			
		||||
func (*DebugCreateMachineResponse) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[14]
 | 
			
		||||
	mi := &file_headscale_v1_machine_proto_msgTypes[16]
 | 
			
		||||
	if protoimpl.UnsafeEnabled && x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
@ -911,7 +1005,7 @@ func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use DebugCreateMachineResponse.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*DebugCreateMachineResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{14}
 | 
			
		||||
	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{16}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *DebugCreateMachineResponse) GetMachine() *Machine {
 | 
			
		||||
@ -994,59 +1088,67 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
 | 
			
		||||
	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x17, 0x0a, 0x15,
 | 
			
		||||
	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
 | 
			
		||||
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09,
 | 
			
		||||
	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
 | 
			
		||||
	0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x69,
 | 
			
		||||
	0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
 | 
			
		||||
	0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01,
 | 
			
		||||
	0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
 | 
			
		||||
	0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x52, 0x0a, 0x13, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a,
 | 
			
		||||
	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
 | 
			
		||||
	0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e,
 | 
			
		||||
	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
 | 
			
		||||
	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x47, 0x0a, 0x14, 0x53, 0x68, 0x61,
 | 
			
		||||
	0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
 | 
			
		||||
	0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
 | 
			
		||||
	0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69,
 | 
			
		||||
	0x6e, 0x65, 0x22, 0x54, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
 | 
			
		||||
	0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61,
 | 
			
		||||
	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e,
 | 
			
		||||
	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x16, 0x55, 0x6e, 0x73, 0x68,
 | 
			
		||||
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
 | 
			
		||||
	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x15,
 | 
			
		||||
	0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
 | 
			
		||||
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
 | 
			
		||||
	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
 | 
			
		||||
	0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
 | 
			
		||||
	0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c,
 | 
			
		||||
	0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
 | 
			
		||||
	0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18,
 | 
			
		||||
	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
 | 
			
		||||
	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x52, 0x0a, 0x13, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
 | 
			
		||||
	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09,
 | 
			
		||||
	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
 | 
			
		||||
	0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x47, 0x0a, 0x14, 0x53, 0x68,
 | 
			
		||||
	0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
 | 
			
		||||
	0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
 | 
			
		||||
	0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61,
 | 
			
		||||
	0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
 | 
			
		||||
	0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10,
 | 
			
		||||
	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
 | 
			
		||||
	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
 | 
			
		||||
	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04,
 | 
			
		||||
	0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x1a,
 | 
			
		||||
	0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
 | 
			
		||||
	0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65,
 | 
			
		||||
	0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69,
 | 
			
		||||
	0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a, 0x0e,
 | 
			
		||||
	0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f,
 | 
			
		||||
	0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f,
 | 
			
		||||
	0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
 | 
			
		||||
	0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48,
 | 
			
		||||
	0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a,
 | 
			
		||||
	0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44,
 | 
			
		||||
	0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54,
 | 
			
		||||
	0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03,
 | 
			
		||||
	0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a,
 | 
			
		||||
	0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
 | 
			
		||||
	0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
 | 
			
		||||
	0x74, 0x6f, 0x33,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x22, 0x54, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61,
 | 
			
		||||
	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a,
 | 
			
		||||
	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
 | 
			
		||||
	0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e,
 | 
			
		||||
	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
 | 
			
		||||
	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x16, 0x55, 0x6e, 0x73,
 | 
			
		||||
	0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
 | 
			
		||||
	0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
 | 
			
		||||
	0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63,
 | 
			
		||||
	0x68, 0x69, 0x6e, 0x65, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65,
 | 
			
		||||
	0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
 | 
			
		||||
	0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12,
 | 
			
		||||
	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
 | 
			
		||||
	0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
 | 
			
		||||
	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18,
 | 
			
		||||
	0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a,
 | 
			
		||||
	0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d,
 | 
			
		||||
	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68,
 | 
			
		||||
	0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68,
 | 
			
		||||
	0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a,
 | 
			
		||||
	0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12,
 | 
			
		||||
	0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48,
 | 
			
		||||
	0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
 | 
			
		||||
	0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54,
 | 
			
		||||
	0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17,
 | 
			
		||||
	0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f,
 | 
			
		||||
	0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53,
 | 
			
		||||
	0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10,
 | 
			
		||||
	0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
 | 
			
		||||
	0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
 | 
			
		||||
	0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
@ -1062,7 +1164,7 @@ func file_headscale_v1_machine_proto_rawDescGZIP() []byte {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var file_headscale_v1_machine_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 | 
			
		||||
var file_headscale_v1_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
 | 
			
		||||
var file_headscale_v1_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
 | 
			
		||||
var file_headscale_v1_machine_proto_goTypes = []interface{}{
 | 
			
		||||
	(RegisterMethod)(0),                // 0: headscale.v1.RegisterMethod
 | 
			
		||||
	(*Machine)(nil),                    // 1: headscale.v1.Machine
 | 
			
		||||
@ -1072,37 +1174,40 @@ var file_headscale_v1_machine_proto_goTypes = []interface{}{
 | 
			
		||||
	(*GetMachineResponse)(nil),         // 5: headscale.v1.GetMachineResponse
 | 
			
		||||
	(*DeleteMachineRequest)(nil),       // 6: headscale.v1.DeleteMachineRequest
 | 
			
		||||
	(*DeleteMachineResponse)(nil),      // 7: headscale.v1.DeleteMachineResponse
 | 
			
		||||
	(*ListMachinesRequest)(nil),        // 8: headscale.v1.ListMachinesRequest
 | 
			
		||||
	(*ListMachinesResponse)(nil),       // 9: headscale.v1.ListMachinesResponse
 | 
			
		||||
	(*ShareMachineRequest)(nil),        // 10: headscale.v1.ShareMachineRequest
 | 
			
		||||
	(*ShareMachineResponse)(nil),       // 11: headscale.v1.ShareMachineResponse
 | 
			
		||||
	(*UnshareMachineRequest)(nil),      // 12: headscale.v1.UnshareMachineRequest
 | 
			
		||||
	(*UnshareMachineResponse)(nil),     // 13: headscale.v1.UnshareMachineResponse
 | 
			
		||||
	(*DebugCreateMachineRequest)(nil),  // 14: headscale.v1.DebugCreateMachineRequest
 | 
			
		||||
	(*DebugCreateMachineResponse)(nil), // 15: headscale.v1.DebugCreateMachineResponse
 | 
			
		||||
	(*Namespace)(nil),                  // 16: headscale.v1.Namespace
 | 
			
		||||
	(*timestamppb.Timestamp)(nil),      // 17: google.protobuf.Timestamp
 | 
			
		||||
	(*PreAuthKey)(nil),                 // 18: headscale.v1.PreAuthKey
 | 
			
		||||
	(*ExpireMachineRequest)(nil),       // 8: headscale.v1.ExpireMachineRequest
 | 
			
		||||
	(*ExpireMachineResponse)(nil),      // 9: headscale.v1.ExpireMachineResponse
 | 
			
		||||
	(*ListMachinesRequest)(nil),        // 10: headscale.v1.ListMachinesRequest
 | 
			
		||||
	(*ListMachinesResponse)(nil),       // 11: headscale.v1.ListMachinesResponse
 | 
			
		||||
	(*ShareMachineRequest)(nil),        // 12: headscale.v1.ShareMachineRequest
 | 
			
		||||
	(*ShareMachineResponse)(nil),       // 13: headscale.v1.ShareMachineResponse
 | 
			
		||||
	(*UnshareMachineRequest)(nil),      // 14: headscale.v1.UnshareMachineRequest
 | 
			
		||||
	(*UnshareMachineResponse)(nil),     // 15: headscale.v1.UnshareMachineResponse
 | 
			
		||||
	(*DebugCreateMachineRequest)(nil),  // 16: headscale.v1.DebugCreateMachineRequest
 | 
			
		||||
	(*DebugCreateMachineResponse)(nil), // 17: headscale.v1.DebugCreateMachineResponse
 | 
			
		||||
	(*Namespace)(nil),                  // 18: headscale.v1.Namespace
 | 
			
		||||
	(*timestamppb.Timestamp)(nil),      // 19: google.protobuf.Timestamp
 | 
			
		||||
	(*PreAuthKey)(nil),                 // 20: headscale.v1.PreAuthKey
 | 
			
		||||
}
 | 
			
		||||
var file_headscale_v1_machine_proto_depIdxs = []int32{
 | 
			
		||||
	16, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace
 | 
			
		||||
	18, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace
 | 
			
		||||
	0,  // 1: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod
 | 
			
		||||
	17, // 2: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	17, // 3: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	17, // 4: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	18, // 5: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey
 | 
			
		||||
	17, // 6: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	19, // 2: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	19, // 3: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	19, // 4: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	20, // 5: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey
 | 
			
		||||
	19, // 6: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	1,  // 7: headscale.v1.RegisterMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 8: headscale.v1.GetMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 9: headscale.v1.ListMachinesResponse.machines:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 10: headscale.v1.ShareMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 11: headscale.v1.UnshareMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 12: headscale.v1.DebugCreateMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	13, // [13:13] is the sub-list for method output_type
 | 
			
		||||
	13, // [13:13] is the sub-list for method input_type
 | 
			
		||||
	13, // [13:13] is the sub-list for extension type_name
 | 
			
		||||
	13, // [13:13] is the sub-list for extension extendee
 | 
			
		||||
	0,  // [0:13] is the sub-list for field type_name
 | 
			
		||||
	1,  // 9: headscale.v1.ExpireMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 10: headscale.v1.ListMachinesResponse.machines:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 11: headscale.v1.ShareMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 12: headscale.v1.UnshareMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	1,  // 13: headscale.v1.DebugCreateMachineResponse.machine:type_name -> headscale.v1.Machine
 | 
			
		||||
	14, // [14:14] is the sub-list for method output_type
 | 
			
		||||
	14, // [14:14] is the sub-list for method input_type
 | 
			
		||||
	14, // [14:14] is the sub-list for extension type_name
 | 
			
		||||
	14, // [14:14] is the sub-list for extension extendee
 | 
			
		||||
	0,  // [0:14] is the sub-list for field type_name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() { file_headscale_v1_machine_proto_init() }
 | 
			
		||||
@ -1198,7 +1303,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*ListMachinesRequest); i {
 | 
			
		||||
			switch v := v.(*ExpireMachineRequest); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1210,7 +1315,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*ListMachinesResponse); i {
 | 
			
		||||
			switch v := v.(*ExpireMachineResponse); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1222,7 +1327,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*ShareMachineRequest); i {
 | 
			
		||||
			switch v := v.(*ListMachinesRequest); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1234,7 +1339,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*ShareMachineResponse); i {
 | 
			
		||||
			switch v := v.(*ListMachinesResponse); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1246,7 +1351,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*UnshareMachineRequest); i {
 | 
			
		||||
			switch v := v.(*ShareMachineRequest); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1258,7 +1363,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*UnshareMachineResponse); i {
 | 
			
		||||
			switch v := v.(*ShareMachineResponse); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1270,7 +1375,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*DebugCreateMachineRequest); i {
 | 
			
		||||
			switch v := v.(*UnshareMachineRequest); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
@ -1282,6 +1387,30 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*UnshareMachineResponse); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
				return &v.sizeCache
 | 
			
		||||
			case 2:
 | 
			
		||||
				return &v.unknownFields
 | 
			
		||||
			default:
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*DebugCreateMachineRequest); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
			case 1:
 | 
			
		||||
				return &v.sizeCache
 | 
			
		||||
			case 2:
 | 
			
		||||
				return &v.unknownFields
 | 
			
		||||
			default:
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file_headscale_v1_machine_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*DebugCreateMachineResponse); i {
 | 
			
		||||
			case 0:
 | 
			
		||||
				return &v.state
 | 
			
		||||
@ -1300,7 +1429,7 @@ func file_headscale_v1_machine_proto_init() {
 | 
			
		||||
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 | 
			
		||||
			RawDescriptor: file_headscale_v1_machine_proto_rawDesc,
 | 
			
		||||
			NumEnums:      1,
 | 
			
		||||
			NumMessages:   15,
 | 
			
		||||
			NumMessages:   17,
 | 
			
		||||
			NumExtensions: 0,
 | 
			
		||||
			NumServices:   0,
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@ -161,6 +161,37 @@
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/api/v1/machine/{machineId}/expire": {
 | 
			
		||||
      "post": {
 | 
			
		||||
        "operationId": "HeadscaleService_ExpireMachine",
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "description": "A successful response.",
 | 
			
		||||
            "schema": {
 | 
			
		||||
              "$ref": "#/definitions/v1ExpireMachineResponse"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "default": {
 | 
			
		||||
            "description": "An unexpected error response.",
 | 
			
		||||
            "schema": {
 | 
			
		||||
              "$ref": "#/definitions/rpcStatus"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "parameters": [
 | 
			
		||||
          {
 | 
			
		||||
            "name": "machineId",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true,
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "format": "uint64"
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "HeadscaleService"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "/api/v1/machine/{machineId}/routes": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "summary": "--- Route start ---",
 | 
			
		||||
@ -649,6 +680,14 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "v1ExpireMachineResponse": {
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "properties": {
 | 
			
		||||
        "machine": {
 | 
			
		||||
          "$ref": "#/definitions/v1Machine"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "v1ExpirePreAuthKeyRequest": {
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "properties": {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								grpcv1.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								grpcv1.go
									
									
									
									
									
								
							@ -201,6 +201,27 @@ func (api headscaleV1APIServer) DeleteMachine(
 | 
			
		||||
	return &v1.DeleteMachineResponse{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api headscaleV1APIServer) ExpireMachine(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	request *v1.ExpireMachineRequest,
 | 
			
		||||
) (*v1.ExpireMachineResponse, error) {
 | 
			
		||||
	machine, err := api.h.GetMachineByID(request.GetMachineId())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.h.ExpireMachine(
 | 
			
		||||
		machine,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	log.Trace().
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Time("expiry", *machine.Expiry).
 | 
			
		||||
		Msg("machine expired")
 | 
			
		||||
 | 
			
		||||
	return &v1.ExpireMachineResponse{Machine: machine.toProto()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api headscaleV1APIServer) ListMachines(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	request *v1.ListMachinesRequest,
 | 
			
		||||
 | 
			
		||||
@ -897,6 +897,133 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
 | 
			
		||||
	assert.Len(s.T(), listOnlyMachineNamespaceAfterUnshare, 4)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *IntegrationCLITestSuite) TestNodeExpireCommand() {
 | 
			
		||||
	namespace, err := s.createNamespace("machine-expire-namespace")
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
	// Randomly generated machine keys
 | 
			
		||||
	machineKeys := []string{
 | 
			
		||||
		"9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
 | 
			
		||||
		"6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
 | 
			
		||||
		"f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
 | 
			
		||||
		"8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
 | 
			
		||||
		"cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
 | 
			
		||||
	}
 | 
			
		||||
	machines := make([]*v1.Machine, len(machineKeys))
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
	for index, machineKey := range machineKeys {
 | 
			
		||||
		_, err := ExecuteCommand(
 | 
			
		||||
			&s.headscale,
 | 
			
		||||
			[]string{
 | 
			
		||||
				"headscale",
 | 
			
		||||
				"debug",
 | 
			
		||||
				"create-node",
 | 
			
		||||
				"--name",
 | 
			
		||||
				fmt.Sprintf("machine-%d", index+1),
 | 
			
		||||
				"--namespace",
 | 
			
		||||
				namespace.Name,
 | 
			
		||||
				"--key",
 | 
			
		||||
				machineKey,
 | 
			
		||||
				"--output",
 | 
			
		||||
				"json",
 | 
			
		||||
			},
 | 
			
		||||
			[]string{},
 | 
			
		||||
		)
 | 
			
		||||
		assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
		machineResult, err := ExecuteCommand(
 | 
			
		||||
			&s.headscale,
 | 
			
		||||
			[]string{
 | 
			
		||||
				"headscale",
 | 
			
		||||
				"nodes",
 | 
			
		||||
				"--namespace",
 | 
			
		||||
				namespace.Name,
 | 
			
		||||
				"register",
 | 
			
		||||
				"--key",
 | 
			
		||||
				machineKey,
 | 
			
		||||
				"--output",
 | 
			
		||||
				"json",
 | 
			
		||||
			},
 | 
			
		||||
			[]string{},
 | 
			
		||||
		)
 | 
			
		||||
		assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
		var machine v1.Machine
 | 
			
		||||
		err = json.Unmarshal([]byte(machineResult), &machine)
 | 
			
		||||
		assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
		machines[index] = &machine
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert.Len(s.T(), machines, len(machineKeys))
 | 
			
		||||
 | 
			
		||||
	listAllResult, err := ExecuteCommand(
 | 
			
		||||
		&s.headscale,
 | 
			
		||||
		[]string{
 | 
			
		||||
			"headscale",
 | 
			
		||||
			"nodes",
 | 
			
		||||
			"list",
 | 
			
		||||
			"--output",
 | 
			
		||||
			"json",
 | 
			
		||||
		},
 | 
			
		||||
		[]string{},
 | 
			
		||||
	)
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
	var listAll []v1.Machine
 | 
			
		||||
	err = json.Unmarshal([]byte(listAllResult), &listAll)
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
	assert.Len(s.T(), listAll, 5)
 | 
			
		||||
 | 
			
		||||
	assert.True(s.T(), listAll[0].Expiry.AsTime().IsZero())
 | 
			
		||||
	assert.True(s.T(), listAll[1].Expiry.AsTime().IsZero())
 | 
			
		||||
	assert.True(s.T(), listAll[2].Expiry.AsTime().IsZero())
 | 
			
		||||
	assert.True(s.T(), listAll[3].Expiry.AsTime().IsZero())
 | 
			
		||||
	assert.True(s.T(), listAll[4].Expiry.AsTime().IsZero())
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < 3; i++ {
 | 
			
		||||
		_, err := ExecuteCommand(
 | 
			
		||||
			&s.headscale,
 | 
			
		||||
			[]string{
 | 
			
		||||
				"headscale",
 | 
			
		||||
				"nodes",
 | 
			
		||||
				"expire",
 | 
			
		||||
				"--identifier",
 | 
			
		||||
				fmt.Sprintf("%d", listAll[i].Id),
 | 
			
		||||
			},
 | 
			
		||||
			[]string{},
 | 
			
		||||
		)
 | 
			
		||||
		assert.Nil(s.T(), err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listAllAfterExpiryResult, err := ExecuteCommand(
 | 
			
		||||
		&s.headscale,
 | 
			
		||||
		[]string{
 | 
			
		||||
			"headscale",
 | 
			
		||||
			"nodes",
 | 
			
		||||
			"list",
 | 
			
		||||
			"--output",
 | 
			
		||||
			"json",
 | 
			
		||||
		},
 | 
			
		||||
		[]string{},
 | 
			
		||||
	)
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
	var listAllAfterExpiry []v1.Machine
 | 
			
		||||
	err = json.Unmarshal([]byte(listAllAfterExpiryResult), &listAllAfterExpiry)
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
	assert.Len(s.T(), listAllAfterExpiry, 5)
 | 
			
		||||
 | 
			
		||||
	assert.True(s.T(), listAllAfterExpiry[0].Expiry.AsTime().Before(time.Now()))
 | 
			
		||||
	assert.True(s.T(), listAllAfterExpiry[1].Expiry.AsTime().Before(time.Now()))
 | 
			
		||||
	assert.True(s.T(), listAllAfterExpiry[2].Expiry.AsTime().Before(time.Now()))
 | 
			
		||||
	assert.True(s.T(), listAllAfterExpiry[3].Expiry.AsTime().IsZero())
 | 
			
		||||
	assert.True(s.T(), listAllAfterExpiry[4].Expiry.AsTime().IsZero())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *IntegrationCLITestSuite) TestRouteCommand() {
 | 
			
		||||
	namespace, err := s.createNamespace("routes-namespace")
 | 
			
		||||
	assert.Nil(s.T(), err)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										110
									
								
								machine.go
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								machine.go
									
									
									
									
									
								
							@ -45,7 +45,6 @@ type Machine struct {
 | 
			
		||||
	LastSeen             *time.Time
 | 
			
		||||
	LastSuccessfulUpdate *time.Time
 | 
			
		||||
	Expiry               *time.Time
 | 
			
		||||
	RequestedExpiry      *time.Time
 | 
			
		||||
 | 
			
		||||
	HostInfo      datatypes.JSON
 | 
			
		||||
	Endpoints     datatypes.JSON
 | 
			
		||||
@ -62,44 +61,20 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// For the time being this method is rather naive.
 | 
			
		||||
func (machine Machine) isAlreadyRegistered() bool {
 | 
			
		||||
func (machine Machine) isRegistered() bool {
 | 
			
		||||
	return machine.Registered
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isExpired returns whether the machine registration has expired.
 | 
			
		||||
func (machine Machine) isExpired() bool {
 | 
			
		||||
	return time.Now().UTC().After(*machine.Expiry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If the Machine is expired, updateMachineExpiry updates the Machine Expiry time to the maximum allowed duration,
 | 
			
		||||
// or the default duration if no Expiry time was requested by the client. The expiry time here does not (yet) cause
 | 
			
		||||
// a client to be disconnected, however they will have to re-auth the machine if they attempt to reconnect after the
 | 
			
		||||
// expiry time.
 | 
			
		||||
func (h *Headscale) updateMachineExpiry(machine *Machine) {
 | 
			
		||||
	if machine.isExpired() {
 | 
			
		||||
		now := time.Now().UTC()
 | 
			
		||||
		maxExpiry := now.Add(
 | 
			
		||||
			h.cfg.MaxMachineRegistrationDuration,
 | 
			
		||||
		) // calculate the maximum expiry
 | 
			
		||||
		defaultExpiry := now.Add(
 | 
			
		||||
			h.cfg.DefaultMachineRegistrationDuration,
 | 
			
		||||
		) // calculate the default expiry
 | 
			
		||||
 | 
			
		||||
		// clamp the expiry time of the machine registration to the maximum allowed, or use the default if none supplied
 | 
			
		||||
		if maxExpiry.Before(*machine.RequestedExpiry) {
 | 
			
		||||
			log.Debug().
 | 
			
		||||
				Msgf("Clamping registration expiry time to maximum: %v (%v)", maxExpiry, h.cfg.MaxMachineRegistrationDuration)
 | 
			
		||||
			machine.Expiry = &maxExpiry
 | 
			
		||||
		} else if machine.RequestedExpiry.IsZero() {
 | 
			
		||||
			log.Debug().Msgf("Using default machine registration expiry time: %v (%v)", defaultExpiry, h.cfg.DefaultMachineRegistrationDuration)
 | 
			
		||||
			machine.Expiry = &defaultExpiry
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Debug().Msgf("Using requested machine registration expiry time: %v", machine.RequestedExpiry)
 | 
			
		||||
			machine.Expiry = machine.RequestedExpiry
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		h.db.Save(&machine)
 | 
			
		||||
	// If Expiry is not set, the client has not indicated that
 | 
			
		||||
	// it wants an expiry time, it is therefor considered
 | 
			
		||||
	// to mean "not expired"
 | 
			
		||||
	if machine.Expiry.IsZero() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return time.Now().UTC().After(*machine.Expiry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) getDirectPeers(machine *Machine) (Machines, error) {
 | 
			
		||||
@ -232,6 +207,23 @@ func (h *Headscale) getPeers(machine *Machine) (Machines, error) {
 | 
			
		||||
	return peers, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) getValidPeers(machine *Machine) (Machines, error) {
 | 
			
		||||
	validPeers := make(Machines, 0)
 | 
			
		||||
 | 
			
		||||
	peers, err := h.getPeers(machine)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Machines{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, peer := range peers {
 | 
			
		||||
		if peer.isRegistered() && !peer.isExpired() {
 | 
			
		||||
			validPeers = append(validPeers, peer)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return validPeers, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) ListMachines() ([]Machine, error) {
 | 
			
		||||
	machines := []Machine{}
 | 
			
		||||
	if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Find(&machines).Error; err != nil {
 | 
			
		||||
@ -287,6 +279,28 @@ func (h *Headscale) UpdateMachine(machine *Machine) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExpireMachine takes a Machine struct and sets the expire field to now.
 | 
			
		||||
func (h *Headscale) ExpireMachine(machine *Machine) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	machine.Expiry = &now
 | 
			
		||||
 | 
			
		||||
	h.setLastStateChangeToNow(machine.Namespace.Name)
 | 
			
		||||
 | 
			
		||||
	h.db.Save(machine)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RefreshMachine takes a Machine struct and sets the expire field to now.
 | 
			
		||||
func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
 | 
			
		||||
	machine.LastSuccessfulUpdate = &now
 | 
			
		||||
	machine.Expiry = &expiry
 | 
			
		||||
 | 
			
		||||
	h.setLastStateChangeToNow(machine.Namespace.Name)
 | 
			
		||||
 | 
			
		||||
	h.db.Save(machine)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteMachine softs deletes a Machine from the database.
 | 
			
		||||
func (h *Headscale) DeleteMachine(machine *Machine) error {
 | 
			
		||||
	err := h.RemoveSharedMachineFromAllNamespaces(machine)
 | 
			
		||||
@ -624,12 +638,37 @@ func (h *Headscale) RegisterMachine(
 | 
			
		||||
		return nil, errMachineNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set
 | 
			
		||||
	// This means that if a user is to slow with register a machine, it will possibly not
 | 
			
		||||
	// have the correct expiry.
 | 
			
		||||
	requestedTime := time.Time{}
 | 
			
		||||
	if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.HexString()); found {
 | 
			
		||||
		log.Trace().
 | 
			
		||||
			Caller().
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("Expiry time found in cache, assigning to node")
 | 
			
		||||
		if reqTime, ok := requestedTimeIf.(time.Time); ok {
 | 
			
		||||
			requestedTime = reqTime
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if machine.isRegistered() {
 | 
			
		||||
		log.Trace().
 | 
			
		||||
			Caller().
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("machine already registered, reauthenticating")
 | 
			
		||||
 | 
			
		||||
		h.RefreshMachine(&machine, requestedTime)
 | 
			
		||||
 | 
			
		||||
		return &machine, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Trace().
 | 
			
		||||
		Caller().
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
		Msg("Attempting to register machine")
 | 
			
		||||
 | 
			
		||||
	if machine.isAlreadyRegistered() {
 | 
			
		||||
	if machine.isRegistered() {
 | 
			
		||||
		err := errMachineAlreadyRegistered
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Caller().
 | 
			
		||||
@ -660,7 +699,8 @@ func (h *Headscale) RegisterMachine(
 | 
			
		||||
	machine.IPAddress = ip.String()
 | 
			
		||||
	machine.NamespaceID = namespace.ID
 | 
			
		||||
	machine.Registered = true
 | 
			
		||||
	machine.RegisterMethod = "cli"
 | 
			
		||||
	machine.RegisterMethod = RegisterMethodCLI
 | 
			
		||||
	machine.Expiry = &requestedTime
 | 
			
		||||
	h.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
	log.Trace().
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ package headscale
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/check.v1"
 | 
			
		||||
)
 | 
			
		||||
@ -25,7 +26,7 @@ func (s *Suite) TestGetMachine(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(machine)
 | 
			
		||||
@ -55,7 +56,7 @@ func (s *Suite) TestGetMachineByID(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
@ -78,7 +79,7 @@ func (s *Suite) TestDeleteMachine(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(1),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
@ -113,7 +114,7 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) {
 | 
			
		||||
		Name:           "testmachine3",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(1),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
@ -144,7 +145,7 @@ func (s *Suite) TestGetDirectPeers(c *check.C) {
 | 
			
		||||
			Name:           "testmachine" + strconv.Itoa(index),
 | 
			
		||||
			NamespaceID:    namespace.ID,
 | 
			
		||||
			Registered:     true,
 | 
			
		||||
			RegisterMethod: "authKey",
 | 
			
		||||
			RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
			AuthKeyID:      uint(pak.ID),
 | 
			
		||||
		}
 | 
			
		||||
		app.db.Save(&machine)
 | 
			
		||||
@ -164,3 +165,37 @@ func (s *Suite) TestGetDirectPeers(c *check.C) {
 | 
			
		||||
	c.Assert(peersOfMachine0[5].Name, check.Equals, "testmachine7")
 | 
			
		||||
	c.Assert(peersOfMachine0[8].Name, check.Equals, "testmachine10")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) TestExpireMachine(c *check.C) {
 | 
			
		||||
	namespace, err := app.CreateNamespace("test")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	_, err = app.GetMachine("test", "testmachine")
 | 
			
		||||
	c.Assert(err, check.NotNil)
 | 
			
		||||
 | 
			
		||||
	machine := &Machine{
 | 
			
		||||
		ID:             0,
 | 
			
		||||
		MachineKey:     "foo",
 | 
			
		||||
		NodeKey:        "bar",
 | 
			
		||||
		DiscoKey:       "faa",
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
		Expiry:         &time.Time{},
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(machine)
 | 
			
		||||
 | 
			
		||||
	machineFromDB, err := app.GetMachine("test", "testmachine")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	c.Assert(machineFromDB.isExpired(), check.Equals, false)
 | 
			
		||||
 | 
			
		||||
	app.ExpireMachine(machineFromDB)
 | 
			
		||||
 | 
			
		||||
	c.Assert(machineFromDB.isExpired(), check.Equals, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
@ -145,7 +145,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared1.ID,
 | 
			
		||||
		Namespace:      *namespaceShared1,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.1",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyShared1.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -163,7 +163,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared2.ID,
 | 
			
		||||
		Namespace:      *namespaceShared2,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.2",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyShared2.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -181,7 +181,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared3.ID,
 | 
			
		||||
		Namespace:      *namespaceShared3,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.3",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKeyShared3.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -199,7 +199,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 | 
			
		||||
		NamespaceID:    namespaceShared1.ID,
 | 
			
		||||
		Namespace:      *namespaceShared1,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.4",
 | 
			
		||||
		AuthKeyID:      uint(preAuthKey2Shared1.ID),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										113
									
								
								oidc.go
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								oidc.go
									
									
									
									
									
								
							@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
@ -15,6 +16,7 @@ import (
 | 
			
		||||
	"github.com/patrickmn/go-cache"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@ -37,7 +39,10 @@ func (h *Headscale) initOIDC() error {
 | 
			
		||||
		h.oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDC.Issuer)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error().Msgf("Could not retrieve OIDC Config: %s", err.Error())
 | 
			
		||||
			log.Error().
 | 
			
		||||
				Err(err).
 | 
			
		||||
				Caller().
 | 
			
		||||
				Msgf("Could not retrieve OIDC Config: %s", err.Error())
 | 
			
		||||
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@ -69,16 +74,23 @@ func (h *Headscale) initOIDC() error {
 | 
			
		||||
// Puts machine key in cache so the callback can retrieve it using the oidc state param
 | 
			
		||||
// Listens in /oidc/register/:mKey.
 | 
			
		||||
func (h *Headscale) RegisterOIDC(ctx *gin.Context) {
 | 
			
		||||
	mKeyStr := ctx.Param("mkey")
 | 
			
		||||
	if mKeyStr == "" {
 | 
			
		||||
	machineKeyStr := ctx.Param("mkey")
 | 
			
		||||
	if machineKeyStr == "" {
 | 
			
		||||
		ctx.String(http.StatusBadRequest, "Wrong params")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Trace().
 | 
			
		||||
		Caller().
 | 
			
		||||
		Str("machine_key", machineKeyStr).
 | 
			
		||||
		Msg("Received oidc register call")
 | 
			
		||||
 | 
			
		||||
	randomBlob := make([]byte, randomByteSize)
 | 
			
		||||
	if _, err := rand.Read(randomBlob); err != nil {
 | 
			
		||||
		log.Error().Msg("could not read 16 bytes from rand")
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Caller().
 | 
			
		||||
			Msg("could not read 16 bytes from rand")
 | 
			
		||||
		ctx.String(http.StatusInternalServerError, "could not read 16 bytes from rand")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
@ -87,7 +99,7 @@ func (h *Headscale) RegisterOIDC(ctx *gin.Context) {
 | 
			
		||||
	stateStr := hex.EncodeToString(randomBlob)[:32]
 | 
			
		||||
 | 
			
		||||
	// place the machine key into the state cache, so it can be retrieved later
 | 
			
		||||
	h.oidcStateCache.Set(stateStr, mKeyStr, oidcStateCacheExpiration)
 | 
			
		||||
	h.oidcStateCache.Set(stateStr, machineKeyStr, oidcStateCacheExpiration)
 | 
			
		||||
 | 
			
		||||
	authURL := h.oauth2Config.AuthCodeURL(stateStr)
 | 
			
		||||
	log.Debug().Msgf("Redirecting to %s for authentication", authURL)
 | 
			
		||||
@ -117,7 +129,11 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debug().Msgf("AccessToken: %v", oauth2Token.AccessToken)
 | 
			
		||||
	log.Trace().
 | 
			
		||||
		Caller().
 | 
			
		||||
		Str("code", code).
 | 
			
		||||
		Str("state", state).
 | 
			
		||||
		Msg("Got oidc callback")
 | 
			
		||||
 | 
			
		||||
	rawIDToken, rawIDTokenOK := oauth2Token.Extra("id_token").(string)
 | 
			
		||||
	if !rawIDTokenOK {
 | 
			
		||||
@ -130,7 +146,11 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
 | 
			
		||||
	idToken, err := verifier.Verify(context.Background(), rawIDToken)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.String(http.StatusBadRequest, "Failed to verify id token: %s", err.Error())
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Caller().
 | 
			
		||||
			Msg("failed to verify id token")
 | 
			
		||||
		ctx.String(http.StatusBadRequest, "Failed to verify id token")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@ -138,34 +158,38 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
	// TODO: we can use userinfo at some point to grab additional information about the user (groups membership, etc)
 | 
			
		||||
	// userInfo, err := oidcProvider.UserInfo(context.Background(), oauth2.StaticTokenSource(oauth2Token))
 | 
			
		||||
	// if err != nil {
 | 
			
		||||
	// 	c.String(http.StatusBadRequest, fmt.Sprintf("Failed to retrieve userinfo: %s", err))
 | 
			
		||||
	// 	c.String(http.StatusBadRequest, fmt.Sprintf("Failed to retrieve userinfo"))
 | 
			
		||||
	// 	return
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	// Extract custom claims
 | 
			
		||||
	var claims IDTokenClaims
 | 
			
		||||
	if err = idToken.Claims(&claims); err != nil {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Err(err).
 | 
			
		||||
			Caller().
 | 
			
		||||
			Msg("Failed to decode id token claims")
 | 
			
		||||
		ctx.String(
 | 
			
		||||
			http.StatusBadRequest,
 | 
			
		||||
			fmt.Sprintf("Failed to decode id token claims: %s", err),
 | 
			
		||||
			"Failed to decode id token claims",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// retrieve machinekey from state cache
 | 
			
		||||
	mKeyIf, mKeyFound := h.oidcStateCache.Get(state)
 | 
			
		||||
	machineKeyIf, machineKeyFound := h.oidcStateCache.Get(state)
 | 
			
		||||
 | 
			
		||||
	if !mKeyFound {
 | 
			
		||||
	if !machineKeyFound {
 | 
			
		||||
		log.Error().
 | 
			
		||||
			Msg("requested machine state key expired before authorisation completed")
 | 
			
		||||
		ctx.String(http.StatusBadRequest, "state has expired")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	mKeyStr, mKeyOK := mKeyIf.(string)
 | 
			
		||||
	machineKey, machineKeyOK := machineKeyIf.(string)
 | 
			
		||||
 | 
			
		||||
	if !mKeyOK {
 | 
			
		||||
	if !machineKeyOK {
 | 
			
		||||
		log.Error().Msg("could not get machine key from cache")
 | 
			
		||||
		ctx.String(
 | 
			
		||||
			http.StatusInternalServerError,
 | 
			
		||||
@ -175,8 +199,16 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set
 | 
			
		||||
	requestedTime := time.Time{}
 | 
			
		||||
	if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey); found {
 | 
			
		||||
		if reqTime, ok := requestedTimeIf.(time.Time); ok {
 | 
			
		||||
			requestedTime = reqTime
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// retrieve machine information
 | 
			
		||||
	machine, err := h.GetMachineByMachineKey(mKeyStr)
 | 
			
		||||
	machine, err := h.GetMachineByMachineKey(machineKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().Msg("machine key not found in database")
 | 
			
		||||
		ctx.String(
 | 
			
		||||
@ -187,6 +219,29 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if machine.isRegistered() {
 | 
			
		||||
		log.Trace().
 | 
			
		||||
			Caller().
 | 
			
		||||
			Str("machine", machine.Name).
 | 
			
		||||
			Msg("machine already registered, reauthenticating")
 | 
			
		||||
 | 
			
		||||
		h.RefreshMachine(machine, requestedTime)
 | 
			
		||||
 | 
			
		||||
		ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`
 | 
			
		||||
<html>
 | 
			
		||||
<body>
 | 
			
		||||
<h1>headscale</h1>
 | 
			
		||||
<p>
 | 
			
		||||
    Reuthenticated as %s, you can now close this window.
 | 
			
		||||
</p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
`, claims.Email)))
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	now := time.Now().UTC()
 | 
			
		||||
 | 
			
		||||
	if namespaceName, ok := h.getNamespaceFromEmail(claims.Email); ok {
 | 
			
		||||
@ -195,12 +250,14 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
			log.Debug().Msg("Registering new machine after successful callback")
 | 
			
		||||
 | 
			
		||||
			namespace, err := h.GetNamespace(namespaceName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
			
		||||
				namespace, err = h.CreateNamespace(namespaceName)
 | 
			
		||||
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Error().
 | 
			
		||||
						Msgf("could not create new namespace '%s'", claims.Email)
 | 
			
		||||
						Err(err).
 | 
			
		||||
						Caller().
 | 
			
		||||
						Msgf("could not create new namespace '%s'", namespaceName)
 | 
			
		||||
					ctx.String(
 | 
			
		||||
						http.StatusInternalServerError,
 | 
			
		||||
						"could not create new namespace",
 | 
			
		||||
@ -208,10 +265,26 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			} else if err != nil {
 | 
			
		||||
				log.Error().
 | 
			
		||||
					Caller().
 | 
			
		||||
					Err(err).
 | 
			
		||||
					Str("namespace", namespaceName).
 | 
			
		||||
					Msg("could not find or create namespace")
 | 
			
		||||
				ctx.String(
 | 
			
		||||
					http.StatusInternalServerError,
 | 
			
		||||
					"could not find or create namespace",
 | 
			
		||||
				)
 | 
			
		||||
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ip, err := h.getAvailableIP()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error().
 | 
			
		||||
					Caller().
 | 
			
		||||
					Err(err).
 | 
			
		||||
					Msg("could not get an IP from the pool")
 | 
			
		||||
				ctx.String(
 | 
			
		||||
					http.StatusInternalServerError,
 | 
			
		||||
					"could not get an IP from the pool",
 | 
			
		||||
@ -223,13 +296,12 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
			machine.IPAddress = ip.String()
 | 
			
		||||
			machine.NamespaceID = namespace.ID
 | 
			
		||||
			machine.Registered = true
 | 
			
		||||
			machine.RegisterMethod = "oidc"
 | 
			
		||||
			machine.RegisterMethod = RegisterMethodOIDC
 | 
			
		||||
			machine.LastSuccessfulUpdate = &now
 | 
			
		||||
			machine.Expiry = &requestedTime
 | 
			
		||||
			h.db.Save(&machine)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		h.updateMachineExpiry(machine)
 | 
			
		||||
 | 
			
		||||
		ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`
 | 
			
		||||
<html>
 | 
			
		||||
<body>
 | 
			
		||||
@ -241,9 +313,12 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
`, claims.Email)))
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Error().
 | 
			
		||||
		Caller().
 | 
			
		||||
		Str("email", claims.Email).
 | 
			
		||||
		Str("username", claims.Username).
 | 
			
		||||
		Str("machine", machine.Name).
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
 | 
			
		||||
		Name:           "testest",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
@ -106,7 +106,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) {
 | 
			
		||||
		Name:           "testest",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
@ -144,7 +144,7 @@ func (*Suite) TestEphemeralKey(c *check.C) {
 | 
			
		||||
		Name:           "testest",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		LastSeen:       &now,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -92,6 +92,12 @@ service HeadscaleService {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rpc ExpireMachine(ExpireMachineRequest) returns (ExpireMachineResponse) {
 | 
			
		||||
        option (google.api.http) = {
 | 
			
		||||
            post: "/api/v1/machine/{machine_id}/expire"
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rpc ListMachines(ListMachinesRequest) returns (ListMachinesResponse) {
 | 
			
		||||
        option (google.api.http) = {
 | 
			
		||||
            get: "/api/v1/machine"
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,14 @@ message DeleteMachineRequest {
 | 
			
		||||
message DeleteMachineResponse {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ExpireMachineRequest {
 | 
			
		||||
    uint64 machine_id = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ExpireMachineResponse {
 | 
			
		||||
    Machine machine = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListMachinesRequest {
 | 
			
		||||
    string namespace = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ func (s *Suite) TestGetRoutes(c *check.C) {
 | 
			
		||||
		Name:           "test_get_route_machine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
		HostInfo:       datatypes.JSON(hostinfo),
 | 
			
		||||
	}
 | 
			
		||||
@ -90,7 +90,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) {
 | 
			
		||||
		Name:           "test_enable_route_machine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
		HostInfo:       datatypes.JSON(hostinfo),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ func CreateNodeNamespace(
 | 
			
		||||
		Name:           node,
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      ip,
 | 
			
		||||
		AuthKeyID:      uint(pak1.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -213,7 +213,7 @@ func (s *Suite) TestComplexSharingAcrossNamespaces(c *check.C) {
 | 
			
		||||
		Name:           "test_get_shared_nodes_4",
 | 
			
		||||
		NamespaceID:    namespace1.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.4",
 | 
			
		||||
		AuthKeyID:      uint(pak4.ID),
 | 
			
		||||
	}
 | 
			
		||||
@ -293,7 +293,7 @@ func (s *Suite) TestDeleteSharedMachine(c *check.C) {
 | 
			
		||||
		Name:           "test_get_shared_nodes_4",
 | 
			
		||||
		NamespaceID:    namespace1.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		IPAddress:      "100.64.0.4",
 | 
			
		||||
		AuthKeyID:      uint(pak4n1.ID),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
		IPAddress:      ip.String(),
 | 
			
		||||
	}
 | 
			
		||||
@ -78,7 +78,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
 | 
			
		||||
			Name:           "testmachine",
 | 
			
		||||
			NamespaceID:    namespace.ID,
 | 
			
		||||
			Registered:     true,
 | 
			
		||||
			RegisterMethod: "authKey",
 | 
			
		||||
			RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
			AuthKeyID:      uint(pak.ID),
 | 
			
		||||
			IPAddress:      ip.String(),
 | 
			
		||||
		}
 | 
			
		||||
@ -151,7 +151,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
 | 
			
		||||
		Name:           "testmachine",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		Registered:     true,
 | 
			
		||||
		RegisterMethod: "authKey",
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
	}
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user