add registration_id support to web auth

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-01-17 16:58:00 +01:00
parent 0890dd69c5
commit a949ca4617
No known key found for this signature in database
2 changed files with 12 additions and 55 deletions

View file

@ -9,15 +9,12 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/chasefleming/elem-go"
"github.com/chasefleming/elem-go/attrs"
"github.com/chasefleming/elem-go/styles" "github.com/chasefleming/elem-go/styles"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/juanfont/headscale/hscontrol/templates" "github.com/juanfont/headscale/hscontrol/templates"
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key"
) )
const ( const (
@ -202,31 +199,6 @@ var codeStyleRegisterWebAPI = styles.Props{
styles.BackgroundColor: "#eee", styles.BackgroundColor: "#eee",
} }
func registerWebHTML(key string) *elem.Element {
return elem.Html(nil,
elem.Head(
nil,
elem.Title(nil, elem.Text("Registration - Headscale")),
elem.Meta(attrs.Props{
attrs.Name: "viewport",
attrs.Content: "width=device-width, initial-scale=1",
}),
),
elem.Body(attrs.Props{
attrs.Style: styles.Props{
styles.FontFamily: "sans",
}.ToInline(),
},
elem.H1(nil, elem.Text("headscale")),
elem.H2(nil, elem.Text("Machine registration")),
elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network:")),
elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()},
elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", key)),
),
),
)
}
type AuthProviderWeb struct { type AuthProviderWeb struct {
serverURL string serverURL string
} }
@ -245,7 +217,7 @@ func (a *AuthProviderWeb) AuthURL(registrationId types.RegistrationID) string {
} }
// RegisterWebAPI shows a simple message in the browser to point to the CLI // RegisterWebAPI shows a simple message in the browser to point to the CLI
// Listens in /register/:nkey. // Listens in /register/:registration_id.
// //
// This is not part of the Tailscale control API, as we could send whatever URL // This is not part of the Tailscale control API, as we could send whatever URL
// in the RegisterResponse.AuthURL field. // in the RegisterResponse.AuthURL field.
@ -254,39 +226,23 @@ func (a *AuthProviderWeb) RegisterHandler(
req *http.Request, req *http.Request,
) { ) {
vars := mux.Vars(req) vars := mux.Vars(req)
machineKeyStr := vars["mkey"] registrationIdStr := vars["registration_id"]
// We need to make sure we dont open for XSS style injections, if the parameter that // We need to make sure we dont open for XSS style injections, if the parameter that
// is passed as a key is not parsable/validated as a NodePublic key, then fail to render // is passed as a key is not parsable/validated as a NodePublic key, then fail to render
// the template and log an error. // the template and log an error.
var machineKey key.MachinePublic registrationId, err := types.RegistrationIDFromString(registrationIdStr)
err := machineKey.UnmarshalText(
[]byte(machineKeyStr),
)
if err != nil { if err != nil {
log.Warn().Err(err).Msg("Failed to parse incoming machinekey") http.Error(writer, "invalid registration ID", http.StatusBadRequest)
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusBadRequest)
_, err := writer.Write([]byte("Wrong params"))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
return return
} }
writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.WriteHeader(http.StatusOK) writer.WriteHeader(http.StatusOK)
if _, err := writer.Write([]byte(registerWebHTML(machineKey.String()).Render())); err != nil { if _, err := writer.Write([]byte(templates.RegisterWeb(registrationId).Render())); err != nil {
if _, err := writer.Write([]byte(templates.RegisterWeb(machineKey.String()).Render())); err != nil { log.Error().
log.Error(). Caller().
Caller(). Err(err).
Err(err). Msg("Failed to write response")
Msg("Failed to write response")
}
} }
} }

View file

@ -6,6 +6,7 @@ import (
"github.com/chasefleming/elem-go" "github.com/chasefleming/elem-go"
"github.com/chasefleming/elem-go/attrs" "github.com/chasefleming/elem-go/attrs"
"github.com/chasefleming/elem-go/styles" "github.com/chasefleming/elem-go/styles"
"github.com/juanfont/headscale/hscontrol/types"
) )
var codeStyleRegisterWebAPI = styles.Props{ var codeStyleRegisterWebAPI = styles.Props{
@ -15,7 +16,7 @@ var codeStyleRegisterWebAPI = styles.Props{
styles.BackgroundColor: "#eee", styles.BackgroundColor: "#eee",
} }
func RegisterWeb(key string) *elem.Element { func RegisterWeb(registrationID types.RegistrationID) *elem.Element {
return HtmlStructure( return HtmlStructure(
elem.Title(nil, elem.Text("Registration - Headscale")), elem.Title(nil, elem.Text("Registration - Headscale")),
elem.Body(attrs.Props{ elem.Body(attrs.Props{
@ -27,7 +28,7 @@ func RegisterWeb(key string) *elem.Element {
elem.H2(nil, elem.Text("Machine registration")), elem.H2(nil, elem.Text("Machine registration")),
elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network: ")), elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network: ")),
elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()}, elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()},
elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", key)), elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", registrationID.String())),
), ),
), ),
) )