2020-06-21 12:32:08 +02:00
|
|
|
package headscale
|
|
|
|
|
|
|
|
import (
|
2021-12-23 03:43:53 +01:00
|
|
|
"bytes"
|
2020-06-21 12:32:08 +02:00
|
|
|
"encoding/json"
|
2021-12-23 03:43:53 +01:00
|
|
|
"html/template"
|
2020-06-21 12:32:08 +02:00
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2022-06-20 12:30:41 +02:00
|
|
|
"github.com/gorilla/mux"
|
2021-11-13 09:39:04 +01:00
|
|
|
"github.com/rs/zerolog/log"
|
2022-09-23 10:39:42 +02:00
|
|
|
"tailscale.com/types/key"
|
2020-06-21 12:32:08 +02:00
|
|
|
)
|
|
|
|
|
2021-11-18 09:49:55 +01:00
|
|
|
const (
|
2022-08-12 09:36:17 +02:00
|
|
|
// TODO(juan): remove this once https://github.com/juanfont/headscale/issues/727 is fixed.
|
2022-08-12 09:02:58 +02:00
|
|
|
registrationHoldoff = time.Second * 5
|
2021-11-22 20:32:52 +01:00
|
|
|
reservedResponseHeaderSize = 4
|
2022-02-27 18:48:12 +01:00
|
|
|
RegisterMethodAuthKey = "authkey"
|
2021-11-22 20:32:52 +01:00
|
|
|
RegisterMethodOIDC = "oidc"
|
|
|
|
RegisterMethodCLI = "cli"
|
|
|
|
ErrRegisterMethodCLIDoesNotSupportExpire = Error(
|
|
|
|
"machines registered with CLI does not support expire",
|
|
|
|
)
|
2021-11-18 09:49:55 +01:00
|
|
|
)
|
2021-11-14 18:31:51 +01:00
|
|
|
|
2022-07-06 13:39:10 +02:00
|
|
|
func (h *Headscale) HealthHandler(
|
|
|
|
writer http.ResponseWriter,
|
|
|
|
req *http.Request,
|
|
|
|
) {
|
|
|
|
respond := func(err error) {
|
|
|
|
writer.Header().Set("Content-Type", "application/health+json; charset=utf-8")
|
|
|
|
|
|
|
|
res := struct {
|
|
|
|
Status string `json:"status"`
|
|
|
|
}{
|
|
|
|
Status: "pass",
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
writer.WriteHeader(http.StatusInternalServerError)
|
|
|
|
log.Error().Caller().Err(err).Msg("health check failed")
|
|
|
|
res.Status = "fail"
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := json.Marshal(res)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Caller().Err(err).Msg("marshal failed")
|
|
|
|
}
|
|
|
|
_, err = writer.Write(buf)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Caller().Err(err).Msg("write failed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-04 11:43:09 +02:00
|
|
|
if err := h.pingDB(req.Context()); err != nil {
|
2022-07-06 13:39:10 +02:00
|
|
|
respond(err)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
respond(nil)
|
|
|
|
}
|
|
|
|
|
2021-12-23 03:43:53 +01:00
|
|
|
type registerWebAPITemplateConfig struct {
|
|
|
|
Key string
|
|
|
|
}
|
2021-02-28 00:58:09 +01:00
|
|
|
|
2021-12-23 03:43:53 +01:00
|
|
|
var registerWebAPITemplate = template.Must(
|
2022-03-08 09:34:46 +01:00
|
|
|
template.New("registerweb").Parse(`
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Registration - Headscale</title>
|
|
|
|
</head>
|
2021-02-28 00:58:09 +01:00
|
|
|
<body>
|
2022-03-08 09:34:46 +01:00
|
|
|
<h1>headscale</h1>
|
|
|
|
<h2>Machine registration</h2>
|
|
|
|
<p>
|
|
|
|
Run the command below in the headscale server to add this machine to your network:
|
|
|
|
</p>
|
2023-01-20 01:37:37 +01:00
|
|
|
<pre><code>headscale nodes register --user USERNAME --key {{.Key}}</code></pre>
|
2021-02-28 00:58:09 +01:00
|
|
|
</body>
|
2022-03-08 09:34:46 +01:00
|
|
|
</html>
|
|
|
|
`))
|
2021-12-23 03:43:53 +01:00
|
|
|
|
|
|
|
// RegisterWebAPI shows a simple message in the browser to point to the CLI
|
2022-08-11 12:11:02 +02:00
|
|
|
// Listens in /register/:nkey.
|
2022-08-11 12:16:50 +02:00
|
|
|
//
|
|
|
|
// This is not part of the Tailscale control API, as we could send whatever URL
|
|
|
|
// in the RegisterResponse.AuthURL field.
|
2022-06-17 16:48:04 +02:00
|
|
|
func (h *Headscale) RegisterWebAPI(
|
2022-06-26 11:55:37 +02:00
|
|
|
writer http.ResponseWriter,
|
|
|
|
req *http.Request,
|
2022-06-17 16:48:04 +02:00
|
|
|
) {
|
2022-08-11 12:11:02 +02:00
|
|
|
vars := mux.Vars(req)
|
|
|
|
nodeKeyStr, ok := vars["nkey"]
|
2022-09-23 10:39:42 +02:00
|
|
|
|
2022-09-23 11:51:38 +02:00
|
|
|
if !NodePublicKeyRegex.Match([]byte(nodeKeyStr)) {
|
|
|
|
log.Warn().Str("node_key", nodeKeyStr).Msg("Invalid node key passed to registration url")
|
|
|
|
|
|
|
|
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
writer.WriteHeader(http.StatusUnauthorized)
|
|
|
|
_, err := writer.Write([]byte("Unauthorized"))
|
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Failed to write response")
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-09-23 10:39:42 +02:00
|
|
|
// 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
|
|
|
|
// the template and log an error.
|
|
|
|
var nodeKey key.NodePublic
|
|
|
|
err := nodeKey.UnmarshalText(
|
|
|
|
[]byte(NodePublicKeyEnsurePrefix(nodeKeyStr)),
|
|
|
|
)
|
|
|
|
|
|
|
|
if !ok || nodeKeyStr == "" || err != nil {
|
|
|
|
log.Warn().Err(err).Msg("Failed to parse incoming nodekey")
|
|
|
|
|
2022-06-26 11:55:37 +02:00
|
|
|
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
writer.WriteHeader(http.StatusBadRequest)
|
2022-06-26 12:21:35 +02:00
|
|
|
_, err := writer.Write([]byte("Wrong params"))
|
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Failed to write response")
|
|
|
|
}
|
2021-12-23 03:43:53 +01:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var content bytes.Buffer
|
|
|
|
if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{
|
2022-08-10 15:35:26 +02:00
|
|
|
Key: nodeKeyStr,
|
2021-12-23 03:43:53 +01:00
|
|
|
}); err != nil {
|
|
|
|
log.Error().
|
|
|
|
Str("func", "RegisterWebAPI").
|
|
|
|
Err(err).
|
|
|
|
Msg("Could not render register web API template")
|
2022-06-26 11:55:37 +02:00
|
|
|
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
writer.WriteHeader(http.StatusInternalServerError)
|
2022-06-26 12:21:35 +02:00
|
|
|
_, err = writer.Write([]byte("Could not render register web API template"))
|
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Failed to write response")
|
|
|
|
}
|
2022-06-26 11:55:37 +02:00
|
|
|
|
|
|
|
return
|
2021-12-23 03:43:53 +01:00
|
|
|
}
|
2021-05-24 21:59:03 +02:00
|
|
|
|
2022-06-26 11:55:37 +02:00
|
|
|
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
writer.WriteHeader(http.StatusOK)
|
2022-09-23 10:39:42 +02:00
|
|
|
_, err = writer.Write(content.Bytes())
|
2022-06-26 12:21:35 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Caller().
|
|
|
|
Err(err).
|
|
|
|
Msg("Failed to write response")
|
|
|
|
}
|
2021-02-28 00:58:09 +01:00
|
|
|
}
|