91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
package settings
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
type encryptedBlob struct {
|
|
Nonce string `json:"nonce"`
|
|
Ciphertext string `json:"ciphertext"`
|
|
}
|
|
|
|
// KeyFromEnv reads and decodes CLUSTERCANVAS_CONFIG_KEY (base64, 16/24/32 bytes).
|
|
func KeyFromEnv() ([]byte, error) {
|
|
rawKey := os.Getenv(ConfigKeyEnvVar)
|
|
if rawKey == "" {
|
|
return nil, fmt.Errorf("%s is not set", ConfigKeyEnvVar)
|
|
}
|
|
return decodeKey(rawKey)
|
|
}
|
|
|
|
// LoadSecrets decrypts secrets.enc from dir using key.
|
|
func LoadSecrets(dir string, key []byte) (Secrets, error) {
|
|
path := filepath.Join(dir, SecretsFileName)
|
|
payload, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return Secrets{}, fmt.Errorf("read secrets: %w", err)
|
|
}
|
|
|
|
var blob encryptedBlob
|
|
if err := json.Unmarshal(payload, &blob); err != nil {
|
|
return Secrets{}, fmt.Errorf("parse secrets envelope: %w", err)
|
|
}
|
|
|
|
nonce, err := base64.StdEncoding.DecodeString(blob.Nonce)
|
|
if err != nil {
|
|
return Secrets{}, fmt.Errorf("decode nonce: %w", err)
|
|
}
|
|
ciphertext, err := base64.StdEncoding.DecodeString(blob.Ciphertext)
|
|
if err != nil {
|
|
return Secrets{}, fmt.Errorf("decode ciphertext: %w", err)
|
|
}
|
|
|
|
plaintext, err := open(nonce, ciphertext, key)
|
|
if err != nil {
|
|
return Secrets{}, err
|
|
}
|
|
|
|
var secrets Secrets
|
|
if err := json.Unmarshal(plaintext, &secrets); err != nil {
|
|
return Secrets{}, fmt.Errorf("parse secrets: %w", err)
|
|
}
|
|
return secrets, nil
|
|
}
|
|
|
|
// SaveSecrets encrypts secrets and writes secrets.enc to dir with mode 0600.
|
|
func SaveSecrets(dir string, secrets Secrets, key []byte) error {
|
|
if err := ensureConfigDir(dir); err != nil {
|
|
return err
|
|
}
|
|
|
|
plaintext, err := json.Marshal(secrets)
|
|
if err != nil {
|
|
return fmt.Errorf("encode secrets: %w", err)
|
|
}
|
|
|
|
nonce, ciphertext, err := seal(plaintext, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
blob := encryptedBlob{
|
|
Nonce: base64.StdEncoding.EncodeToString(nonce),
|
|
Ciphertext: base64.StdEncoding.EncodeToString(ciphertext),
|
|
}
|
|
payload, err := json.MarshalIndent(blob, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("encode secrets envelope: %w", err)
|
|
}
|
|
payload = append(payload, '\n')
|
|
|
|
path := filepath.Join(dir, SecretsFileName)
|
|
if err := os.WriteFile(path, payload, 0o600); err != nil {
|
|
return fmt.Errorf("write secrets: %w", err)
|
|
}
|
|
return nil
|
|
}
|