Envelope encryption
Every sensitive field that lands in MongoDB is sealed with AES-256-GCM. A fresh random data encryption key (DEK) is generated per field and wrapped by the active key encryption key (KEK). This page explains the model, what is encrypted, and what appears on disk.
01The envelope
Every encrypted field in the database carries this shape:
{
"wrapped_dek": "<opaque bytes>", // DEK sealed by the KEK
"nonce": "<12–16 bytes>", // GCM nonce used to encrypt the payload
"ciphertext": "<opaque bytes>", // AES-256-GCM(payload, DEK, nonce)
"kek_version": 2, // which KEK sealed this envelope
"provider": "env" // "env" or "pkcs11"
}
The DEK is never reused across fields — each record gets its own. The KEK, by contrast, is long-lived and versioned so it can be rotated without re-encrypting every record at once (see KEK rotation).
02What is encrypted
Every field you would rather not see in a mongodump is sealed:
| Collection | Encrypted field | What it protects |
|---|---|---|
certificate_private_keys | encrypted_key | The private key PEM that pairs with every issued certificate. |
acme_accounts | encrypted_private_key | The account-identity key used to sign ACME requests. |
msca_connections | encrypted_credentials | AD service-account username + password. |
dns_credentials | encrypted_api_key | Every DNS provider's API material. |
module_credentials | encrypted_value | SSH keys, kubeconfigs, F5/NetScaler/IIS passwords, Vault tokens, Huawei AK/SK. |
notification_channels | encrypted_config | SMTP passwords, Slack webhooks, Teams webhooks. |
| TOTP seeds, license blob | (inline) | 2FA shared secrets and license-key material. |
PEM certificate bodies (public) and the ACME registration URL are not encrypted — they are safe to expose.
03What is not envelope-encrypted
- User passwords — bcrypt hashed (not encrypted; irreversible by design).
- Refresh tokens — SHA-256 hashed (reuse detection requires a fixed digest).
- API keys — SHA-256 + pepper hashed. The pepper is env-only; see API keys.
- MongoDB-at-rest — orthogonal concern. If your policy requires at-rest volume encryption, configure it on the MongoDB side (LUKS, cloud-provider CMK, WiredTiger encryption). Envelope encryption protects against a logical leak (
mongodump, read-replica snapshot); volume encryption protects against physical theft. Run both.
04KEK providers
The wrap/unwrap of the DEK is delegated to a provider. CertAutoPilot ships with two:
env— Phase 1 default. Raw KEK bytes live inCERTAUTOPILOT_ENCRYPTION_ENV_KEK_V{N}environment variables. Simplest to operate; the backend process must be trusted with the key material.pkcs11— Phase 2. The KEK lives inside a PKCS#11 HSM. CertAutoPilot only holds opaque handles and the wrapped DEK blobs; the key material never leaves the HSM.
The choice is install-locked in MongoDB
(kek_install singleton). Swapping between env
and pkcs11 on an already-provisioned database is
rejected — see provider migration.
05The kek_version field
Every envelope carries the version of the KEK that sealed it. This is what makes rotation incremental:
- Before rotation, every envelope has
kek_version: 1. - Operator adds
V2key material and restarts each node so the backend loads both versions.V1is still the keystore's active version — no rotate has happened yet. - Operator runs
certautopilot kek rotate --from-version=1 --to-version=2. The worker reads every envelope, unwraps withV1, re-wraps explicitly with V2 (not with the process's own current — this keeps the outerdoc.kek_versionconsistent with the inner envelopekvtag at all times), writes back atomically per record, and flipsV2active in the keystore viaSwapActive. - Every live node's heartbeat tick (~30 s) detects the SwapActive and hot-reloads
V2as the current KEK — no rolling restart required. New writes from then on seal withV2.V1stays loaded for unwrap of any older envelopes (none, if the rotation ran to completion) untilkek remove+ env-var cleanup clears it.
See KEK rotation for the full runbook.
06The provider field
Tagging each envelope with the provider that sealed it is a safety
net: the backend refuses to unwrap a provider: env
envelope when the active provider is pkcs11, and vice
versa, preventing silent data corruption if someone edits the
kek_install record by hand.
07Guarantees and limits
- Confidentiality + integrity via AES-256-GCM. Tampering with the ciphertext yields an authenticated-decryption failure.
- Per-field DEK. A leak of one envelope does not weaken any other.
- In-memory exposure: the backend holds the KEK (for
env) or the HSM handle (forpkcs11) in memory; a running-process memory dump reveals it. This is inherent to online cryptosystems — use OS-level memory protection (disable core dumps for the unit, enableProtectKernelTunables, and so on). - Key custody is on you. CertAutoPilot does not have a "recover my KEK" flow. Lose the KEK → envelopes become unreadable. Back up secrets (standalone
secrets.envor the K8s Secret) alongside MongoDB.