What Nitro Enclaves protect, what remains outside the enclave, and how KMS attestation fits in
Containment Chamber can run inside an AWS Nitro Enclave — an isolated compute environment with hardware-enforced memory encryption. When running in enclave mode, validator BLS private keys only exist decrypted inside the enclave’s protected memory. Even a fully compromised EC2 instance cannot access them.
Nitro mode is selected with tee.platform: nitro. It also requires tls.mode: ratls and a persistent signer_state backend. The enclave exposes the signing API over vsock, and the parent EC2 instance only forwards bytes between TCP clients, AWS services, and the enclave.
Security Model
Section titled “Security Model”The enclave security model rests on three pillars:
1. Memory isolation — The Nitro hypervisor encrypts enclave memory with a key that never leaves the hardware. The parent EC2 instance, AWS operators, and anyone with physical access to the machine cannot read enclave memory.
2. Attestation — The Nitro Security Module (NSM) produces signed attestation documents proving: “This enclave is running this exact binary, with these PCR measurements.” KMS validates these documents before returning a CiphertextForRecipient envelope encrypted to an enclave-generated recipient key.
3. Reproducible builds — Anyone can rebuild the enclave image from source and verify the PCR0 / ImageSha384 measurement matches the published release. This makes the attestation independently verifiable.
Architecture
Section titled “Architecture”direction: down
ec2: "EC2 Instance (untrusted)" { style.fill: "#F6F8FA" style.stroke: "#6B7280" style.font-color: "#170206"
pod: "Pod Container" { style.fill: "#FEEC8C" style.stroke: "#D35F0A" style.font-color: "#170206" nitro_cli: "nitro-cli\n(manages enclave lifecycle)" ingress: "enclave-proxy fleet\nTCP → vsock\nsigning: PROXY v2\nhealth/metrics: raw" egress: "vsock-proxy fleet\nKMS · DynamoDB · PostgreSQL · other endpoints" config: "vsock-config\nbootstrap YAML :7001" creds: "vsock-creds\nPod Identity credentials :7002" logs: "vsock-stdout\nlogs :7000" }
enclave: "Nitro Enclave (trusted)" { style.fill: "#CAF2E6" style.stroke: "#13A477" style.font-color: "#170206" signer: "containment-chamber\n(--features nitro)" nsm: "NSM\nentropy · attestation · PCRs" ratls: "RA-TLS listener :9443\nX.509 + NSM document" health: "HTTP health :9000\nmetrics :3000" kms: "AttestedKmsClient\nper-decrypt recipient\nKMS RecipientAttestation" master_key: "MasterKeyHolder\nreconstruct master key" dynamo: "DynamoDbKeyStore → load encrypted BLS keys" pg_antislash: "PostgreSQL anti-slashing" axum: "Axum on vsock\nsigning API"
signer -> nsm nsm -> ratls: "bind SPKI hash to PCRs" signer -> ratls signer -> health signer -> kms signer -> master_key signer -> dynamo signer -> pg_antislash signer -> axum }
pod.ingress.style.fill: "#FFF6EF" pod.ingress.style.stroke: "#D35F0A" pod.ingress.style.font-color: "#170206" pod.nitro_cli.style.fill: "#FEEC8C" pod.nitro_cli.style.stroke: "#D35F0A" pod.nitro_cli.style.font-color: "#170206" pod.egress.style.fill: "#FFF6EF" pod.egress.style.stroke: "#D35F0A" pod.egress.style.font-color: "#170206" pod.config.style.fill: "#FEEC8C" pod.config.style.stroke: "#D35F0A" pod.config.style.font-color: "#170206" pod.creds.style.fill: "#FEEC8C" pod.creds.style.stroke: "#D35F0A" pod.creds.style.font-color: "#170206" pod.logs.style.fill: "#FFF6EF" pod.logs.style.stroke: "#D35F0A" pod.logs.style.font-color: "#170206" enclave.signer.style.fill: "#CAF2E6" enclave.signer.style.stroke: "#13A477" enclave.signer.style.font-color: "#170206" enclave.nsm.style.fill: "#FEEC8C" enclave.nsm.style.stroke: "#D35F0A" enclave.nsm.style.font-color: "#170206" enclave.ratls.style.fill: "#CAF2E6" enclave.ratls.style.stroke: "#13A477" enclave.ratls.style.font-color: "#170206" enclave.health.style.fill: "#CAF2E6" enclave.health.style.stroke: "#13A477" enclave.health.style.font-color: "#170206" enclave.kms.style.fill: "#FEEC8C" enclave.kms.style.stroke: "#D35F0A" enclave.kms.style.font-color: "#170206" enclave.master_key.style.fill: "#FEEC8C" enclave.master_key.style.stroke: "#D35F0A" enclave.master_key.style.font-color: "#170206" enclave.dynamo.style.fill: "#FFF6EF" enclave.dynamo.style.stroke: "#D35F0A" enclave.dynamo.style.font-color: "#170206" enclave.pg_antislash.style.fill: "#FFF6EF" enclave.pg_antislash.style.stroke: "#D35F0A" enclave.pg_antislash.style.font-color: "#170206" enclave.axum.style.fill: "#CAF2E6" enclave.axum.style.stroke: "#13A477" enclave.axum.style.font-color: "#170206"
pod.ingress -> enclave.ratls: "RA-TLS over vsock\n+ PROXY v2" {style.stroke-dash: 3} pod.ingress -> enclave.health: "health/metrics\nraw vsock" {style.stroke-dash: 3} pod.config -> enclave.signer: "bootstrap config" {style.stroke-dash: 3} enclave.kms -> pod.egress: "vsock" {style.stroke-dash: 3} enclave.dynamo -> pod.egress: "vsock" {style.stroke-dash: 3} enclave.pg_antislash -> pod.egress: "vsock" {style.stroke-dash: 3} enclave.signer -> pod.logs: "tracing events" {style.stroke-dash: 3} enclave.signer -> pod.creds: "ECS credentials provider" {style.stroke-dash: 3}}The enclave uses a parent-side vsock-proxy per configured outbound endpoint: KMS, DynamoDB, optionally PostgreSQL for anti-slashing, and any other hostname declared under tee.nitro.egress.endpoints. For each endpoint, the enclave maps the hostname to a unique loopback address and forwards that local TCP connection over vsock to the parent. End-to-end TLS is preserved — the parent moves bytes but does not terminate the connection.
AWS credentials use a separate path. Inside the enclave, the AWS SDK talks to a local ECS-compatible credentials endpoint on 127.0.0.1:8090; that endpoint dials the parent vsock-creds bridge on port 7002, and the parent fetches credentials from the EKS Pod Identity Agent using a fresh service-account token.
RA-TLS is mandatory in Nitro mode. The enclave generates an ephemeral TLS key, asks the NSM for an attestation document bound to the key’s SPKI, and embeds that document in the X.509 certificate. Clients verify the Nitro document against the pinned AWS Nitro Root CA, confirm the TLS key binding, and pin the measured PCR set (PCR0, PCR1, PCR2, PCR3, PCR8) before sending signing or operator traffic.
What the Enclave Protects
Section titled “What the Enclave Protects”| Threat | Without Enclave | With Enclave |
|---|---|---|
| Compromised EC2 instance | ❌ Attacker reads key material from memory | ✅ Enclave memory is hardware-encrypted |
| Compromised KMS credentials | ❌ Attacker calls KMS directly | ✅ KMS decrypt requires valid NSM RecipientAttestation; plaintext is returned only inside an enclave-recipient envelope |
| Malicious enclave binary | ❌ No verification | ✅ PCR0 hash must match expected value |
| Compromised signing certificate | ❌ Attacker signs malicious binary | ✅ PCR0 still blocks wrong binary |
The Unverifiable Link
Section titled “The Unverifiable Link”PCR Values
Section titled “PCR Values”Nitro Enclaves use Platform Configuration Registers (PCRs) to record measurements:
| PCR | What It Measures | Changes When |
|---|---|---|
| PCR0 | Hash of the entire EIF binary | Every rebuild |
| PCR1 | Hash of the Linux kernel and bootstrap ramdisk | Enclave kernel/OS changes |
| PCR2 | Hash of the application (user-space) in the EIF | Application rebuild |
| PCR8 | Hash of the signing certificate | Signing key rotation |
| PCR3 | Hash of the parent IAM role ARN | IAM role changes |
Production deployments should enforce both PCR8 and PCR0 / ImageSha384. PCR8 alone is insufficient — if the signing key is compromised, an attacker can sign a malicious binary that passes PCR8. PCR0 / ImageSha384 prevents this because it pins to the exact enclave image measurement.
PCR1 and PCR2 are also recorded in every attestation document. Most deployments enforce ImageSha384 or PCR0 plus PCR8 (and optionally PCR3 for IAM-role binding), but PCR1 and PCR2 can be added as KMS policy conditions for finer-grained measurement pinning.
With reproducible builds, each key holder can independently verify PCR0 by rebuilding from source.
KMS Custody
Section titled “KMS Custody”Nitro Enclaves isolate decrypted key material from the parent instance, but KMS policy control is still part of the trust model. For production, use multiple Shamir KMS keys across separate AWS accounts, with separate administrators. Each account should enforce the enclave attestation policy on its own key and independently approve PCR updates during upgrades.
This prevents one AWS account admin from unilaterally changing every KMS policy or disabling every share. Choose the threshold to match your availability and governance needs, such as 2-of-3 for simple custody separation or 3-of-5 for a higher collusion bar.
The custody parameters themselves — which KMS keys, the threshold, and the root-token recipients — are baked into the measured binary from custody/ceremony.ron at build time (not runtime config), so they are covered by PCR0. A Nitro binary built without a ceremony refuses to boot rather than serve custody-less. See Building the Custody EIF.
Next Steps
Section titled “Next Steps”- AWS Setup — KMS key policies, signing certificates, IAM
- Deployment — Helm chart, K8s prerequisites
- Operations — PCR rotation, troubleshooting

