Skip to content

Nitro Enclave AWS Setup

Configure IAM roles, KMS policies, and signing-certificate measurement conditions

The signing certificate signs the EIF file. Its SHA-384 fingerprint becomes PCR8, one of the KMS policy conditions that gates Shamir-share decryption.

The enclave image measurement is PCR0. AWS KMS exposes that same value as the condition key kms:RecipientAttestation:ImageSha384, so the Terraform variable is named enclave_pcr0_hashes but the rendered KMS policy uses ImageSha384.

  1. Generate a signing keypair (do this once, store the key securely):

    Terminal window
    openssl ecparam -name secp384r1 -genkey -noout -out signing_key.pem
    openssl req -new -x509 -key signing_key.pem -out signing_cert.pem \
    -days 3650 -subj "/CN=containment-chamber"
  2. Extract the PCR8 hash:

    Terminal window
    nitro-cli pcr --signing-certificate signing_cert.pem
    # → abc123def456... (your PCR8 hash)
  3. Build the signed EIF:

    Terminal window
    nitro-cli build-enclave \
    --docker-uri containment-chamber:latest-enclave \
    --signing-certificate signing_cert.pem \
    --private-key signing_key.pem \
    --output-file enclave.eif
  4. Extract the PCR0 / ImageSha384 hash (changes on every EIF rebuild):

    Terminal window
    nitro-cli describe-eif --eif-path enclave.eif | jq -r '.Measurements.PCR0'
    # → 789abc... (use this as enclave_pcr0_hashes / ImageSha384)

Use the multi-account enclave Terraform example when you want Shamir KMS keys split across separate AWS accounts:

Terminal window
cd terraform/examples/enclave
terraform init
terraform apply \
-var 'primary_profile=account-a' \
-var 'secondary_b_profile=account-b' \
-var 'secondary_c_profile=account-c' \
-var 'enclave_signing_cert_hash=<PCR8_HASH>' \
-var 'enclave_pcr0_hashes=["<PCR0_HASH>"]'

Use terraform/examples/single-account-enclave instead when you want the same Nitro attestation policy shape without cross-account custody separation.

The Terraform examples create two different classes of KMS keys:

  • Shamir share keys — wrap the chamber master key shares. kms:Decrypt requires PCR8 plus ImageSha384 when enclave_pcr0_hashes is set, and PCR3 when enclave_pcr3_hash is set.
  • DynamoDB table-encryption keys — encrypt the keystore, anti-slashing, and signer-state tables at rest. These are not attestation-gated because DynamoDB calls KMS at the service layer, outside the enclave.

For Shamir share keys:

  • kms:Decrypt — allowed to the signer role only with the configured attestation conditions.
  • kms:Encrypt — allowed to the signer role without an attestation condition. KMS does not support RecipientAttestation on Encrypt; the share plaintext originates inside the enclave and is sent to KMS over TLS.
# PCR8 — signing certificate hash (required)
# ImageSha384 — PCR0 / EIF image hash (required for production)
# PCR3 — parent IAM role hash (optional, extra lockdown)

The signer IAM role needs:

  • DynamoDB access for the keystore, anti-slashing, and signer-state tables.
  • KMS access for the DynamoDB table-encryption keys.
  • kms:Encrypt, kms:Decrypt, and kms:DescribeKey on the Shamir share keys, granted by the KMS key policies with attestation conditions on decrypt.

For production enclave deployments, prefer N attestation-gated KMS keys in N AWS accounts, administered by different people or teams. This keeps the enclave memory boundary from depending on one AWS account administrator: changing one key policy, compromising one account, or disabling one KMS key is not enough to recover or deny the chamber master key when the Shamir threshold is chosen correctly.

Use the same separation for the enclave KMS policies as for standard DynamoDB+KMS mode, but add Nitro attestation conditions to every Shamir KMS key. Each account should independently review and approve PCR changes during upgrades.

For EKS deployments, configure IRSA or EKS Pod Identity:

Terminal window
# IRSA
kubectl annotate serviceaccount containment-chamber \
eks.amazonaws.com/role-arn=arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME
# EKS Pod Identity
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--namespace default \
--service-account containment-chamber \
--role-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME