On February 8 2026 Amazon Web Services announced native support for Nitro Enclaves inside CodeBuild. This marks the first time a managed CI/CD service can launch hardware‑isolated enclaves on‑the‑fly, giving teams a practical way to protect API keys, TLS certificates, and other high‑value secrets from the build environment itself. In this deep‑dive we’ll walk through the architecture, required IAM policies, and a concrete example that encrypts a Docker image’s ARG values inside an enclave before the image is pushed to Amazon ECR.

Why Nitro Enclaves for CI/CD?

Traditional CodeBuild projects run in plain Amazon Linux containers that share the underlying EC2 host’s memory space. Even with IAM roles and Parameter Store, a compromised build script can read environment variables or temporary files and exfiltrate secrets. Nitro Enclaves provide memory‑isolated, attested execution environments that:

  • Do not have any persistent storage or network interfaces.
  • Communicate with the parent build container only through vsock channels.
  • Expose a cryptographic attestation document that can be verified by downstream services.

By off‑loading secret handling to an enclave, the build container can remain stateless and untrusted, dramatically reducing the attack surface.

High‑Level Architecture

Nitro Enclave inside CodeBuild architecture diagram
Figure 1 – Enclave‑backed secret handling in a CodeBuild job.

The flow is simple:

  1. The CodeBuild runtime launches an enclave‑launcher binary.
  2. The launcher creates a Nitro Enclave with a minimal Ubuntu 22.04 image that contains the aws-nitro-enclaves-cli tools.
  3. The parent container sends a request over vsock to the enclave, asking it to decrypt a ciphertext stored in AWS KMS.
  4. The enclave returns the plaintext secret via the same channel; the parent process uses it for the build (e.g., Docker build‑arg, npm token, etc.).
  5. All secret material is discarded when the enclave terminates at the end of the job.

Preparing the Enclave Image

AWS supplies a base image called aws-nitro-enclaves-base, but most teams prefer to bake their own minimal image that contains only the required binaries (KMS SDK, OpenSSL, and any language‑specific tooling). Below is a Dockerfile that builds an enclave image suitable for Java‑based builds:

FROM public.ecr.aws/nitro-enclaves/aws-nitro-enclaves-base:latest

# Install Java runtime & AWS SDK for KMS
RUN yum install -y java-17-openjdk \
    && curl -L https://repo.maven.apache.org/maven2/software/amazon/awssdk/kms/2.24.0/kms-2.24.0.jar -o /opt/kms.jar

# Add a small Go helper that reads from vsock and calls KMS Decrypt
COPY enclave-helper /opt/enclave-helper
RUN chmod +x /opt/enclave-helper

ENTRYPOINT ["/opt/enclave-helper"]

Build and push the enclave image to an ECR repository that the build project’s execution role can read:

aws ecr create-repository --repository-name ci-enclave-image
docker build -t ci-enclave-image .
docker tag ci-enclave-image:latest <account-id>.dkr.ecr.<region>.amazonaws.com/ci-enclave-image:latest
aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/ci-enclave-image:latest

Configuring the CodeBuild Project

The new CodeBuild feature is enabled by adding an enclaveConfiguration block to the buildspec.yml or via the console UI. Example buildspec.yml:

version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto17
    commands:
      - echo Installing enclave launcher...
      - yum install -y aws-nitro-enclaves-cli
  pre_build:
    commands:
      - echo Starting Nitro Enclave…
      - enclave-cli run \
          --image <account-id>.dkr.ecr.<region>.amazonaws.com/ci-enclave-image:latest \
          --cpu-count 2 \
          --memory 512 \
          --vsock-port 5000 \
          --enclave-id $ENCLAVE_ID
      - echo Requesting secret decryption…
      - |
        cat <<EOF > decrypt-request.json
        {
          "kmsKeyId": "arn:aws:kms:<region>:<account-id>:key/<key-id>",
          "ciphertextBlob": "$(aws ssm get-parameter --name /ci/build-token --with-decryption --query Parameter.Value --output text)"
        }
        EOF
      - ENCLAVE_SECRET=$(enclave-cli vsock-send --enclave-id $ENCLAVE_ID \
          --port 5000 --payload file://decrypt-request.json)
  build:
    commands:
      - echo Building Docker image…
      - docker build --build-arg BUILD_TOKEN=$ENCLAVE_SECRET -t my-app:latest .
  post_build:
    commands:
      - echo Pushing image to ECR…
      - $(aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com)
      - docker tag my-app:latest <account-id>.dkr.ecr.<region>.amazonaws.com/my-app:latest
      - docker push <account-id>.dkr.ecr.<region>.amazonaws.com/my-app:latest
artifacts:
  files: '**/*'

Note the use of vsock-send to exchange JSON with the enclave. The enclave binary validates the request, calls KMS Decrypt, and returns the plaintext. Because the secret never appears in the build container’s environment, a compromised step cannot leak it.

IAM and KMS Permissions

Two policies are required:

  • CodeBuild execution role – must be able to sts:AssumeRole on the enclave‑launcher service and read the ECR image:
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ],
      "Resource": "*"
    }
    
  • KMS key policy – grants the enclave’s isolated principal (identified by the enclave’s attestation document) permission to Decrypt. The policy uses the aws:PrincipalTag/Enclave condition, which is automatically injected by the enclave runtime:
    {
      "Sid": "AllowEnclaveDecrypt",
      "Effect": "Allow",
      "Principal": {"AWS": "*"},
      "Action": "kms:Decrypt",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalTag/Enclave": "true"
        }
      }
    }
    

Testing and Attestation Verification

After the first successful run, you should verify the enclave’s attestation document to ensure it originated from the expected AMI and code hash. The CLI provides a helper:

enclave-cli attest --enclave-id $ENCLAVE_ID --output json | jq .verificationResult

A true result confirms the enclave has not been tampered with. Integrate this check into a pre‑build step; if verification fails, abort the pipeline to avoid leaking secrets.

Best Practices and Gotchas

  • Keep enclave images minimal. Smaller attack surface and faster start‑up.
  • Never write decrypted secrets to disk. Pass them directly to the next process via environment variables or command‑line arguments that are cleared afterward.
  • Limit enclave lifetime. The enclave terminates automatically when the CodeBuild job ends, but you can also invoke enclave-cli stop early if an error occurs.
  • Monitor vsock traffic. CloudWatch Logs for the build container should capture the enclave’s stdout/stderr; watch for unexpected errors that may indicate SDK version mismatches.
  • Cost awareness. Each enclave consumes a dedicated Nitro hypervisor slice (≈ $0.012 per GB‑hour). For short‑lived builds the cost is negligible, but factor it into large‑scale pipelines.
"Treat the enclave as a hardware‑rooted vault that lives only for the duration of a build step – no more, no less."

Conclusion

By embedding Nitro Enclaves directly into CodeBuild, AWS gives DevOps engineers a concrete path to hardware‑backed secret isolation without leaving the managed CI/CD ecosystem. The pattern described here—creating a minimal enclave image, invoking it via vsock, and using KMS for decryption—can be reused across languages and frameworks, from Docker builds to serverless function packaging. As supply‑chain attacks grow more sophisticated, moving secrets out of the build container’s memory and into a tamper‑evident enclave is a pragmatic defense that scales with the modern CI/CD workflow.