Credential Encryption
Accurids encrypts sensitive credential fields (endpoint passwords, API keys) at rest using AES-256-GCM. This protects stored secrets even if an attacker gains read access to the database.
Overview
When a master key is configured, all credential fields are encrypted before being written to the database. The ciphertext is stored as a prefixed string (v1:<keyId>:<iv>:<ct>) in the same varchar column — no schema changes are required beyond the initial migration.
When no master key is configured, credential fields are stored with a plain: prefix (unencrypted). A startup warning is emitted in this case.
Configuration
| Variable | Default | Description |
|---|---|---|
accurids.security.encryption.master-key |
(none) | Primary encryption key. Any non-blank string (see Generating a key). |
accurids.security.encryption.old-keys |
(empty) | List of previous master keys retained for decryption during key rotation. |
accurids.security.encryption.migrate-on-startup |
true |
Whether to automatically re-encrypt credentials with the current key on startup. |
accurids.security.encryption.allow-downgrade |
false |
Whether startup migration may convert encrypted values to plaintext when no master key is set. |
Generating a key
The master key can be any non-blank string — UUID, passphrase, hex, or base64. Internally, the key is run through HKDF-SHA256 (RFC 5869) to derive the actual 256-bit AES key, so no specific format or length is required.
For production use, generate a high-entropy value (at least 128 bits), e.g. by:
# Option 1: base64-encoded random bytes
openssl rand -base64 32
# Option 2: UUID
uuidgen
Important: Use the same master key on every node of a cluster. A mismatch will cause decryption failures.
Key rotation
To rotate the encryption key:
- Add the current key to
old-keys— this ensures existing ciphertexts remain readable during the transition. - Set the new key as
master-key— new writes will use the new key immediately. - Deploy to all nodes — the automatic startup migration re-encrypts all credentials with the new key (only one node in the cluster performs the migration).
- Remove the old key — once all nodes are running with the new key and the migration has completed, the old key can be removed from
old-keys.
Example: rotating from key A to key B
Before rotation:
accurids.security.encryption.master-key: "key-A"
After rotation:
accurids.security.encryption.master-key: "key-B"
accurids.security.encryption.old-keys[0]: "key-A"
After migration completes (check logs for "Encryption key migration complete"):
accurids.security.encryption.master-key: "key-B"
Multiple old keys can be specified using indexed notation:
accurids.security.encryption.old-keys[0]: "key-A"
accurids.security.encryption.old-keys[1]: "key-Z"
Automatic startup migration
When migrate-on-startup is true (the default) and a master key is configured, Accurids checks all credential columns at application start. Any value not yet encrypted with the current key (including plain:-prefixed values and values encrypted with an old key) is decrypted and re-encrypted with the current key.
The migration:
- Runs only once per cluster (coordinated via a cluster lock) — safe for multi-node deployments.
- Is idempotent — rows already on the current key are skipped.
- Handles individual row failures gracefully — a corrupt value is logged at WARN level but does not abort the migration of other rows.
Disabling migration
Set migrate-on-startup to false if you want to control when migration happens (e.g. in a maintenance window):
accurids.security.encryption.migrate-on-startup: false
Deploying encryption for the first time
If you are upgrading from a version of Accurids that did not have credential encryption, or if encryption was not enabled before, Accurids migrates all unencrypted credentials to ciphertext on the next boot.
No manual data migration is required.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
| Startup warning: "No master key configured" | master-key is not set or blank |
Set the environment variable / config property |
EncryptionException: key ID not recognised |
The credential was encrypted with a key that is not in master-key or old-keys |
Add the missing key to old-keys |
EncryptionException: no handler recognises the prefix |
A bare string without plain: or v1: prefix |
Re-run the Flyway migration or manually prefix with plain: |
| Migration log shows failures | Individual row has corrupt data | Inspect the logged row ID and fix or re-set the credential manually |