Add back privatekey, but automatically generate it if it does not exist

This commit is contained in:
Kristoffer Dalby 2021-11-28 09:17:18 +00:00
parent 32006f3a20
commit 34f4109fbd
4 changed files with 73 additions and 16 deletions

2
api.go
View file

@ -34,7 +34,7 @@ func (h *Headscale) KeyHandler(ctx *gin.Context) {
ctx.Data( ctx.Data(
http.StatusOK, http.StatusOK,
"text/plain; charset=utf-8", "text/plain; charset=utf-8",
[]byte(MachinePublicKeyStripPrefix(*h.publicKey)), []byte(MachinePublicKeyStripPrefix(h.privateKey.Public())),
) )
} }

67
app.go
View file

@ -47,11 +47,12 @@ import (
) )
const ( const (
AuthPrefix = "Bearer " AuthPrefix = "Bearer "
Postgres = "postgres" Postgres = "postgres"
Sqlite = "sqlite3" Sqlite = "sqlite3"
updateInterval = 5000 updateInterval = 5000
HTTPReadTimeout = 30 * time.Second HTTPReadTimeout = 30 * time.Second
privateKeyFileMode = 0o600
requestedExpiryCacheExpiration = time.Minute * 5 requestedExpiryCacheExpiration = time.Minute * 5
requestedExpiryCacheCleanupInterval = time.Minute * 10 requestedExpiryCacheCleanupInterval = time.Minute * 10
@ -68,6 +69,7 @@ type Config struct {
Addr string Addr string
EphemeralNodeInactivityTimeout time.Duration EphemeralNodeInactivityTimeout time.Duration
IPPrefix netaddr.IPPrefix IPPrefix netaddr.IPPrefix
PrivateKeyPath string
BaseDomain string BaseDomain string
DERP DERPConfig DERP DERPConfig
@ -128,7 +130,6 @@ type Headscale struct {
dbString string dbString string
dbType string dbType string
dbDebug bool dbDebug bool
publicKey *key.MachinePublic
privateKey *key.MachinePrivate privateKey *key.MachinePrivate
DERPMap *tailcfg.DERPMap DERPMap *tailcfg.DERPMap
@ -147,8 +148,10 @@ type Headscale struct {
// NewHeadscale returns the Headscale app. // NewHeadscale returns the Headscale app.
func NewHeadscale(cfg Config) (*Headscale, error) { func NewHeadscale(cfg Config) (*Headscale, error) {
privKey := key.NewMachine() privKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
pubKey := privKey.Public() if err != nil {
return nil, fmt.Errorf("failed to read or create private key: %w", err)
}
var dbString string var dbString string
switch cfg.DBtype { switch cfg.DBtype {
@ -176,13 +179,12 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
cfg: cfg, cfg: cfg,
dbType: cfg.DBtype, dbType: cfg.DBtype,
dbString: dbString, dbString: dbString,
privateKey: &privKey, privateKey: privKey,
publicKey: &pubKey,
aclRules: tailcfg.FilterAllowAll, // default allowall aclRules: tailcfg.FilterAllowAll, // default allowall
requestedExpiryCache: requestedExpiryCache, requestedExpiryCache: requestedExpiryCache,
} }
err := app.initDB() err = app.initDB()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -694,3 +696,46 @@ func stdoutHandler(ctx *gin.Context) {
Bytes("body", body). Bytes("body", body).
Msg("Request did not match") Msg("Request did not match")
} }
func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) {
privateKey, err := os.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
log.Info().Str("path", path).Msg("No private key file at path, creating...")
machineKey := key.NewMachine()
machineKeyStr, err := machineKey.MarshalText()
if err != nil {
return nil, fmt.Errorf(
"failed to convert private key to string for saving: %w",
err,
)
}
err = os.WriteFile(path, machineKeyStr, privateKeyFileMode)
if err != nil {
return nil, fmt.Errorf(
"failed to save private key to disk: %w",
err,
)
}
return &machineKey, nil
} else if err != nil {
return nil, fmt.Errorf("failed to read private key file: %w", err)
}
privateKeyEnsurePrefix := PrivateKeyEnsurePrefix(string(privateKey))
var machineKey key.MachinePrivate
if err = machineKey.UnmarshalText([]byte(privateKeyEnsurePrefix)); 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)
}
return &machineKey, nil
}

View file

@ -222,10 +222,11 @@ func getHeadscaleConfig() headscale.Config {
derpConfig := GetDERPConfig() derpConfig := GetDERPConfig()
return headscale.Config{ return headscale.Config{
ServerURL: viper.GetString("server_url"), ServerURL: viper.GetString("server_url"),
Addr: viper.GetString("listen_addr"), Addr: viper.GetString("listen_addr"),
IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")), IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")),
BaseDomain: baseDomain, PrivateKeyPath: absPath(viper.GetString("private_key_path")),
BaseDomain: baseDomain,
DERP: derpConfig, DERP: derpConfig,

View file

@ -46,6 +46,9 @@ const (
// This prefix is used in the control protocol, so cannot be // This prefix is used in the control protocol, so cannot be
// changed. // changed.
discoPublicHexPrefix = "discokey:" discoPublicHexPrefix = "discokey:"
// privateKey prefix.
privateHexPrefix = "privkey:"
) )
func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string { func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string {
@ -84,6 +87,14 @@ func DiscoPublicKeyEnsurePrefix(discoKey string) string {
return discoKey return discoKey
} }
func PrivateKeyEnsurePrefix(privateKey string) string {
if !strings.HasPrefix(privateKey, privateHexPrefix) {
return privateHexPrefix + privateKey
}
return privateKey
}
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors // Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
type Error string type Error string