diff --git a/api.go b/api.go index 96a00b98..9ab6b6b2 100644 --- a/api.go +++ b/api.go @@ -33,12 +33,12 @@ const ( // KeyHandler provides the Headscale pub key // Listens in /key. func (h *Headscale) KeyHandler( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write([]byte(MachinePublicKeyStripPrefix(h.privateKey.Public()))) + writer.Header().Set("Content-Type", "text/plain; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write([]byte(MachinePublicKeyStripPrefix(h.privateKey.Public()))) } type registerWebAPITemplateConfig struct { @@ -65,14 +65,14 @@ var registerWebAPITemplate = template.Must( // RegisterWebAPI shows a simple message in the browser to point to the CLI // Listens in /register. func (h *Headscale) RegisterWebAPI( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { - machineKeyStr := r.URL.Query().Get("key") + machineKeyStr := req.URL.Query().Get("key") if machineKeyStr == "" { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("Wrong params")) + writer.Header().Set("Content-Type", "text/plain; charset=utf-8") + writer.WriteHeader(http.StatusBadRequest) + writer.Write([]byte("Wrong params")) return } @@ -85,34 +85,36 @@ func (h *Headscale) RegisterWebAPI( Str("func", "RegisterWebAPI"). Err(err). Msg("Could not render register web API template") - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("Could not render register web API template")) + writer.Header().Set("Content-Type", "text/plain; charset=utf-8") + writer.WriteHeader(http.StatusInternalServerError) + writer.Write([]byte("Could not render register web API template")) + + return } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(content.Bytes()) + writer.Header().Set("Content-Type", "text/html; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(content.Bytes()) } // RegistrationHandler handles the actual registration process of a machine // Endpoint /machine/:mkey. func (h *Headscale) RegistrationHandler( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { - vars := mux.Vars(r) + vars := mux.Vars(req) machineKeyStr, ok := vars["mkey"] if !ok || machineKeyStr == "" { log.Error(). Str("handler", "RegistrationHandler"). Msg("No machine ID in request") - http.Error(w, "No machine ID in request", http.StatusBadRequest) + http.Error(writer, "No machine ID in request", http.StatusBadRequest) return } - body, _ := io.ReadAll(r.Body) + body, _ := io.ReadAll(req.Body) var machineKey key.MachinePublic err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) @@ -122,19 +124,19 @@ func (h *Headscale) RegistrationHandler( Err(err). Msg("Cannot parse machine key") machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc() - http.Error(w, "Cannot parse machine key", http.StatusBadRequest) + http.Error(writer, "Cannot parse machine key", http.StatusBadRequest) return } - req := tailcfg.RegisterRequest{} - err = decode(body, &req, &machineKey, h.privateKey) + registerRequest := tailcfg.RegisterRequest{} + err = decode(body, ®isterRequest, &machineKey, h.privateKey) if err != nil { log.Error(). Caller(). Err(err). Msg("Cannot decode message") machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc() - http.Error(w, "Cannot decode message", http.StatusBadRequest) + http.Error(writer, "Cannot decode message", http.StatusBadRequest) return } @@ -142,23 +144,23 @@ func (h *Headscale) RegistrationHandler( now := time.Now().UTC() machine, err := h.GetMachineByMachineKey(machineKey) if errors.Is(err, gorm.ErrRecordNotFound) { - log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") + log.Info().Str("machine", registerRequest.Hostinfo.Hostname).Msg("New machine") machineKeyStr := MachinePublicKeyStripPrefix(machineKey) // If the machine has AuthKey set, handle registration via PreAuthKeys - if req.Auth.AuthKey != "" { - h.handleAuthKey(w, r, machineKey, req) + if registerRequest.Auth.AuthKey != "" { + h.handleAuthKey(writer, req, machineKey, registerRequest) return } - givenName, err := h.GenerateGivenName(req.Hostinfo.Hostname) + givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname) if err != nil { log.Error(). Caller(). Str("func", "RegistrationHandler"). - Str("hostinfo.name", req.Hostinfo.Hostname). + Str("hostinfo.name", registerRequest.Hostinfo.Hostname). Err(err) return @@ -170,20 +172,20 @@ func (h *Headscale) RegistrationHandler( // happens newMachine := Machine{ MachineKey: machineKeyStr, - Hostname: req.Hostinfo.Hostname, + Hostname: registerRequest.Hostinfo.Hostname, GivenName: givenName, - NodeKey: NodePublicKeyStripPrefix(req.NodeKey), + NodeKey: NodePublicKeyStripPrefix(registerRequest.NodeKey), LastSeen: &now, Expiry: &time.Time{}, } - if !req.Expiry.IsZero() { + if !registerRequest.Expiry.IsZero() { log.Trace(). Caller(). - Str("machine", req.Hostinfo.Hostname). - Time("expiry", req.Expiry). + Str("machine", registerRequest.Hostinfo.Hostname). + Time("expiry", registerRequest.Expiry). Msg("Non-zero expiry time requested") - newMachine.Expiry = &req.Expiry + newMachine.Expiry = ®isterRequest.Expiry } h.registrationCache.Set( @@ -192,7 +194,7 @@ func (h *Headscale) RegistrationHandler( registerCacheExpiration, ) - h.handleMachineRegistrationNew(w, r, machineKey, req) + h.handleMachineRegistrationNew(writer, req, machineKey, registerRequest) return } @@ -204,11 +206,11 @@ func (h *Headscale) RegistrationHandler( // - 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 == NodePublicKeyStripPrefix(req.NodeKey) { + if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.NodeKey) { // 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(w, r, machineKey, *machine) + if !registerRequest.Expiry.IsZero() && registerRequest.Expiry.UTC().Before(now) { + h.handleMachineLogOut(writer, req, machineKey, *machine) return } @@ -216,22 +218,22 @@ func (h *Headscale) RegistrationHandler( // 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(w, r, machineKey, *machine) + h.handleMachineValidRegistration(writer, req, machineKey, *machine) return } } // The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration - if machine.NodeKey == NodePublicKeyStripPrefix(req.OldNodeKey) && + if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.OldNodeKey) && !machine.isExpired() { - h.handleMachineRefreshKey(w, r, machineKey, req, *machine) + h.handleMachineRefreshKey(writer, req, machineKey, registerRequest, *machine) return } // The machine has expired - h.handleMachineExpired(w, r, machineKey, req, *machine) + h.handleMachineExpired(writer, req, machineKey, registerRequest, *machine) return } @@ -239,12 +241,12 @@ func (h *Headscale) RegistrationHandler( func (h *Headscale) getMapResponse( machineKey key.MachinePublic, - req tailcfg.MapRequest, + mapRequest tailcfg.MapRequest, machine *Machine, ) ([]byte, error) { log.Trace(). Str("func", "getMapResponse"). - Str("machine", req.Hostinfo.Hostname). + Str("machine", mapRequest.Hostinfo.Hostname). Msg("Creating Map response") node, err := machine.toNode(h.cfg.BaseDomain, h.cfg.DNSConfig, true) if err != nil { @@ -305,12 +307,12 @@ func (h *Headscale) getMapResponse( log.Trace(). Str("func", "getMapResponse"). - Str("machine", req.Hostinfo.Hostname). + Str("machine", mapRequest.Hostinfo.Hostname). // Interface("payload", resp). Msgf("Generated map response: %s", tailMapResponseToString(resp)) var respBody []byte - if req.Compress == "zstd" { + if mapRequest.Compress == "zstd" { src, err := json.Marshal(resp) if err != nil { log.Error(). @@ -376,8 +378,8 @@ func (h *Headscale) getMapKeepAliveResponse( } func (h *Headscale) handleMachineLogOut( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, machineKey key.MachinePublic, machine Machine, ) { @@ -398,19 +400,19 @@ func (h *Headscale) handleMachineLogOut( Caller(). Err(err). Msg("Cannot encode message") - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(respBody) } func (h *Headscale) handleMachineValidRegistration( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, machineKey key.MachinePublic, machine Machine, ) { @@ -434,21 +436,21 @@ func (h *Headscale) handleMachineValidRegistration( Msg("Cannot encode message") machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name). Inc() - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name). Inc() - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(respBody) } func (h *Headscale) handleMachineExpired( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, machine Machine, @@ -461,7 +463,7 @@ func (h *Headscale) handleMachineExpired( Msg("Machine registration has expired. Sending a authurl to register") if registerRequest.Auth.AuthKey != "" { - h.handleAuthKey(w, r, machineKey, registerRequest) + h.handleAuthKey(writer, req, machineKey, registerRequest) return } @@ -482,21 +484,21 @@ func (h *Headscale) handleMachineExpired( Msg("Cannot encode message") machineRegistrations.WithLabelValues("reauth", "web", "error", machine.Namespace.Name). Inc() - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } machineRegistrations.WithLabelValues("reauth", "web", "success", machine.Namespace.Name). Inc() - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(respBody) } func (h *Headscale) handleMachineRefreshKey( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, machine Machine, @@ -513,7 +515,7 @@ func (h *Headscale) handleMachineRefreshKey( Caller(). Err(err). Msg("Failed to update machine key in the database") - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } @@ -526,19 +528,19 @@ func (h *Headscale) handleMachineRefreshKey( Caller(). Err(err). Msg("Cannot encode message") - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(respBody) } func (h *Headscale) handleMachineRegistrationNew( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, ) { @@ -565,20 +567,20 @@ func (h *Headscale) handleMachineRegistrationNew( Caller(). Err(err). Msg("Cannot encode message") - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(respBody) } // TODO: check if any locks are needed around IP allocation. func (h *Headscale) handleAuthKey( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, machineKey key.MachinePublic, registerRequest tailcfg.RegisterRequest, ) { @@ -607,16 +609,16 @@ func (h *Headscale) handleAuthKey( Str("machine", registerRequest.Hostinfo.Hostname). Err(err). Msg("Cannot encode message") - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() return } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusUnauthorized) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusUnauthorized) + writer.Write(respBody) log.Error(). Caller(). @@ -691,7 +693,7 @@ func (h *Headscale) handleAuthKey( Msg("could not register machine") machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } @@ -711,15 +713,15 @@ func (h *Headscale) handleAuthKey( Msg("Cannot encode message") machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name). Inc() - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(respBody) + writer.Header().Set("Content-Type", "application/json; charset=utf-8") + writer.WriteHeader(http.StatusOK) + writer.Write(respBody) log.Info(). Str("func", "handleAuthKey"). Str("machine", registerRequest.Hostinfo.Hostname). diff --git a/app.go b/app.go index 952a1803..9fc0ab5b 100644 --- a/app.go +++ b/app.go @@ -332,23 +332,23 @@ func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, func (h *Headscale) httpAuthenticationMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { log.Trace(). Caller(). - Str("client_address", r.RemoteAddr). + Str("client_address", req.RemoteAddr). Msg("HTTP authentication invoked") - authHeader := r.Header.Get("authorization") + authHeader := req.Header.Get("authorization") if !strings.HasPrefix(authHeader, AuthPrefix) { log.Error(). Caller(). - Str("client_address", r.RemoteAddr). + Str("client_address", req.RemoteAddr). Msg(`missing "Bearer " prefix in "Authorization" header`) - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Unauthorized")) + writer.WriteHeader(http.StatusUnauthorized) + writer.Write([]byte("Unauthorized")) return } @@ -358,27 +358,27 @@ func (h *Headscale) httpAuthenticationMiddleware(next http.Handler) http.Handler log.Error(). Caller(). Err(err). - Str("client_address", r.RemoteAddr). + Str("client_address", req.RemoteAddr). Msg("failed to validate token") - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("Unauthorized")) + writer.WriteHeader(http.StatusInternalServerError) + writer.Write([]byte("Unauthorized")) return } if !valid { log.Info(). - Str("client_address", r.RemoteAddr). + Str("client_address", req.RemoteAddr). Msg("invalid token") - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Unauthorized")) + writer.WriteHeader(http.StatusUnauthorized) + writer.Write([]byte("Unauthorized")) return } - next.ServeHTTP(w, r) + next.ServeHTTP(writer, req) }) } @@ -849,15 +849,15 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { } func stdoutHandler( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { - body, _ := io.ReadAll(r.Body) + body, _ := io.ReadAll(req.Body) log.Trace(). - Interface("header", r.Header). - Interface("proto", r.Proto). - Interface("url", r.URL). + Interface("header", req.Header). + Interface("proto", req.Proto). + Interface("url", req.URL). Bytes("body", body). Msg("Request did not match") } diff --git a/derp_server.go b/derp_server.go index 97a6fc06..fb0acba0 100644 --- a/derp_server.go +++ b/derp_server.go @@ -94,30 +94,30 @@ func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) { } func (h *Headscale) DERPHandler( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { - log.Trace().Caller().Msgf("/derp request from %v", r.RemoteAddr) - up := strings.ToLower(r.Header.Get("Upgrade")) + log.Trace().Caller().Msgf("/derp request from %v", req.RemoteAddr) + up := strings.ToLower(req.Header.Get("Upgrade")) if up != "websocket" && up != "derp" { if up != "" { log.Warn().Caller().Msgf("Weird websockets connection upgrade: %q", up) } - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusUpgradeRequired) - w.Write([]byte("DERP requires connection upgrade")) + writer.Header().Set("Content-Type", "text/plain") + writer.WriteHeader(http.StatusUpgradeRequired) + writer.Write([]byte("DERP requires connection upgrade")) return } - fastStart := r.Header.Get(fastStartHeader) == "1" + fastStart := req.Header.Get(fastStartHeader) == "1" - hijacker, ok := w.(http.Hijacker) + hijacker, ok := writer.(http.Hijacker) if !ok { log.Error().Caller().Msg("DERP requires Hijacker interface from Gin") - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("HTTP does not support general TCP support")) + writer.Header().Set("Content-Type", "text/plain") + writer.WriteHeader(http.StatusInternalServerError) + writer.Write([]byte("HTTP does not support general TCP support")) return } @@ -125,13 +125,13 @@ func (h *Headscale) DERPHandler( netConn, conn, err := hijacker.Hijack() if err != nil { log.Error().Caller().Err(err).Msgf("Hijack failed") - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("HTTP does not support general TCP support")) + writer.Header().Set("Content-Type", "text/plain") + writer.WriteHeader(http.StatusInternalServerError) + writer.Write([]byte("HTTP does not support general TCP support")) return } - log.Trace().Caller().Msgf("Hijacked connection from %v", r.RemoteAddr) + log.Trace().Caller().Msgf("Hijacked connection from %v", req.RemoteAddr) if !fastStart { pubKey := h.privateKey.Public() diff --git a/oidc.go b/oidc.go index 67e3b467..441bfd84 100644 --- a/oidc.go +++ b/oidc.go @@ -64,16 +64,16 @@ 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( - w http.ResponseWriter, - r *http.Request, + writer http.ResponseWriter, + req *http.Request, ) { - vars := mux.Vars(r) + vars := mux.Vars(req) machineKeyStr, ok := vars["mkey"] if !ok || machineKeyStr == "" { log.Error(). Caller(). Msg("Missing machine key in URL") - http.Error(w, "Missing machine key in URL", http.StatusBadRequest) + http.Error(writer, "Missing machine key in URL", http.StatusBadRequest) return } @@ -88,7 +88,7 @@ func (h *Headscale) RegisterOIDC( log.Error(). Caller(). Msg("could not read 16 bytes from rand") - http.Error(w, "Internal server error", http.StatusInternalServerError) + http.Error(writer, "Internal server error", http.StatusInternalServerError) return } @@ -108,7 +108,7 @@ func (h *Headscale) RegisterOIDC( authURL := h.oauth2Config.AuthCodeURL(stateStr, extras...) log.Debug().Msgf("Redirecting to %s for authentication", authURL) - http.Redirect(w, r, authURL, http.StatusFound) + http.Redirect(writer, req, authURL, http.StatusFound) } type oidcCallbackTemplateConfig struct { diff --git a/utils.go b/utils.go index fd4cda86..87930a16 100644 --- a/utils.go +++ b/utils.go @@ -324,18 +324,18 @@ func GenerateRandomStringURLSafe(n int) (string, error) { // It will return an error if the system's secure random // number generator fails to function correctly, in which // case the caller should not continue. -func GenerateRandomStringDNSSafe(n int) (string, error) { +func GenerateRandomStringDNSSafe(size int) (string, error) { var str string var err error - for len(str) < n { - str, err = GenerateRandomStringURLSafe(n) + for len(str) < size { + str, err = GenerateRandomStringURLSafe(size) if err != nil { return "", err } str = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", "")) } - return str[:n], nil + return str[:size], nil } func IsStringInSlice(slice []string, str string) bool {