diff --git a/.golangci.yaml b/.golangci.yaml
index 49eea58f..ba518801 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -26,6 +26,7 @@ linters:
- ireturn
- execinquery
- exhaustruct
+ - nolintlint
# We should strive to enable these:
- wrapcheck
diff --git a/README.md b/README.md
index 8dec84c6..661895a6 100644
--- a/README.md
+++ b/README.md
@@ -424,6 +424,13 @@ make build
+
+
+
+
+ LiuHanCheng
+
+ |
@@ -439,7 +446,7 @@ make build
|
-
+
Victor Freire
@@ -459,6 +466,8 @@ make build
thomas
|
+
+
@@ -466,8 +475,13 @@ make build
Abraham Ingersoll
|
-
-
+
+
+
+
+ Andrei Pechkurov
+
+ |
@@ -496,6 +510,8 @@ make build
Bryan Stenson
|
+
+
@@ -510,8 +526,6 @@ make build
kundel
|
-
-
@@ -540,6 +554,8 @@ make build
Jim Tittsler
|
+
+
@@ -554,8 +570,6 @@ make build
Pierre Carru
|
-
-
@@ -579,11 +593,13 @@ make build
|
-
+
- WhiteSource Renovate
+ Mend Renovate
|
+
+
@@ -598,8 +614,6 @@ make build
Shaanan Cohney
|
-
-
@@ -628,6 +642,8 @@ make build
Teteros
|
+
+
@@ -642,8 +658,6 @@ make build
Tianon Gravi
|
-
-
@@ -672,6 +686,8 @@ make build
Yujie Xia
|
+
+
@@ -686,8 +702,6 @@ make build
Zhiyuan Zheng
|
-
-
@@ -716,6 +730,8 @@ make build
ignoramous
|
+
+
@@ -730,8 +746,6 @@ make build
pernila
|
-
-
diff --git a/app.go b/app.go
index 7a67e4a3..c97c3f15 100644
--- a/app.go
+++ b/app.go
@@ -51,12 +51,6 @@ const (
errUnsupportedLetsEncryptChallengeType = Error(
"unknown value for Lets Encrypt challenge type",
)
-
- ErrFailedPrivateKey = Error("failed to read or create private key")
- ErrFailedNoisePrivateKey = Error(
- "failed to read or create Noise protocol private key",
- )
- ErrSamePrivateKeys = Error("private key and noise private key are the same")
)
const (
@@ -131,17 +125,17 @@ func LookupTLSClientAuthMode(mode string) (tls.ClientAuthType, bool) {
func NewHeadscale(cfg *Config) (*Headscale, error) {
privateKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
if err != nil {
- return nil, ErrFailedPrivateKey
+ 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, ErrFailedNoisePrivateKey
+ return nil, fmt.Errorf("failed to read or create Noise protocol private key: %w", err)
}
if privateKey.Equal(*noisePrivateKey) {
- return nil, ErrSamePrivateKeys
+ return nil, fmt.Errorf("private key and noise private key are the same: %w", err)
}
var dbString string
diff --git a/cmd/headscale/cli/debug.go b/cmd/headscale/cli/debug.go
index 8010a9be..0e0e6ff2 100644
--- a/cmd/headscale/cli/debug.go
+++ b/cmd/headscale/cli/debug.go
@@ -3,6 +3,7 @@ package cli
import (
"fmt"
+ "github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
@@ -10,8 +11,7 @@ import (
)
const (
- keyLength = 64
- errPreAuthKeyTooShort = Error("key too short, must be 64 hexadecimal characters")
+ errPreAuthKeyMalformed = Error("key is malformed. expected 64 hex characters with `nodekey` prefix")
)
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
@@ -87,8 +87,8 @@ var createNodeCmd = &cobra.Command{
return
}
- if len(machineKey) != keyLength {
- err = errPreAuthKeyTooShort
+ if !headscale.NodePublicKeyRegex.Match([]byte(machineKey)) {
+ err = errPreAuthKeyMalformed
ErrorOutput(
err,
fmt.Sprintf("Error: %s", err),
diff --git a/config-example.yaml b/config-example.yaml
index c23c7428..0f17fb82 100644
--- a/config-example.yaml
+++ b/config-example.yaml
@@ -35,14 +35,13 @@ grpc_listen_addr: 0.0.0.0:50443
# are doing.
grpc_allow_insecure: false
-# Private key used encrypt the traffic between headscale
+# Private key used to encrypt the traffic between headscale
# and Tailscale clients.
-# The private key file which will be
-# autogenerated if it's missing
+# The private key file will be autogenerated if it's missing.
private_key_path: /var/lib/headscale/private.key
# The Noise section includes specific configuration for the
-# TS2021 Noise procotol
+# TS2021 Noise protocol
noise:
# The Noise private key is used to encrypt the
# traffic between headscale and Tailscale clients when
@@ -78,7 +77,7 @@ derp:
region_code: "headscale"
region_name: "Headscale Embedded DERP"
- # Listens in UDP at the configured address for STUN connections to help on NAT traversal.
+ # Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
#
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
@@ -112,9 +111,9 @@ disable_check_updates: false
# Time before an inactive ephemeral node is deleted?
ephemeral_node_inactivity_timeout: 30m
-# Period to check for node updates in the tailnet. A value too low will severily affect
+# Period to check for node updates within the tailnet. A value too low will severely affect
# CPU consumption of Headscale. A value too high (over 60s) will cause problems
-# to the nodes, as they won't get updates or keep alive messages in time.
+# for the nodes, as they won't get updates or keep alive messages frequently enough.
# In case of doubts, do not touch the default 10s.
node_update_check_interval: 10s
diff --git a/grpcv1.go b/grpcv1.go
index 9fac9aff..25ee7777 100644
--- a/grpcv1.go
+++ b/grpcv1.go
@@ -1,4 +1,4 @@
-//nolint
+// nolint
package headscale
import (
@@ -12,6 +12,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"tailscale.com/tailcfg"
+ "tailscale.com/types/key"
)
type headscaleV1APIServer struct { // v1.HeadscaleServiceServer
@@ -496,9 +497,14 @@ func (api headscaleV1APIServer) DebugCreateMachine(
HostInfo: HostInfo(hostinfo),
}
+ nodeKey := key.NodePublic{}
+ err = nodeKey.UnmarshalText([]byte(request.GetKey()))
+ if err != nil {
+ log.Panic().Msg("can not add machine for debug. invalid node key")
+ }
api.h.registrationCache.Set(
- request.GetKey(),
+ NodePublicKeyStripPrefix(nodeKey),
newMachine,
registerCacheExpiration,
)
diff --git a/integration_cli_test.go b/integration_cli_test.go
index ee6a9e1f..5a5cb0c8 100644
--- a/integration_cli_test.go
+++ b/integration_cli_test.go
@@ -1,4 +1,4 @@
-//nolint
+// nolint
package headscale
import (
@@ -558,8 +558,8 @@ func (s *IntegrationCLITestSuite) TestNodeTagCommand() {
assert.Nil(s.T(), err)
machineKeys := []string{
- "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
- "6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
+ "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
+ "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -691,11 +691,11 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
// Randomly generated machine keys
machineKeys := []string{
- "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
- "6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
- "f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
- "8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
- "cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
+ "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
+ "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
+ "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
+ "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
+ "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -779,8 +779,8 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
assert.Equal(s.T(), "machine-5", listAll[4].Name)
otherNamespaceMachineKeys := []string{
- "b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
- "dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
+ "nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
+ "nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
}
otherNamespaceMachines := make([]*v1.Machine, len(otherNamespaceMachineKeys))
assert.Nil(s.T(), err)
@@ -950,11 +950,11 @@ func (s *IntegrationCLITestSuite) TestNodeExpireCommand() {
// Randomly generated machine keys
machineKeys := []string{
- "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
- "6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
- "f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
- "8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
- "cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
+ "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
+ "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
+ "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
+ "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
+ "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -1077,11 +1077,11 @@ func (s *IntegrationCLITestSuite) TestNodeRenameCommand() {
// Randomly generated machine keys
machineKeys := []string{
- "cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
- "8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
- "f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
- "6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
- "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
+ "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
+ "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
+ "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
+ "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
+ "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
}
machines := make([]*v1.Machine, len(machineKeys))
assert.Nil(s.T(), err)
@@ -1248,7 +1248,7 @@ func (s *IntegrationCLITestSuite) TestRouteCommand() {
assert.Nil(s.T(), err)
// Randomly generated machine keys
- machineKey := "9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe"
+ machineKey := "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe"
_, _, err = ExecuteCommand(
&s.headscale,
@@ -1588,7 +1588,7 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() {
assert.Nil(s.T(), err)
// Randomly generated machine key
- machineKey := "688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
+ machineKey := "nodekey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
_, _, err = ExecuteCommand(
&s.headscale,
diff --git a/integration_oidc_test.go b/integration_oidc_test.go
index 8ca46db7..6dd79bed 100644
--- a/integration_oidc_test.go
+++ b/integration_oidc_test.go
@@ -1,4 +1,4 @@
-//nolint
+// nolint
package headscale
import (
diff --git a/machine.go b/machine.go
index 39e5110a..b688be65 100644
--- a/machine.go
+++ b/machine.go
@@ -839,7 +839,13 @@ func (h *Headscale) RegisterMachineFromAuthCallback(
namespaceName string,
registrationMethod string,
) (*Machine, error) {
- if machineInterface, ok := h.registrationCache.Get(nodeKeyStr); ok {
+ nodeKey := key.NodePublic{}
+ err := nodeKey.UnmarshalText([]byte(nodeKeyStr))
+ if err != nil {
+ return nil, err
+ }
+
+ if machineInterface, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(nodeKey)); ok {
if registrationMachine, ok := machineInterface.(Machine); ok {
namespace, err := h.GetNamespace(namespaceName)
if err != nil {
diff --git a/oidc.go b/oidc.go
index f0af600e..d3423977 100644
--- a/oidc.go
+++ b/oidc.go
@@ -604,10 +604,9 @@ func (h *Headscale) registerMachineForOIDCCallback(
namespace *Namespace,
nodeKey *key.NodePublic,
) error {
- nodeKeyStr := NodePublicKeyStripPrefix(*nodeKey)
if _, err := h.RegisterMachineFromAuthCallback(
- nodeKeyStr,
+ nodeKey.String(),
namespace.Name,
RegisterMethodOIDC,
); err != nil {
diff --git a/protocol_common.go b/protocol_common.go
index c6bc2ee5..e0d2c409 100644
--- a/protocol_common.go
+++ b/protocol_common.go
@@ -435,6 +435,10 @@ func (h *Headscale) handleAuthKeyCommon(
resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser()
+ // Provide LoginName when registering with pre-auth key
+ // Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName*
+ resp.Login = *pak.Namespace.toLogin()
+
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
@@ -490,12 +494,12 @@ func (h *Headscale) handleNewMachineCommon(
resp.AuthURL = fmt.Sprintf(
"%s/oidc/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
- NodePublicKeyStripPrefix(registerRequest.NodeKey),
+ registerRequest.NodeKey,
)
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
- NodePublicKeyStripPrefix(registerRequest.NodeKey))
+ registerRequest.NodeKey)
}
respBody, err := h.marshalResponse(resp, machineKey)
@@ -726,7 +730,7 @@ func (h *Headscale) handleMachineExpiredCommon(
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
- NodePublicKeyStripPrefix(registerRequest.NodeKey))
+ registerRequest.NodeKey)
}
respBody, err := h.marshalResponse(resp, machineKey)
diff --git a/protocol_common_poll.go b/protocol_common_poll.go
index 246d0ce2..9eebe921 100644
--- a/protocol_common_poll.go
+++ b/protocol_common_poll.go
@@ -451,7 +451,7 @@ func (h *Headscale) pollNetMapStream(
Time("last_successful_update", lastUpdate).
Time("last_state_change", h.getLastStateChange(machine.Namespace)).
Msgf("There has been updates since the last successful update to %s", machine.Hostname)
- data, err := h.getMapResponseData(mapRequest, machine, false)
+ data, err := h.getMapResponseData(mapRequest, machine, isNoise)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
@@ -622,7 +622,7 @@ func (h *Headscale) scheduledPollWorker(
defer closeChanWithLog(
keepAliveChan,
fmt.Sprint(ctx.Value(machineNameContextKey)),
- "updateChan",
+ "keepAliveChan",
)
for {
|