How to Build an Enclave Image File for AWS Nitro Enclaves?
Building and packaging a Docker container image into an Enclave Image File for Amazon EC2 Nitro Enclaves requires preparing a compatible container image and converting it using the AWS Nitro Enclaves Command Line Interface. This guide outlines the hardware prerequisites, installation steps, container configuration requirements, and the build commands needed to generate a signed or unsigned Enclave Image File along with its cryptographic measurements for attestation.
Prerequisites and Instance Configuration
To build and run an Enclave Image File (EIF), you need an Amazon EC2 instance that supports AWS Nitro Enclaves, or a compatible Linux build environment with Docker installed. While you can build an EIF on any x86_64 or ARM64 Linux machine with Docker and the Nitro CLI installed, running the enclave requires a parent EC2 instance launched with the Nitro Enclaves option enabled.
Before starting the build process, ensure your environment meets the following criteria:
- An Amazon Linux 2, Amazon Linux 2023, or Ubuntu Linux host matching
the target CPU architecture (
x86_64oraarch64). - Docker Engine installed, running, and accessible by your current user account.
- Sufficient disk space to hold both the uncompressed Docker image
layers and the resulting
.eiffile (the final EIF size is roughly equal to the uncompressed container image size plus kernel and initramfs overhead, \(S_{\text{EIF}} \approx S_{\text{image}} + S_{\text{kernel}} + S_{\text{initramfs}}\)).
Installing the AWS Nitro Enclaves CLI and Docker
On Amazon Linux 2023, you can install the AWS Nitro Enclaves CLI and Docker directly from the standard system repositories:
sudo dnf install -y aws-nitro-enclaves-cli aws-nitro-enclaves-cli-devel dockerOn Amazon Linux 2, use the Amazon Extras library to install the required packages:
sudo amazon-linux-extras install aws-nitro-enclaves-cli -y
sudo yum install -y aws-nitro-enclaves-cli-devel dockerAfter installing the packages, add your user to the ne
and docker security groups so you can execute build
commands without elevated root privileges, and start the Docker
service:
sudo usermod -aG ne $USER
sudo usermod -aG docker $USER
sudo systemctl enable --now dockerLog out and log back in for the group membership changes to take effect. Verify the installation by checking the version of the CLI tool:
nitro-cli --versionCreating a Compatible Docker Container Image
An enclave has no persistent storage, no external network interface
(no TCP/IP stack connected to the internet or VPC), and no interactive
shell access. Instead, it communicates exclusively with its parent EC2
instance over a local virtual socket (vsock)
connection.
When authoring a Dockerfile for a Nitro Enclave, keep
the following constraints in mind:
- Entrypoint or CMD is required: The
nitro-cli build-enclavecommand extracts theENTRYPOINTorCMDdirective from the Docker image metadata and uses it as the initial process started inside the enclave. - Self-contained dependencies: All libraries, binaries, and static assets must be baked directly into the container image because the enclave cannot download packages at runtime.
- Read-only root filesystem considerations: The
enclave root filesystem runs entirely in memory
(
initramfs). Ensure your application writes temporary data to/tmpor/runand respects the memory allocation assigned to the enclave.
Below is an example of a Python application (app.py)
that listens on a vsock port inside the enclave, followed
by its corresponding Dockerfile:
import socket
VSOCK_PORT = 5000
VMADDR_CID_ANY = 0xFFFFFFFF
def main():
s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
s.bind((VMADDR_CID_ANY, VSOCK_PORT))
s.listen()
print(f"Listening on vsock port {VSOCK_PORT}...")
while True:
conn, addr = s.accept()
data = conn.recv(1024)
if data:
conn.sendall(b"Ack: " + data)
conn.close()
if __name__ == "__main__":
main()Create a Dockerfile in the same directory:
FROM public.ecr.aws/amazonlinux/amazonlinux:2023
RUN dnf install -y python3 && dnf clean all
WORKDIR /app
COPY app.py /app/app.py
CMD ["python3", "/app/app.py"]Build the local Docker image using the standard Docker build command:
docker build -t vsock-sample:latest .Packaging the Docker Image into an Enclave Image File
Once the Docker image is present in your local Docker daemon or
accessible in a remote container registry, use the
nitro-cli build-enclave command to package it into an
Enclave Image File (.eif).
Run the following command to convert the
vsock-sample:latest Docker image into an EIF named
vsock-sample.eif:
nitro-cli build-enclave \
--docker-uri vsock-sample:latest \
--output-file vsock-sample.eifIf you have a Dockerfile and want nitro-cli
to build the Docker image and convert it in a single step, you can also
pass the --docker-dir flag pointing to the build context
directory alongside --docker-uri:
nitro-cli build-enclave \
--docker-dir ./ \
--docker-uri vsock-sample:latest \
--output-file vsock-sample.eifDuring the build process, nitro-cli pulls or inspects
the container layers, flattens the filesystem into an initial RAM
filesystem (initramfs), bundles the Nitro Enclaves Linux
kernel and bootstrap init process, and writes the final binary image to
vsock-sample.eif.
Understanding Build Output and PCR Measurements
When nitro-cli build-enclave finishes, it outputs a JSON
object to standard output containing cryptographic Platform
Configuration Register (PCR) measurements calculated using SHA-384:
{
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "a1b2c3d4e5f6...",
"PCR1": "112233445566...",
"PCR2": "998877665544..."
}
}Each register serves a specific purpose in cryptographic attestation and AWS Key Management Service (KMS) access policies:
- PCR0 (Enclave Image): A cryptographic hash of the
entire Enclave Image File, including the kernel, bootstrap binaries, and
application filesystem. Any modification to the application code or base
image changes
PCR0. - PCR1 (Linux Kernel and Bootstrap): A hash of the kernel and boot ramfs provided by AWS Nitro Enclaves.
- PCR2 (Application): A hash of the user space application root filesystem extracted from the Docker container image, excluding the kernel and bootstrap components.
You can inspect an existing .eif file at any time
without rebuilding it by running
nitro-cli describe-eif:
nitro-cli describe-eif --eif-path vsock-sample.eifSigning an Enclave Image File for PCR8 Attestation
In production workflows, updating an application changes
PCR0 and PCR2, which requires updating IAM or
AWS KMS key policies that rely on those registers. To authorize enclaves
by their build signer rather than a specific image hash, you can sign
the EIF with an elliptic curve (ECDSA P-384) private key and an X.509
certificate during the build phase. Signing populates
PCR8 with the hash of the signing certificate.
First, generate an ECDSA P-384 private key and a self-signed certificate using OpenSSL:
openssl ecparam -name secp384r1 -genkey -out signing-key.pem
openssl req -new -x509 -key signing-key.pem -sha384 -days 365 -out signing-cert.pem -subj "/CN=EnclaveSigner"Next, run nitro-cli build-enclave with the
--private-key and --signing-certificate
flags:
nitro-cli build-enclave \
--docker-uri vsock-sample:latest \
--output-file vsock-sample-signed.eif \
--private-key signing-key.pem \
--signing-certificate signing-cert.pemThe resulting JSON output will now include a PCR8 value
alongside PCR0, PCR1, and PCR2.
You can reference PCR8 in AWS KMS key policy conditions
(kms:RecipientAttestation:PCR8) so any enclave image signed
by your key and certificate is authorized to decrypt sensitive
secrets.
Verifying and Running the Enclave Image File
To test the packaged .eif file on a Nitro
Enclave-enabled EC2 instance, verify that the Nitro Enclaves allocator
service is configured with sufficient memory and CPU cores in
/etc/nitro_enclaves/allocator.yaml, restart the
nitro-enclaves-allocator.service, and launch the
enclave:
nitro-cli run-enclave \
--cpu-count 2 \
--memory 1024 \
--eif-path vsock-sample.eif \
--debug-modeNote that --debug-mode allows you to view standard
output from the container via
nitro-cli console --enclave-id <ENCLAVE_ID>, which is
helpful during initial development and testing. However, when
--debug-mode is enabled, the Nitro Hypervisor zeros out all
PCR values in the attestation document (\(PCR_i = 0\)), preventing production KMS
attestation checks from succeeding until the flag is removed.