43 lines
1.3 KiB
Go
43 lines
1.3 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"crypto/x509"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// LoadOrCreateIdentity returns the master's private key, creating and
|
|
// storing one with generate on first start. Keeping it in Postgres means it
|
|
// survives container recreation and is covered by database backups; losing
|
|
// it would force every agent to be reconfigured with a new MASTER_KEY.
|
|
func (s *Store) LoadOrCreateIdentity(ctx context.Context, generate func() (ed25519.PrivateKey, error)) (ed25519.PrivateKey, error) {
|
|
fresh, err := generate()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
der, err := x509.MarshalPKCS8PrivateKey(fresh)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal master key: %w", err)
|
|
}
|
|
if _, err := s.pool.Exec(ctx,
|
|
`INSERT INTO master_identity (id, private_key) VALUES (1, $1) ON CONFLICT (id) DO NOTHING`, der,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("store master key: %w", err)
|
|
}
|
|
var stored []byte
|
|
if err := s.pool.QueryRow(ctx, `SELECT private_key FROM master_identity WHERE id = 1`).Scan(&stored); err != nil {
|
|
return nil, fmt.Errorf("load master key: %w", err)
|
|
}
|
|
parsed, err := x509.ParsePKCS8PrivateKey(stored)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse master key: %w", err)
|
|
}
|
|
key, ok := parsed.(ed25519.PrivateKey)
|
|
if !ok {
|
|
return nil, errors.New("stored master key is not ed25519")
|
|
}
|
|
return key, nil
|
|
}
|