Remove support for non-noise clients (pre-1.32) (#1611)
This commit is contained in:
parent
b918aa03fc
commit
a59aab2081
72 changed files with 319 additions and 679 deletions
|
@ -77,7 +77,6 @@ type Headscale struct {
|
|||
dbString string
|
||||
dbType string
|
||||
dbDebug bool
|
||||
privateKey2019 *key.MachinePrivate
|
||||
noisePrivateKey *key.MachinePrivate
|
||||
|
||||
DERPMap *tailcfg.DERPMap
|
||||
|
@ -101,21 +100,11 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
|||
runtime.SetBlockProfileRate(1)
|
||||
}
|
||||
|
||||
privateKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read or create private key: %w", err)
|
||||
}
|
||||
|
||||
// TS2021 requires to have a different key from the legacy protocol.
|
||||
noisePrivateKey, err := readOrCreatePrivateKey(cfg.NoisePrivateKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read or create Noise protocol private key: %w", err)
|
||||
}
|
||||
|
||||
if privateKey.Equal(*noisePrivateKey) {
|
||||
return nil, fmt.Errorf("private key and noise private key are the same: %w", err)
|
||||
}
|
||||
|
||||
var dbString string
|
||||
switch cfg.DBtype {
|
||||
case db.Postgres:
|
||||
|
@ -156,7 +145,6 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
|||
cfg: cfg,
|
||||
dbType: cfg.DBtype,
|
||||
dbString: dbString,
|
||||
privateKey2019: privateKey,
|
||||
noisePrivateKey: noisePrivateKey,
|
||||
registrationCache: registrationCache,
|
||||
pollNetMapStreamWG: sync.WaitGroup{},
|
||||
|
@ -199,10 +187,18 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
|||
}
|
||||
|
||||
if cfg.DERP.ServerEnabled {
|
||||
// TODO(kradalby): replace this key with a dedicated DERP key.
|
||||
derpServerKey, err := readOrCreatePrivateKey(cfg.DERP.ServerPrivateKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read or create DERP server private key: %w", err)
|
||||
}
|
||||
|
||||
if derpServerKey.Equal(*noisePrivateKey) {
|
||||
return nil, fmt.Errorf("DERP server private key and noise private key are the same: %w", err)
|
||||
}
|
||||
|
||||
embeddedDERPServer, err := derpServer.NewDERPServer(
|
||||
cfg.ServerURL,
|
||||
key.NodePrivate(*privateKey),
|
||||
key.NodePrivate(*derpServerKey),
|
||||
&cfg.DERP,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -450,7 +446,6 @@ func (h *Headscale) createRouter(grpcMux *grpcRuntime.ServeMux) *mux.Router {
|
|||
router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet)
|
||||
router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet)
|
||||
router.HandleFunc("/register/{mkey}", h.RegisterWebAPI).Methods(http.MethodGet)
|
||||
h.addLegacyHandlers(router)
|
||||
|
||||
router.HandleFunc("/oidc/register/{mkey}", h.RegisterOIDC).Methods(http.MethodGet)
|
||||
router.HandleFunc("/oidc/callback", h.OIDCCallback).Methods(http.MethodGet)
|
||||
|
@ -914,12 +909,6 @@ func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) {
|
|||
|
||||
var machineKey key.MachinePrivate
|
||||
if err = machineKey.UnmarshalText([]byte(trimmedPrivateKey)); err != nil {
|
||||
log.Info().
|
||||
Str("path", path).
|
||||
Msg("This might be due to a legacy (headscale pre-0.12) private key. " +
|
||||
"If the key is in WireGuard format, delete the key and restart headscale. " +
|
||||
"A new key will automatically be generated. All Tailscale clients will have to be restarted")
|
||||
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package hscontrol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/juanfont/headscale/hscontrol/mapper"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
@ -16,22 +16,19 @@ import (
|
|||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
// handleRegister is the common logic for registering a client in the legacy and Noise protocols
|
||||
//
|
||||
// When using Noise, the machineKey is Zero.
|
||||
// handleRegister is the logic for registering a client.
|
||||
func (h *Headscale) handleRegister(
|
||||
writer http.ResponseWriter,
|
||||
req *http.Request,
|
||||
registerRequest tailcfg.RegisterRequest,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
now := time.Now().UTC()
|
||||
node, err := h.db.GetNodeByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// If the node has AuthKey set, handle registration via PreAuthKeys
|
||||
if registerRequest.Auth.AuthKey != "" {
|
||||
h.handleAuthKey(writer, registerRequest, machineKey, isNoise)
|
||||
h.handleAuthKey(writer, registerRequest, machineKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -53,14 +50,13 @@ func (h *Headscale) handleRegister(
|
|||
Str("node_key", registerRequest.NodeKey.ShortString()).
|
||||
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
|
||||
Str("follow_up", registerRequest.Followup).
|
||||
Bool("noise", isNoise).
|
||||
Msg("Node is waiting for interactive login")
|
||||
|
||||
select {
|
||||
case <-req.Context().Done():
|
||||
return
|
||||
case <-time.After(registrationHoldoff):
|
||||
h.handleNewNode(writer, registerRequest, machineKey, isNoise)
|
||||
h.handleNewNode(writer, registerRequest, machineKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -74,7 +70,6 @@ func (h *Headscale) handleRegister(
|
|||
Str("node_key", registerRequest.NodeKey.ShortString()).
|
||||
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
|
||||
Str("follow_up", registerRequest.Followup).
|
||||
Bool("noise", isNoise).
|
||||
Msg("New node not yet in the database")
|
||||
|
||||
givenName, err := h.db.GenerateGivenName(
|
||||
|
@ -108,7 +103,6 @@ func (h *Headscale) handleRegister(
|
|||
if !registerRequest.Expiry.IsZero() {
|
||||
log.Trace().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Time("expiry", registerRequest.Expiry).
|
||||
Msg("Non-zero expiry time requested")
|
||||
|
@ -121,7 +115,7 @@ func (h *Headscale) handleRegister(
|
|||
registerCacheExpiration,
|
||||
)
|
||||
|
||||
h.handleNewNode(writer, registerRequest, machineKey, isNoise)
|
||||
h.handleNewNode(writer, registerRequest, machineKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -157,7 +151,7 @@ func (h *Headscale) handleRegister(
|
|||
// https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
|
||||
if !registerRequest.Expiry.IsZero() &&
|
||||
registerRequest.Expiry.UTC().Before(now) {
|
||||
h.handleNodeLogOut(writer, *node, machineKey, isNoise)
|
||||
h.handleNodeLogOut(writer, *node, machineKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -165,7 +159,7 @@ func (h *Headscale) handleRegister(
|
|||
// If node is not expired, and it is register, we have a already accepted this node,
|
||||
// let it proceed with a valid registration
|
||||
if !node.IsExpired() {
|
||||
h.handleNodeWithValidRegistration(writer, *node, machineKey, isNoise)
|
||||
h.handleNodeWithValidRegistration(writer, *node, machineKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -179,7 +173,6 @@ func (h *Headscale) handleRegister(
|
|||
registerRequest,
|
||||
*node,
|
||||
machineKey,
|
||||
isNoise,
|
||||
)
|
||||
|
||||
return
|
||||
|
@ -194,7 +187,7 @@ func (h *Headscale) handleRegister(
|
|||
}
|
||||
|
||||
// The node has expired or it is logged out
|
||||
h.handleNodeExpiredOrLoggedOut(writer, registerRequest, *node, machineKey, isNoise)
|
||||
h.handleNodeExpiredOrLoggedOut(writer, registerRequest, *node, machineKey)
|
||||
|
||||
// TODO(juan): RegisterRequest includes an Expiry time, that we could optionally use
|
||||
node.Expiry = &time.Time{}
|
||||
|
@ -215,7 +208,6 @@ func (h *Headscale) handleRegister(
|
|||
}
|
||||
|
||||
// handleAuthKey contains the logic to manage auth key client registration
|
||||
// It is used both by the legacy and the new Noise protocol.
|
||||
// When using Noise, the machineKey is Zero.
|
||||
//
|
||||
// TODO: check if any locks are needed around IP allocation.
|
||||
|
@ -223,12 +215,10 @@ func (h *Headscale) handleAuthKey(
|
|||
writer http.ResponseWriter,
|
||||
registerRequest tailcfg.RegisterRequest,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
log.Debug().
|
||||
Caller().
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Bool("noise", isNoise).
|
||||
Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
|
||||
resp := tailcfg.RegisterResponse{}
|
||||
|
||||
|
@ -236,17 +226,15 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Err(err).
|
||||
Msg("Failed authentication via AuthKey")
|
||||
resp.MachineAuthorized = false
|
||||
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
|
@ -263,14 +251,12 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Msg("Failed authentication via AuthKey")
|
||||
|
||||
|
@ -286,7 +272,6 @@ func (h *Headscale) handleAuthKey(
|
|||
|
||||
log.Debug().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Msg("Authentication key was valid, proceeding to acquire IP addresses")
|
||||
|
||||
|
@ -300,7 +285,6 @@ func (h *Headscale) handleAuthKey(
|
|||
if node != nil {
|
||||
log.Trace().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("node was already registered before, refreshing with new auth key")
|
||||
|
||||
|
@ -310,7 +294,6 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Err(err).
|
||||
Msg("Failed to refresh node")
|
||||
|
@ -318,7 +301,7 @@ func (h *Headscale) handleAuthKey(
|
|||
return
|
||||
}
|
||||
|
||||
aclTags := pak.Proto().AclTags
|
||||
aclTags := pak.Proto().GetAclTags()
|
||||
if len(aclTags) > 0 {
|
||||
// This conditional preserves the existing behaviour, although SaaS would reset the tags on auth-key login
|
||||
err = h.db.SetTags(node, aclTags)
|
||||
|
@ -326,7 +309,6 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Strs("aclTags", aclTags).
|
||||
Err(err).
|
||||
|
@ -342,7 +324,6 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("func", "RegistrationHandler").
|
||||
Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
|
||||
Err(err).
|
||||
|
@ -361,7 +342,7 @@ func (h *Headscale) handleAuthKey(
|
|||
NodeKey: nodeKey,
|
||||
LastSeen: &now,
|
||||
AuthKeyID: uint(pak.ID),
|
||||
ForcedTags: pak.Proto().AclTags,
|
||||
ForcedTags: pak.Proto().GetAclTags(),
|
||||
}
|
||||
|
||||
node, err = h.db.RegisterNode(
|
||||
|
@ -370,7 +351,6 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("could not register node")
|
||||
nodeRegistrations.WithLabelValues("new", util.RegisterMethodAuthKey, "error", pak.User.Name).
|
||||
|
@ -385,7 +365,6 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to use pre-auth key")
|
||||
nodeRegistrations.WithLabelValues("new", util.RegisterMethodAuthKey, "error", pak.User.Name).
|
||||
|
@ -401,11 +380,10 @@ func (h *Headscale) handleAuthKey(
|
|||
// Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName*
|
||||
resp.Login = *pak.User.TailscaleLogin()
|
||||
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
|
@ -423,32 +401,29 @@ func (h *Headscale) handleAuthKey(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Str("ips", strings.Join(node.IPAddresses.StringSlice(), ", ")).
|
||||
Msg("Successfully authenticated via AuthKey")
|
||||
}
|
||||
|
||||
// handleNewNode exposes for both legacy and Noise the functionality to get a URL
|
||||
// for authorizing the node. This url is then showed to the user by the local Tailscale client.
|
||||
// handleNewNode returns the authorisation URL to the client based on what type
|
||||
// of registration headscale is configured with.
|
||||
// This url is then showed to the user by the local Tailscale client.
|
||||
func (h *Headscale) handleNewNode(
|
||||
writer http.ResponseWriter,
|
||||
registerRequest tailcfg.RegisterRequest,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
resp := tailcfg.RegisterResponse{}
|
||||
|
||||
// The node registration is new, redirect the client to the registration URL
|
||||
log.Debug().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Msg("The node seems to be new, sending auth url")
|
||||
|
||||
|
@ -464,11 +439,10 @@ func (h *Headscale) handleNewNode(
|
|||
machineKey.String())
|
||||
}
|
||||
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
http.Error(writer, "Internal server error", http.StatusInternalServerError)
|
||||
|
@ -481,7 +455,6 @@ func (h *Headscale) handleNewNode(
|
|||
_, err = writer.Write(respBody)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Bool("noise", isNoise).
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
|
@ -489,7 +462,6 @@ func (h *Headscale) handleNewNode(
|
|||
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("AuthURL", resp.AuthURL).
|
||||
Str("node", registerRequest.Hostinfo.Hostname).
|
||||
Msg("Successfully sent auth url")
|
||||
|
@ -499,12 +471,10 @@ func (h *Headscale) handleNodeLogOut(
|
|||
writer http.ResponseWriter,
|
||||
node types.Node,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
resp := tailcfg.RegisterResponse{}
|
||||
|
||||
log.Info().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("Client requested logout")
|
||||
|
||||
|
@ -513,7 +483,6 @@ func (h *Headscale) handleNodeLogOut(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to expire node")
|
||||
http.Error(writer, "Internal server error", http.StatusInternalServerError)
|
||||
|
@ -525,11 +494,10 @@ func (h *Headscale) handleNodeLogOut(
|
|||
resp.MachineAuthorized = false
|
||||
resp.NodeKeyExpired = true
|
||||
resp.User = *node.User.TailscaleUser()
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
http.Error(writer, "Internal server error", http.StatusInternalServerError)
|
||||
|
@ -542,7 +510,6 @@ func (h *Headscale) handleNodeLogOut(
|
|||
_, err = writer.Write(respBody)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Bool("noise", isNoise).
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
|
@ -564,7 +531,6 @@ func (h *Headscale) handleNodeLogOut(
|
|||
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("Successfully logged out")
|
||||
}
|
||||
|
@ -573,14 +539,12 @@ func (h *Headscale) handleNodeWithValidRegistration(
|
|||
writer http.ResponseWriter,
|
||||
node types.Node,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
resp := tailcfg.RegisterResponse{}
|
||||
|
||||
// The node registration is valid, respond with redirect to /map
|
||||
log.Debug().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("Client is registered and we have the current NodeKey. All clear to /map")
|
||||
|
||||
|
@ -589,11 +553,10 @@ func (h *Headscale) handleNodeWithValidRegistration(
|
|||
resp.User = *node.User.TailscaleUser()
|
||||
resp.Login = *node.User.TailscaleLogin()
|
||||
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
nodeRegistrations.WithLabelValues("update", "web", "error", node.User.Name).
|
||||
|
@ -611,14 +574,12 @@ func (h *Headscale) handleNodeWithValidRegistration(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("Node successfully authorized")
|
||||
}
|
||||
|
@ -628,13 +589,11 @@ func (h *Headscale) handleNodeKeyRefresh(
|
|||
registerRequest tailcfg.RegisterRequest,
|
||||
node types.Node,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
resp := tailcfg.RegisterResponse{}
|
||||
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("We have the OldNodeKey in the database. This is a key refresh")
|
||||
|
||||
|
@ -651,11 +610,10 @@ func (h *Headscale) handleNodeKeyRefresh(
|
|||
|
||||
resp.AuthURL = ""
|
||||
resp.User = *node.User.TailscaleUser()
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
http.Error(writer, "Internal server error", http.StatusInternalServerError)
|
||||
|
@ -669,14 +627,12 @@ func (h *Headscale) handleNodeKeyRefresh(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node_key", registerRequest.NodeKey.ShortString()).
|
||||
Str("old_node_key", registerRequest.OldNodeKey.ShortString()).
|
||||
Str("node", node.Hostname).
|
||||
|
@ -688,12 +644,11 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
|
|||
registerRequest tailcfg.RegisterRequest,
|
||||
node types.Node,
|
||||
machineKey key.MachinePublic,
|
||||
isNoise bool,
|
||||
) {
|
||||
resp := tailcfg.RegisterResponse{}
|
||||
|
||||
if registerRequest.Auth.AuthKey != "" {
|
||||
h.handleAuthKey(writer, registerRequest, machineKey, isNoise)
|
||||
h.handleAuthKey(writer, registerRequest, machineKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -701,7 +656,6 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
|
|||
// The client has registered before, but has expired or logged out
|
||||
log.Trace().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Str("machine_key", machineKey.ShortString()).
|
||||
Str("node_key", registerRequest.NodeKey.ShortString()).
|
||||
|
@ -718,11 +672,10 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
|
|||
machineKey.String())
|
||||
}
|
||||
|
||||
respBody, err := mapper.MarshalResponse(resp, isNoise, h.privateKey2019, machineKey)
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Cannot encode message")
|
||||
nodeRegistrations.WithLabelValues("reauth", "web", "error", node.User.Name).
|
||||
|
@ -740,14 +693,12 @@ func (h *Headscale) handleNodeExpiredOrLoggedOut(
|
|||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
log.Trace().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("machine_key", machineKey.ShortString()).
|
||||
Str("node_key", registerRequest.NodeKey.ShortString()).
|
||||
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
//go:build ts2019
|
||||
|
||||
package hscontrol
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
// RegistrationHandler handles the actual registration process of a machine
|
||||
// Endpoint /machine/:mkey.
|
||||
func (h *Headscale) RegistrationHandler(
|
||||
writer http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) {
|
||||
vars := mux.Vars(req)
|
||||
machineKeyStr, ok := vars["mkey"]
|
||||
if !ok || machineKeyStr == "" {
|
||||
log.Error().
|
||||
Str("handler", "RegistrationHandler").
|
||||
Msg("No machine ID in request")
|
||||
http.Error(writer, "No machine ID in request", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(req.Body)
|
||||
|
||||
var machineKey key.MachinePublic
|
||||
err := machineKey.UnmarshalText([]byte("mkey:" + machineKeyStr))
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Cannot parse machine key")
|
||||
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
|
||||
http.Error(writer, "Cannot parse machine key", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
registerRequest := tailcfg.RegisterRequest{}
|
||||
err = util.DecodeAndUnmarshalNaCl(body, ®isterRequest, &machineKey, h.privateKey2019)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Cannot decode message")
|
||||
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
|
||||
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
h.handleRegister(writer, req, registerRequest, machineKey, false)
|
||||
}
|
|
@ -39,7 +39,19 @@ func (ns *noiseServer) NoiseRegistrationHandler(
|
|||
return
|
||||
}
|
||||
|
||||
// Reject unsupported versions
|
||||
if registerRequest.Version < MinimumCapVersion {
|
||||
log.Info().
|
||||
Caller().
|
||||
Int("min_version", int(MinimumCapVersion)).
|
||||
Int("client_version", int(registerRequest.Version)).
|
||||
Msg("unsupported client connected")
|
||||
http.Error(writer, "Internal error", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ns.nodeKey = registerRequest.NodeKey
|
||||
|
||||
ns.headscale.handleRegister(writer, req, registerRequest, ns.conn.Peer(), true)
|
||||
ns.headscale.handleRegister(writer, req, registerRequest, ns.conn.Peer())
|
||||
}
|
||||
|
|
|
@ -198,5 +198,5 @@ func (*Suite) TestPreAuthKeyACLTags(c *check.C) {
|
|||
|
||||
listedPaks, err := db.ListPreAuthKeys("test8")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(listedPaks[0].Proto().AclTags, check.DeepEquals, tags)
|
||||
c.Assert(listedPaks[0].Proto().GetAclTags(), check.DeepEquals, tags)
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
//go:build ts2019
|
||||
|
||||
package hscontrol
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (h *Headscale) addLegacyHandlers(router *mux.Router) {
|
||||
router.HandleFunc("/machine/{mkey}/map", h.PollNetMapHandler).
|
||||
Methods(http.MethodPost)
|
||||
router.HandleFunc("/machine/{mkey}", h.RegistrationHandler).Methods(http.MethodPost)
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
//go:build !ts2019
|
||||
|
||||
package hscontrol
|
||||
|
||||
import "github.com/gorilla/mux"
|
||||
|
||||
func (h *Headscale) addLegacyHandlers(router *mux.Router) {
|
||||
}
|
|
@ -8,7 +8,6 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -63,26 +62,6 @@ func (h *Headscale) KeyHandler(
|
|||
// New Tailscale clients send a 'v' parameter to indicate the CurrentCapabilityVersion
|
||||
capVer, err := parseCabailityVersion(req)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoCapabilityVersion) {
|
||||
log.Debug().
|
||||
Str("handler", "/key").
|
||||
Msg("New legacy client")
|
||||
// Old clients don't send a 'v' parameter, so we send the legacy public key
|
||||
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, err := writer.Write(
|
||||
[]byte(strings.TrimPrefix(h.privateKey2019.Public().String(), "mkey:")),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
|
@ -101,7 +80,7 @@ func (h *Headscale) KeyHandler(
|
|||
|
||||
log.Debug().
|
||||
Str("handler", "/key").
|
||||
Int("v", int(capVer)).
|
||||
Int("cap_ver", int(capVer)).
|
||||
Msg("New noise client")
|
||||
if err != nil {
|
||||
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
|
@ -120,8 +99,7 @@ func (h *Headscale) KeyHandler(
|
|||
// TS2021 (Tailscale v2 protocol) requires to have a different key
|
||||
if capVer >= NoiseCapabilityVersion {
|
||||
resp := tailcfg.OverTLSPublicKeyResponse{
|
||||
LegacyPublicKey: h.privateKey2019.Public(),
|
||||
PublicKey: h.noisePrivateKey.Public(),
|
||||
PublicKey: h.noisePrivateKey.Public(),
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -48,10 +47,6 @@ var debugDumpMapResponsePath = envknob.String("HEADSCALE_DEBUG_DUMP_MAPRESPONSE_
|
|||
// - Create a "minifier" that removes info not needed for the node
|
||||
|
||||
type Mapper struct {
|
||||
privateKey2019 *key.MachinePrivate
|
||||
isNoise bool
|
||||
capVer tailcfg.CapabilityVersion
|
||||
|
||||
// Configuration
|
||||
// TODO(kradalby): figure out if this is the format we want this in
|
||||
derpMap *tailcfg.DERPMap
|
||||
|
@ -73,9 +68,6 @@ type Mapper struct {
|
|||
func NewMapper(
|
||||
node *types.Node,
|
||||
peers types.Nodes,
|
||||
privateKey *key.MachinePrivate,
|
||||
isNoise bool,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
derpMap *tailcfg.DERPMap,
|
||||
baseDomain string,
|
||||
dnsCfg *tailcfg.DNSConfig,
|
||||
|
@ -84,17 +76,12 @@ func NewMapper(
|
|||
) *Mapper {
|
||||
log.Debug().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Str("node", node.Hostname).
|
||||
Msg("creating new mapper")
|
||||
|
||||
uid, _ := util.GenerateRandomStringDNSSafe(mapperIDLength)
|
||||
|
||||
return &Mapper{
|
||||
privateKey2019: privateKey,
|
||||
isNoise: isNoise,
|
||||
capVer: capVer,
|
||||
|
||||
derpMap: derpMap,
|
||||
baseDomain: baseDomain,
|
||||
dnsCfg: dnsCfg,
|
||||
|
@ -212,10 +199,11 @@ func addNextDNSMetadata(resolvers []*dnstype.Resolver, node *types.Node) {
|
|||
func (m *Mapper) fullMapResponse(
|
||||
node *types.Node,
|
||||
pol *policy.ACLPolicy,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
) (*tailcfg.MapResponse, error) {
|
||||
peers := nodeMapToList(m.peers)
|
||||
|
||||
resp, err := m.baseWithConfigMapResponse(node, pol)
|
||||
resp, err := m.baseWithConfigMapResponse(node, pol, capVer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -224,7 +212,7 @@ func (m *Mapper) fullMapResponse(
|
|||
resp,
|
||||
pol,
|
||||
node,
|
||||
m.capVer,
|
||||
capVer,
|
||||
peers,
|
||||
peers,
|
||||
m.baseDomain,
|
||||
|
@ -247,15 +235,11 @@ func (m *Mapper) FullMapResponse(
|
|||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
resp, err := m.fullMapResponse(node, pol)
|
||||
resp, err := m.fullMapResponse(node, pol, mapRequest.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if m.isNoise {
|
||||
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
|
||||
}
|
||||
|
||||
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
|
||||
}
|
||||
|
||||
|
@ -267,15 +251,11 @@ func (m *Mapper) LiteMapResponse(
|
|||
node *types.Node,
|
||||
pol *policy.ACLPolicy,
|
||||
) ([]byte, error) {
|
||||
resp, err := m.baseWithConfigMapResponse(node, pol)
|
||||
resp, err := m.baseWithConfigMapResponse(node, pol, mapRequest.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if m.isNoise {
|
||||
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
|
||||
}
|
||||
|
||||
return m.marshalMapResponse(mapRequest, resp, node, mapRequest.Compress)
|
||||
}
|
||||
|
||||
|
@ -325,7 +305,7 @@ func (m *Mapper) PeerChangedResponse(
|
|||
&resp,
|
||||
pol,
|
||||
node,
|
||||
m.capVer,
|
||||
mapRequest.Version,
|
||||
nodeMapToList(m.peers),
|
||||
changed,
|
||||
m.baseDomain,
|
||||
|
@ -414,15 +394,8 @@ func (m *Mapper) marshalMapResponse(
|
|||
var respBody []byte
|
||||
if compression == util.ZstdCompression {
|
||||
respBody = zstdEncode(jsonBody)
|
||||
if !m.isNoise { // if legacy protocol
|
||||
respBody = m.privateKey2019.SealTo(node.MachineKey, respBody)
|
||||
}
|
||||
} else {
|
||||
if !m.isNoise { // if legacy protocol
|
||||
respBody = m.privateKey2019.SealTo(node.MachineKey, jsonBody)
|
||||
} else {
|
||||
respBody = jsonBody
|
||||
}
|
||||
respBody = jsonBody
|
||||
}
|
||||
|
||||
data := make([]byte, reservedResponseHeaderSize)
|
||||
|
@ -432,32 +405,6 @@ func (m *Mapper) marshalMapResponse(
|
|||
return data, nil
|
||||
}
|
||||
|
||||
// MarshalResponse takes an Tailscale Response, marhsal it to JSON.
|
||||
// If isNoise is set, then the JSON body will be returned
|
||||
// If !isNoise and privateKey2019 is set, the JSON body will be sealed in a Nacl box.
|
||||
func MarshalResponse(
|
||||
resp interface{},
|
||||
isNoise bool,
|
||||
privateKey2019 *key.MachinePrivate,
|
||||
machineKey key.MachinePublic,
|
||||
) ([]byte, error) {
|
||||
jsonBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Cannot marshal response")
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !isNoise && privateKey2019 != nil {
|
||||
return privateKey2019.SealTo(machineKey, jsonBody), nil
|
||||
}
|
||||
|
||||
return jsonBody, nil
|
||||
}
|
||||
|
||||
func zstdEncode(in []byte) []byte {
|
||||
encoder, ok := zstdEncoderPool.Get().(*zstd.Encoder)
|
||||
if !ok {
|
||||
|
@ -503,10 +450,11 @@ func (m *Mapper) baseMapResponse() tailcfg.MapResponse {
|
|||
func (m *Mapper) baseWithConfigMapResponse(
|
||||
node *types.Node,
|
||||
pol *policy.ACLPolicy,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
) (*tailcfg.MapResponse, error) {
|
||||
resp := m.baseMapResponse()
|
||||
|
||||
tailnode, err := tailNode(node, m.capVer, pol, m.dnsCfg, m.baseDomain, m.randomClientPort)
|
||||
tailnode, err := tailNode(node, capVer, pol, m.dnsCfg, m.baseDomain, m.randomClientPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -472,9 +472,6 @@ func Test_fullMapResponse(t *testing.T) {
|
|||
mappy := NewMapper(
|
||||
tt.node,
|
||||
tt.peers,
|
||||
nil,
|
||||
false,
|
||||
0,
|
||||
tt.derpMap,
|
||||
tt.baseDomain,
|
||||
tt.dnsConfig,
|
||||
|
@ -485,6 +482,7 @@ func Test_fullMapResponse(t *testing.T) {
|
|||
got, err := mappy.fullMapResponse(
|
||||
tt.node,
|
||||
tt.pol,
|
||||
0,
|
||||
)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
|
|
|
@ -25,12 +25,10 @@ type UpdateNode func()
|
|||
func logPollFunc(
|
||||
mapRequest tailcfg.MapRequest,
|
||||
node *types.Node,
|
||||
isNoise bool,
|
||||
) (func(string), func(error, string)) {
|
||||
return func(msg string) {
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Bool("readOnly", mapRequest.ReadOnly).
|
||||
Bool("omitPeers", mapRequest.OmitPeers).
|
||||
Bool("stream", mapRequest.Stream).
|
||||
|
@ -41,7 +39,6 @@ func logPollFunc(
|
|||
func(err error, msg string) {
|
||||
log.Error().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Bool("readOnly", mapRequest.ReadOnly).
|
||||
Bool("omitPeers", mapRequest.OmitPeers).
|
||||
Bool("stream", mapRequest.Stream).
|
||||
|
@ -52,8 +49,8 @@ func logPollFunc(
|
|||
}
|
||||
}
|
||||
|
||||
// handlePoll is the common code for the legacy and Noise protocols to
|
||||
// managed the poll loop.
|
||||
// handlePoll ensures the node gets the appropriate updates from either
|
||||
// polling or immediate responses.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (h *Headscale) handlePoll(
|
||||
|
@ -61,10 +58,8 @@ func (h *Headscale) handlePoll(
|
|||
ctx context.Context,
|
||||
node *types.Node,
|
||||
mapRequest tailcfg.MapRequest,
|
||||
isNoise bool,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
) {
|
||||
logInfo, logErr := logPollFunc(mapRequest, node, isNoise)
|
||||
logInfo, logErr := logPollFunc(mapRequest, node)
|
||||
|
||||
// This is the mechanism where the node gives us inforamtion about its
|
||||
// current configuration.
|
||||
|
@ -77,12 +72,12 @@ func (h *Headscale) handlePoll(
|
|||
if mapRequest.OmitPeers && !mapRequest.Stream && !mapRequest.ReadOnly {
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Bool("readOnly", mapRequest.ReadOnly).
|
||||
Bool("omitPeers", mapRequest.OmitPeers).
|
||||
Bool("stream", mapRequest.Stream).
|
||||
Str("node_key", node.NodeKey.ShortString()).
|
||||
Str("node", node.Hostname).
|
||||
Int("cap_ver", int(mapRequest.Version)).
|
||||
Msg("Received endpoint update")
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
@ -129,7 +124,7 @@ func (h *Headscale) handlePoll(
|
|||
// The intended use is for clients to discover the DERP map at
|
||||
// start-up before their first real endpoint update.
|
||||
} else if mapRequest.OmitPeers && !mapRequest.Stream && mapRequest.ReadOnly {
|
||||
h.handleLiteRequest(writer, node, mapRequest, isNoise, capVer)
|
||||
h.handleLiteRequest(writer, node, mapRequest)
|
||||
|
||||
return
|
||||
} else if mapRequest.OmitPeers && mapRequest.Stream {
|
||||
|
@ -160,9 +155,6 @@ func (h *Headscale) handlePoll(
|
|||
mapp := mapper.NewMapper(
|
||||
node,
|
||||
peers,
|
||||
h.privateKey2019,
|
||||
isNoise,
|
||||
capVer,
|
||||
h.DERPMap,
|
||||
h.cfg.BaseDomain,
|
||||
h.cfg.DNSConfig,
|
||||
|
@ -337,7 +329,6 @@ func (h *Headscale) handlePoll(
|
|||
|
||||
log.Info().
|
||||
Caller().
|
||||
Bool("noise", isNoise).
|
||||
Bool("readOnly", mapRequest.ReadOnly).
|
||||
Bool("omitPeers", mapRequest.OmitPeers).
|
||||
Bool("stream", mapRequest.Stream).
|
||||
|
@ -382,19 +373,14 @@ func (h *Headscale) handleLiteRequest(
|
|||
writer http.ResponseWriter,
|
||||
node *types.Node,
|
||||
mapRequest tailcfg.MapRequest,
|
||||
isNoise bool,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
) {
|
||||
logInfo, logErr := logPollFunc(mapRequest, node, isNoise)
|
||||
logInfo, logErr := logPollFunc(mapRequest, node)
|
||||
|
||||
mapp := mapper.NewMapper(
|
||||
node,
|
||||
// TODO(kradalby): It might not be acceptable to send
|
||||
// an empty peer list here.
|
||||
types.Nodes{},
|
||||
h.privateKey2019,
|
||||
isNoise,
|
||||
capVer,
|
||||
h.DERPMap,
|
||||
h.cfg.BaseDomain,
|
||||
h.cfg.DNSConfig,
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
//go:build ts2019
|
||||
|
||||
package hscontrol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
// PollNetMapHandler takes care of /machine/:id/map
|
||||
//
|
||||
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
|
||||
// the clients when something in the network changes.
|
||||
//
|
||||
// The clients POST stuff like HostInfo and their Endpoints here, but
|
||||
// only after their first request (marked with the ReadOnly field).
|
||||
//
|
||||
// At this moment the updates are sent in a quite horrendous way, but they kinda work.
|
||||
func (h *Headscale) PollNetMapHandler(
|
||||
writer http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) {
|
||||
vars := mux.Vars(req)
|
||||
machineKeyStr, ok := vars["mkey"]
|
||||
if !ok || machineKeyStr == "" {
|
||||
log.Error().
|
||||
Str("handler", "PollNetMap").
|
||||
Msg("No machine key in request")
|
||||
http.Error(writer, "No machine key in request", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
log.Trace().
|
||||
Str("handler", "PollNetMap").
|
||||
Str("id", machineKeyStr).
|
||||
Msg("PollNetMapHandler called")
|
||||
body, _ := io.ReadAll(req.Body)
|
||||
|
||||
var machineKey key.MachinePublic
|
||||
err := machineKey.UnmarshalText([]byte("mkey:" + machineKeyStr))
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("handler", "PollNetMap").
|
||||
Err(err).
|
||||
Msg("Cannot parse client key")
|
||||
|
||||
http.Error(writer, "Cannot parse client key", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
mapRequest := tailcfg.MapRequest{}
|
||||
err = util.DecodeAndUnmarshalNaCl(body, &mapRequest, &machineKey, h.privateKey2019)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("handler", "PollNetMap").
|
||||
Err(err).
|
||||
Msg("Cannot decode message")
|
||||
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
node, err := h.db.GetNodeByMachineKey(machineKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
log.Warn().
|
||||
Str("handler", "PollNetMap").
|
||||
Msgf("Ignoring request, cannot find node with key %s", machineKey.String())
|
||||
|
||||
http.Error(writer, "", http.StatusUnauthorized)
|
||||
|
||||
return
|
||||
}
|
||||
log.Error().
|
||||
Str("handler", "PollNetMap").
|
||||
Msgf("Failed to fetch node from the database with Machine key: %s", machineKey.String())
|
||||
http.Error(writer, "", http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace().
|
||||
Str("handler", "PollNetMap").
|
||||
Str("id", machineKeyStr).
|
||||
Str("node", node.Hostname).
|
||||
Msg("A node is sending a MapRequest via legacy protocol")
|
||||
|
||||
capVer, err := parseCabailityVersion(req)
|
||||
if err != nil && !errors.Is(err, ErrNoCapabilityVersion) {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("failed to parse capVer")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
h.handlePoll(writer, req.Context(), node, mapRequest, false, capVer)
|
||||
}
|
|
@ -12,6 +12,10 @@ import (
|
|||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
const (
|
||||
MinimumCapVersion tailcfg.CapabilityVersion = 36
|
||||
)
|
||||
|
||||
// NoisePollNetMapHandler takes care of /machine/:id/map using the Noise protocol
|
||||
//
|
||||
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
|
||||
|
@ -47,6 +51,18 @@ func (ns *noiseServer) NoisePollNetMapHandler(
|
|||
return
|
||||
}
|
||||
|
||||
// Reject unsupported versions
|
||||
if mapRequest.Version < MinimumCapVersion {
|
||||
log.Info().
|
||||
Caller().
|
||||
Int("min_version", int(MinimumCapVersion)).
|
||||
Int("client_version", int(mapRequest.Version)).
|
||||
Msg("unsupported client connected")
|
||||
http.Error(writer, "Internal error", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ns.nodeKey = mapRequest.NodeKey
|
||||
|
||||
node, err := ns.headscale.db.GetNodeByAnyKey(
|
||||
|
@ -73,20 +89,8 @@ func (ns *noiseServer) NoisePollNetMapHandler(
|
|||
log.Debug().
|
||||
Str("handler", "NoisePollNetMap").
|
||||
Str("node", node.Hostname).
|
||||
Int("cap_ver", int(mapRequest.Version)).
|
||||
Msg("A node sending a MapRequest with Noise protocol")
|
||||
|
||||
capVer, err := parseCabailityVersion(req)
|
||||
if err != nil && !errors.Is(err, ErrNoCapabilityVersion) {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("failed to parse capVer")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(kradalby): since we are now passing capVer, we could arguably stop passing
|
||||
// isNoise, and rather have a isNoise function that takes capVer
|
||||
ns.headscale.handlePoll(writer, req.Context(), node, mapRequest, true, capVer)
|
||||
ns.headscale.handlePoll(writer, req.Context(), node, mapRequest)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ func (s *Suite) ResetDB(c *check.C) {
|
|||
c.Fatal(err)
|
||||
}
|
||||
cfg := types.Config{
|
||||
PrivateKeyPath: tmpDir + "/private.key",
|
||||
NoisePrivateKeyPath: tmpDir + "/noise_private.key",
|
||||
DBtype: "sqlite3",
|
||||
DBpath: tmpDir + "/headscale_test.db",
|
||||
|
|
|
@ -41,7 +41,6 @@ type Config struct {
|
|||
EphemeralNodeInactivityTimeout time.Duration
|
||||
NodeUpdateCheckInterval time.Duration
|
||||
IPPrefixes []netip.Prefix
|
||||
PrivateKeyPath string
|
||||
NoisePrivateKeyPath string
|
||||
BaseDomain string
|
||||
Log LogConfig
|
||||
|
@ -108,15 +107,16 @@ type OIDCConfig struct {
|
|||
}
|
||||
|
||||
type DERPConfig struct {
|
||||
ServerEnabled bool
|
||||
ServerRegionID int
|
||||
ServerRegionCode string
|
||||
ServerRegionName string
|
||||
STUNAddr string
|
||||
URLs []url.URL
|
||||
Paths []string
|
||||
AutoUpdate bool
|
||||
UpdateFrequency time.Duration
|
||||
ServerEnabled bool
|
||||
ServerRegionID int
|
||||
ServerRegionCode string
|
||||
ServerRegionName string
|
||||
ServerPrivateKeyPath string
|
||||
STUNAddr string
|
||||
URLs []url.URL
|
||||
Paths []string
|
||||
AutoUpdate bool
|
||||
UpdateFrequency time.Duration
|
||||
}
|
||||
|
||||
type LogTailConfig struct {
|
||||
|
@ -286,6 +286,7 @@ func GetDERPConfig() DERPConfig {
|
|||
serverRegionCode := viper.GetString("derp.server.region_code")
|
||||
serverRegionName := viper.GetString("derp.server.region_name")
|
||||
stunAddr := viper.GetString("derp.server.stun_listen_addr")
|
||||
privateKeyPath := util.AbsolutePathFromConfigPath(viper.GetString("derp.server.private_key_path"))
|
||||
|
||||
if serverEnabled && stunAddr == "" {
|
||||
log.Fatal().
|
||||
|
@ -313,15 +314,16 @@ func GetDERPConfig() DERPConfig {
|
|||
updateFrequency := viper.GetDuration("derp.update_frequency")
|
||||
|
||||
return DERPConfig{
|
||||
ServerEnabled: serverEnabled,
|
||||
ServerRegionID: serverRegionID,
|
||||
ServerRegionCode: serverRegionCode,
|
||||
ServerRegionName: serverRegionName,
|
||||
STUNAddr: stunAddr,
|
||||
URLs: urls,
|
||||
Paths: paths,
|
||||
AutoUpdate: autoUpdate,
|
||||
UpdateFrequency: updateFrequency,
|
||||
ServerEnabled: serverEnabled,
|
||||
ServerRegionID: serverRegionID,
|
||||
ServerRegionCode: serverRegionCode,
|
||||
ServerRegionName: serverRegionName,
|
||||
ServerPrivateKeyPath: privateKeyPath,
|
||||
STUNAddr: stunAddr,
|
||||
URLs: urls,
|
||||
Paths: paths,
|
||||
AutoUpdate: autoUpdate,
|
||||
UpdateFrequency: updateFrequency,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,9 +584,6 @@ func GetHeadscaleConfig() (*Config, error) {
|
|||
DisableUpdateCheck: viper.GetBool("disable_check_updates"),
|
||||
|
||||
IPPrefixes: prefixes,
|
||||
PrivateKeyPath: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("private_key_path"),
|
||||
),
|
||||
NoisePrivateKeyPath: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("noise.private_key_path"),
|
||||
),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue