KEK providers
A KEK provider wraps and unwraps data-encryption keys. CertAutoPilot
ships two: env (software, KEK bytes in environment
variables) and pkcs11 (hardware, KEK lives in a PKCS#11
HSM). The choice is made at install time and is immutable thereafter.
01Comparison
env (software) | pkcs11 (HSM) | |
|---|---|---|
| Where the KEK lives | Process memory + secrets.env / K8s Secret | Inside the HSM; never leaves |
| Operational complexity | Low | Moderate — HSM client install, PIN management, vendor-specific flags |
| Rotation story | Edit env vars, rolling restart, kek rotate | Generate a new HSM key, rolling restart, kek rotate |
| Compliance posture | Good (AES-256-GCM, versioned) | Stronger — FIPS 140-2 / 140-3 Level 2 / 3 depending on HSM |
| Failure modes if the KEK is lost | Data unrecoverable | Data unrecoverable (HSM backup policy is on you) |
| Typical use | Self-hosted single-tenant, dev, pre-prod | Regulated enterprise, multi-tenant, SOC 2 / PCI-DSS shops |
02env provider
The simplest provider. Raw 32-byte KEK material is supplied via
CERTAUTOPILOT_ENCRYPTION_ENV_KEK_V{N} (one
env var per version). The backend loads every present version at
startup and reads the kek_versions collection to
decide which one is active for new writes — rotation flips that
pointer via certautopilot kek rotate, no per-host
env var change is involved.
Storage:
- Standalone —
/etc/certautopilot/secrets.env, mode0600, loaded by systemd viaEnvironmentFile=. - Kubernetes — a Secret consumed via
envFrom.secretRef, rendered either inline by the Helm chart or from an external Secret (SealedSecrets, External Secrets, Vault injector).
Key material generation:
openssl rand -hex 32
Always 64 hex chars = 32 bytes. Shorter values are rejected on startup.
The KEK bytes live in the backend process's address space while it runs. A memory-dump attack (a root on the host, a core dump, a debugger) exposes them. Disable core dumps on the unit (LimitCORE=0), enable memory-write-exec protections (MemoryDenyWriteExecute=true — already set by the standalone unit), and keep host access tight.
03pkcs11 provider
The HSM provider never lets the KEK enter the backend's address
space. CertAutoPilot dlopens the vendor's PKCS#11 library, logs
into the token with the operator PIN, and invokes
CKM_AES_GCM on key handles. The
wrapped DEK and the nonce come back to the backend; the KEK stays
in the HSM.
The provider stores only keystore metadata in MongoDB — the
HSM key label / handle + version, never raw material. A mismatch
between the config-declared provider and the kek_install
record aborts startup.
Tested HSMs: SoftHSM2 (dev/CI), AWS CloudHSM, Thales Luna Network HSM. Any FIPS-approved HSM supporting CKM_AES_GCM should work — we run a capability probe before committing.
04The install lock
At install time, the chosen provider is written into a singleton
MongoDB document kek_install. Every subsequent startup
reads this document and refuses to run if the config-declared
provider disagrees. This prevents a misconfigured restart from
silently switching storage backends — a mistake that would tag
fresh envelopes with a provider tag that cannot unwrap the old
ones.
Switching providers on an already-provisioned system requires a fresh install + data re-import. See Provider migration.
05How to choose
Pick env if any of these hold:
- You do not have an HSM.
- You are not in a regulated vertical that requires one.
- You want the smallest operational surface area.
Pick pkcs11 if any of these hold:
- SOC 2 / PCI-DSS / HIPAA auditors will ask where the KEK lives.
- Your security model treats backend memory dumps as a realistic threat.
- You have an HSM already (CloudHSM, Luna, Fortanix) and want to consolidate root-of-trust on it.
You can start with env and move to pkcs11 later — but because the switch requires a fresh install, decide early where possible.
06Rotation within the same provider
Both providers support seamless rotation via
certautopilot kek rotate. The flow is identical:
- Add a new version (new env var, or
kek pkcs11-init --version=N+1). - Roll the fleet so every process loads both versions.
kek verify --target=N+1confirms readiness.kek rotate --from-version=N --to-version=N+1re-wraps every envelope.--from-versiondefaults to the keystore's active row but passing it explicitly keeps the intended transition unambiguous on the audit record.- (Optional)
kek remove --version=Nretires the old version.
See KEK rotation.