Skip to content

Commit

Permalink
remove 2019 private key
Browse files Browse the repository at this point in the history
Signed-off-by: Kristoffer Dalby <[email protected]>
  • Loading branch information
kradalby committed Nov 19, 2023
1 parent af1c2ad commit f230013
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 90 deletions.
15 changes: 7 additions & 8 deletions config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,12 @@ grpc_listen_addr: 127.0.0.1:50443
# are doing.
grpc_allow_insecure: false

# Private key used to encrypt the traffic between headscale
# and Tailscale clients.
# 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 protocol
noise:
# The Noise private key is used to encrypt the
# traffic between headscale and Tailscale clients when
# using the new Noise-based protocol. It must be different
# from the legacy private key.
# using the new Noise-based protocol.
private_key_path: /var/lib/headscale/noise_private.key

# List of IP prefixes to allocate tailaddresses from.
Expand Down Expand Up @@ -95,6 +88,12 @@ derp:
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
stun_listen_addr: "0.0.0.0:3478"

# Private key used to encrypt the traffic between headscale DERP
# and Tailscale clients.
# The private key file will be autogenerated if it's missing.
#
private_key_path: /var/lib/headscale/derp_server_private.key

# List of externally available DERP maps encoded in JSON
urls:
- https://controlplane.tailscale.com/derpmap/default
Expand Down
30 changes: 10 additions & 20 deletions hscontrol/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ type Headscale struct {
dbString string
dbType string
dbDebug bool
privateKey2019 *key.MachinePrivate
noisePrivateKey *key.MachinePrivate

DERPMap *tailcfg.DERPMap
Expand All @@ -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:
Expand Down Expand Up @@ -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{},
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -913,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)
}

Expand Down
10 changes: 4 additions & 6 deletions hscontrol/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ 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,
Expand Down Expand Up @@ -214,7 +212,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.
Expand Down Expand Up @@ -418,8 +415,9 @@ func (h *Headscale) handleAuthKey(
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,
Expand Down
24 changes: 1 addition & 23 deletions hscontrol/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"html/template"
"net/http"
"strconv"
"strings"
"time"

"github.com/gorilla/mux"
Expand Down Expand Up @@ -64,26 +63,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).
Expand Down Expand Up @@ -121,8 +100,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)
Expand Down
7 changes: 2 additions & 5 deletions hscontrol/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ 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
capVer tailcfg.CapabilityVersion
capVer tailcfg.CapabilityVersion

// Configuration
// TODO(kradalby): figure out if this is the format we want this in
Expand All @@ -72,7 +71,6 @@ type Mapper struct {
func NewMapper(
node *types.Node,
peers types.Nodes,
privateKey *key.MachinePrivate,
capVer tailcfg.CapabilityVersion,
derpMap *tailcfg.DERPMap,
baseDomain string,
Expand All @@ -88,8 +86,7 @@ func NewMapper(
uid, _ := util.GenerateRandomStringDNSSafe(mapperIDLength)

return &Mapper{
privateKey2019: privateKey,
capVer: capVer,
capVer: capVer,

derpMap: derpMap,
baseDomain: baseDomain,
Expand Down
1 change: 0 additions & 1 deletion hscontrol/mapper/mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,6 @@ func Test_fullMapResponse(t *testing.T) {
mappy := NewMapper(
tt.node,
tt.peers,
nil,
0,
tt.derpMap,
tt.baseDomain,
Expand Down
6 changes: 2 additions & 4 deletions hscontrol/poll.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,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(
Expand Down Expand Up @@ -156,7 +156,6 @@ func (h *Headscale) handlePoll(
mapp := mapper.NewMapper(
node,
peers,
h.privateKey2019,
capVer,
h.DERPMap,
h.cfg.BaseDomain,
Expand Down Expand Up @@ -385,7 +384,6 @@ func (h *Headscale) handleLiteRequest(
// TODO(kradalby): It might not be acceptable to send
// an empty peer list here.
types.Nodes{},
h.privateKey2019,
capVer,
h.DERPMap,
h.cfg.BaseDomain,
Expand Down
1 change: 0 additions & 1 deletion hscontrol/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
43 changes: 21 additions & 22 deletions hscontrol/types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ type Config struct {
EphemeralNodeInactivityTimeout time.Duration
NodeUpdateCheckInterval time.Duration
IPPrefixes []netip.Prefix
PrivateKeyPath string
NoisePrivateKeyPath string
BaseDomain string
Log LogConfig
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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().
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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"),
),
Expand Down

0 comments on commit f230013

Please sign in to comment.