Return better web errors to the user (#2398)

* add dedicated http error to propagate to user

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* classify user errors in http handlers

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* move validation of pre auth key out of db

This move separates the logic a bit and allow us to
write specific errors for the caller, in this case the web
layer so we can present the user with the correct error
codes without bleeding web stuff into a generic validate.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-02-01 15:25:18 +01:00 committed by GitHub
parent 1c7f3bc440
commit 45752db0f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 268 additions and 229 deletions

View file

@ -3,6 +3,7 @@ package hscontrol
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@ -12,6 +13,7 @@ import (
"github.com/juanfont/headscale/hscontrol/types"
"github.com/rs/zerolog/log"
"golang.org/x/net/http2"
"gorm.io/gorm"
"tailscale.com/control/controlbase"
"tailscale.com/control/controlhttp/controlhttpserver"
"tailscale.com/tailcfg"
@ -81,7 +83,7 @@ func (h *Headscale) NoiseUpgradeHandler(
noiseServer.earlyNoise,
)
if err != nil {
httpError(writer, err, "noise upgrade failed", http.StatusInternalServerError)
httpError(writer, fmt.Errorf("noise upgrade failed: %w", err))
return
}
@ -198,7 +200,7 @@ func (ns *noiseServer) NoisePollNetMapHandler(
var mapRequest tailcfg.MapRequest
if err := json.Unmarshal(body, &mapRequest); err != nil {
httpError(writer, err, "Internal error", http.StatusInternalServerError)
httpError(writer, err)
return
}
@ -211,7 +213,11 @@ func (ns *noiseServer) NoisePollNetMapHandler(
node, err := ns.headscale.db.GetNodeByNodeKey(mapRequest.NodeKey)
if err != nil {
httpError(writer, err, "Internal error", http.StatusInternalServerError)
if errors.Is(err, gorm.ErrRecordNotFound) {
httpError(writer, NewHTTPError(http.StatusNotFound, "node not found", nil))
return
}
httpError(writer, err)
return
}
@ -230,7 +236,7 @@ func (ns *noiseServer) NoiseRegistrationHandler(
req *http.Request,
) {
if req.Method != http.MethodPost {
httpError(writer, nil, "Wrong method", http.StatusMethodNotAllowed)
httpError(writer, errMethodNotAllowed)
return
}