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.
Comparison
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 |
env 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.
pkcs11 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.
The 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.
How 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.
Rotation 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.