- Rename Go module switchboard-core → armature (155+ files) - Rename Docker image → gobha/armature - Rename K8s resources, secrets, deployments - Rename Prometheus metrics switchboard_* → armature_* - Rename env vars SWITCHBOARD_ADMIN_* → ARMATURE_ADMIN_* - Rename DB names switchboard_core* → armature* - Update all frontend branding, notification templates, docs - Update CI scripts, e2e tests, Keycloak realm, nginx conf - Rename scripts/switchboard-ca.sh → scripts/armature-ca.sh - Rename k8s/switchboard.yaml → k8s/armature.yaml - Rename chart alerting/dashboard files - Fix: DockerHub push uses env: binding for secret injection - Helm chart updated (name, labels, template functions, dashboard, alerting) - Replace favicon/icon assets with Armature brand No functional changes. Pure mechanical rename + CI fix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
116 lines
3.0 KiB
Go
116 lines
3.0 KiB
Go
package crypto
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
)
|
|
|
|
// Rekey re-encrypts all global/team API keys from oldKey to newKey.
|
|
// Personal BYOK keys are unaffected (they're keyed to the user's UEK,
|
|
// not the environment-derived key).
|
|
//
|
|
// Runs in a single transaction — all-or-nothing. On failure, no keys
|
|
// are modified.
|
|
//
|
|
// Usage:
|
|
//
|
|
// armature vault rekey
|
|
// ENCRYPTION_KEY = current/old key
|
|
// NEW_ENCRYPTION_KEY = new key to re-encrypt with
|
|
func Rekey(db *sql.DB, oldEnvKey, newEnvKey string) error {
|
|
if oldEnvKey == "" {
|
|
return fmt.Errorf("ENCRYPTION_KEY (current key) is required")
|
|
}
|
|
if newEnvKey == "" {
|
|
return fmt.Errorf("NEW_ENCRYPTION_KEY is required")
|
|
}
|
|
if oldEnvKey == newEnvKey {
|
|
return fmt.Errorf("ENCRYPTION_KEY and NEW_ENCRYPTION_KEY are identical — nothing to do")
|
|
}
|
|
|
|
oldKey, err := DeriveKeyFromEnv(oldEnvKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to derive old key: %w", err)
|
|
}
|
|
|
|
newKey, err := DeriveKeyFromEnv(newEnvKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to derive new key: %w", err)
|
|
}
|
|
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
|
}
|
|
defer tx.Rollback() // no-op after commit
|
|
|
|
// Find all global/team keys that are encrypted
|
|
rows, err := tx.Query(`
|
|
SELECT id, api_key_enc, key_nonce
|
|
FROM provider_configs
|
|
WHERE key_scope IN ('global', 'team')
|
|
AND api_key_enc IS NOT NULL
|
|
`)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query encrypted keys: %w", err)
|
|
}
|
|
|
|
type rekeyRow struct {
|
|
id string
|
|
ciphertext []byte
|
|
nonce []byte
|
|
}
|
|
|
|
var toRekey []rekeyRow
|
|
for rows.Next() {
|
|
var r rekeyRow
|
|
if err := rows.Scan(&r.id, &r.ciphertext, &r.nonce); err != nil {
|
|
rows.Close()
|
|
return fmt.Errorf("failed to scan row: %w", err)
|
|
}
|
|
toRekey = append(toRekey, r)
|
|
}
|
|
rows.Close()
|
|
if err := rows.Err(); err != nil {
|
|
return fmt.Errorf("row iteration error: %w", err)
|
|
}
|
|
|
|
if len(toRekey) == 0 {
|
|
log.Println("No global/team encrypted keys found — nothing to rekey.")
|
|
return nil
|
|
}
|
|
|
|
log.Printf("Rekeying %d provider config(s)...", len(toRekey))
|
|
|
|
// Decrypt with old key, re-encrypt with new key, update in place
|
|
for _, r := range toRekey {
|
|
plaintext, err := Decrypt(r.ciphertext, r.nonce, oldKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decrypt config %s with old key: %w (is ENCRYPTION_KEY correct?)", r.id, err)
|
|
}
|
|
|
|
newCiphertext, newNonce, err := Encrypt(plaintext, newKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to re-encrypt config %s: %w", r.id, err)
|
|
}
|
|
|
|
_, err = tx.Exec(`
|
|
UPDATE provider_configs
|
|
SET api_key_enc = $1, key_nonce = $2
|
|
WHERE id = $3
|
|
`, newCiphertext, newNonce, r.id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update config %s: %w", r.id, err)
|
|
}
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return fmt.Errorf("failed to commit rekey transaction: %w", err)
|
|
}
|
|
|
|
log.Printf("✅ Successfully rekeyed %d provider config(s).", len(toRekey))
|
|
log.Println("Update ENCRYPTION_KEY to the new value and restart the server.")
|
|
return nil
|
|
}
|