feat(master): postgres store with migrations, master identity and agents
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user