handle register auth errors (#2435)

* handle register auth errors

This commit handles register auth errors as the
Tailscale clients expect. It returns the error as
part of a tailcfg.RegisterResponse and not as a
http error.

In addition it fixes a nil pointer panic triggered
by not handling the errors as part of this chain.

Fixes #2434

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

* 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-23 08:02:46 -08:00 committed by GitHub
parent b220fb7d51
commit bcff0eaae7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 132 additions and 30 deletions

View file

@ -230,6 +230,10 @@ func (ns *noiseServer) NoisePollNetMapHandler(
}
}
func regErr(err error) *tailcfg.RegisterResponse {
return &tailcfg.RegisterResponse{Error: err.Error()}
}
// NoiseRegistrationHandler handles the actual registration process of a node.
func (ns *noiseServer) NoiseRegistrationHandler(
writer http.ResponseWriter,
@ -241,52 +245,47 @@ func (ns *noiseServer) NoiseRegistrationHandler(
return
}
registerRequest, registerResponse, err := func() (*tailcfg.RegisterRequest, []byte, error) {
registerRequest, registerResponse := func() (*tailcfg.RegisterRequest, *tailcfg.RegisterResponse) {
var resp *tailcfg.RegisterResponse
body, err := io.ReadAll(req.Body)
if err != nil {
return nil, nil, err
return &tailcfg.RegisterRequest{}, regErr(err)
}
var registerRequest tailcfg.RegisterRequest
if err := json.Unmarshal(body, &registerRequest); err != nil {
return nil, nil, err
var regReq tailcfg.RegisterRequest
if err := json.Unmarshal(body, &regReq); err != nil {
return &regReq, regErr(err)
}
ns.nodeKey = registerRequest.NodeKey
ns.nodeKey = regReq.NodeKey
resp, err := ns.headscale.handleRegister(req.Context(), registerRequest, ns.conn.Peer())
// TODO(kradalby): Here we could have two error types, one that is surfaced to the client
// and one that returns 500.
resp, err = ns.headscale.handleRegister(req.Context(), regReq, ns.conn.Peer())
if err != nil {
return nil, nil, err
var httpErr HTTPError
if errors.As(err, &httpErr) {
resp = &tailcfg.RegisterResponse{
Error: httpErr.Msg,
}
return &regReq, resp
} else {
}
return &regReq, regErr(err)
}
respBody, err := json.Marshal(resp)
if err != nil {
return nil, nil, err
}
return &registerRequest, respBody, nil
return &regReq, resp
}()
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Error handling registration")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
}
// Reject unsupported versions
if rejectUnsupported(writer, registerRequest.Version, ns.machineKey, registerRequest.NodeKey) {
return
}
respBody, err := json.Marshal(registerResponse)
if err != nil {
httpError(writer, err)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(registerResponse)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
writer.Write(respBody)
}