diff options
Diffstat (limited to '')
| -rw-r--r-- | internal/controlplane/rotation.go | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/internal/controlplane/rotation.go b/internal/controlplane/rotation.go new file mode 100644 index 0000000..1fc8084 --- /dev/null +++ b/internal/controlplane/rotation.go @@ -0,0 +1,69 @@ +package controlplane + +import ( + "context" + "fmt" +) + +type encryptedColumn struct{ table, key, column string } + +// RotateCredentials re-encrypts every control-plane secret with the primary +// key in the configured keyring. Run all gateway instances with both new and +// previous keys before invoking this operation. +func (s *Store) RotateCredentials(ctx context.Context) (int, error) { + tx, err := s.db.Begin(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback(ctx) + columns := []encryptedColumn{ + {"providers", "id", "api_key_ciphertext"}, + {"console_mail_outbox", "id", "body_ciphertext"}, + {"console_totp_credentials", "user_id", "secret_ciphertext"}, + {"console_passkeys", "id", "credential_ciphertext"}, + {"console_webauthn_challenges", "id", "session_ciphertext"}, + } + total := 0 + for _, item := range columns { + rows, queryErr := tx.Query(ctx, fmt.Sprintf(`SELECT %s::text,%s FROM %s`, item.key, item.column, item.table)) + if queryErr != nil { + return total, queryErr + } + type record struct { + id string + ciphertext []byte + } + records := []record{} + for rows.Next() { + var value record + if err := rows.Scan(&value.id, &value.ciphertext); err != nil { + rows.Close() + return total, err + } + records = append(records, value) + } + if err := rows.Err(); err != nil { + rows.Close() + return total, err + } + rows.Close() + for _, value := range records { + plaintext, err := s.cipher.Decrypt(value.ciphertext) + if err != nil { + return total, fmt.Errorf("decrypt %s %s: %w", item.table, value.id, err) + } + ciphertext, err := s.cipher.Encrypt(plaintext) + if err != nil { + return total, err + } + if _, err := tx.Exec(ctx, fmt.Sprintf(`UPDATE %s SET %s=$2 WHERE %s=$1`, item.table, item.column, item.key), value.id, ciphertext); err != nil { + return total, err + } + total++ + } + } + if err := tx.Commit(ctx); err != nil { + return total, err + } + return total, nil +} |
