mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-29 18:33:05 +00:00
Add back privatekey, but automatically generate it if it does not exist
This commit is contained in:
parent
32006f3a20
commit
34f4109fbd
4 changed files with 73 additions and 16 deletions
2
api.go
2
api.go
|
@ -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
67
app.go
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
11
utils.go
11
utils.go
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue