Install
$ agentstack add skill-pinkpixel-dev-skills-collection-2-implementing-aws-nitro-enclave-security ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Implementing AWS Nitro Enclave Security
When to Use
- Processing sensitive data (PII, PHI, financial records, cryptographic secrets) that must be isolated from EC2 instance operators and administrators
- Building confidential computing pipelines where even root-level access on the parent instance cannot read enclave memory or state
- Implementing cryptographic attestation workflows that tie KMS decryption rights to a specific, verified enclave image hash
- Deploying multi-party computation environments where two or more enclaves authenticate each other via attestation before exchanging data
- Hardening existing workloads that currently decrypt secrets on the parent instance by migrating decryption into an enclave boundary
Do not use when the workload does not handle sensitive data that requires hardware-level isolation, when the instance type does not support Nitro Enclaves (requires Nitro-based instances with at least 4 vCPUs), or when latency constraints make the vsock communication overhead unacceptable.
Prerequisites
- An AWS account with permissions to launch Nitro-capable EC2 instances (m5.xlarge or larger, C5, R5, M6i families)
- AWS CLI v2 and the
nitro-clitoolset installed on the parent EC2 instance (Amazon Linux 2 or AL2023) - Docker installed on the parent instance for building enclave image files (EIF)
- An AWS KMS symmetric key with key policy permissions for the enclave's IAM role
- The
aws-nitro-enclaves-sdk-cor Pythonaws-encryption-sdkfor enclave-side KMS operations - The Nitro Enclaves allocator service configured with sufficient memory and vCPU allocation in
/etc/nitro_enclaves/allocator.yaml
Workflow
Step 1: Configure the Nitro Enclaves Environment
Set up the parent EC2 instance to support enclave launches:
- Install the Nitro Enclaves CLI: On Amazon Linux 2, install the tools and allocator:
``bash sudo amazon-linux-extras install aws-nitro-enclaves-cli sudo yum install aws-nitro-enclaves-cli-devel -y sudo systemctl enable --now nitro-enclaves-allocator.service sudo systemctl enable --now docker sudo usermod -aG ne ec2-user sudo usermod -aG docker ec2-user ``
- Configure memory and CPU allocation: Edit
/etc/nitro_enclaves/allocator.yamlto reserve resources for the enclave. The enclave requires dedicated memory that is carved from the parent instance:
``yaml --- memory_mib: 4096 cpu_count: 2 ` Restart the allocator: sudo systemctl restart nitro-enclaves-allocator.service`
- Verify setup: Run
nitro-cli describe-enclavesto confirm the CLI can communicate with the Nitro hypervisor. An empty JSON array[]indicates no enclaves are running and the setup is correct.
Step 2: Build the Enclave Image File (EIF)
Package the sensitive workload into a signed enclave image:
- Create the application Dockerfile: The enclave runs a minimal Linux environment. The application communicates exclusively through vsock:
```dockerfile FROM amazonlinux:2
RUN yum install -y python3 python3-pip && \ pip3 install boto3 cbor2 cryptography requests
COPY enclaveapp.py /app/enclaveapp.py
WORKDIR /app CMD ["python3", "enclave_app.py"] ```
- Build the EIF with nitro-cli: Convert the Docker image into an enclave image file, capturing the PCR measurements:
``bash docker build -t enclave-app:latest . nitro-cli build-enclave \ --docker-uri enclave-app:latest \ --output-file enclave-app.eif `` The output contains three critical PCR values:
- PCR0: SHA-384 hash of the enclave image file (the full image digest)
- PCR1: SHA-384 hash of the Linux kernel and bootstrap process
- PCR2: SHA-384 hash of the application code
Record these values; they are used in KMS key policies for attestation-based access control.
- Build a signed EIF (recommended for production): Generate a signing certificate and use it to produce PCR8:
```bash openssl ecparam -name secp384r1 -genkey -noout -out enclavekey.pem openssl req -new -key enclavekey.pem -sha384 \ -nodes -subj "/CN=Enclave Signer" -out enclavecsr.pem openssl x509 -req -days 365 -in enclavecsr.pem \ -signkey enclavekey.pem -sha384 -out enclavecert.pem
nitro-cli build-enclave \ --docker-uri enclave-app:latest \ --output-file enclave-app.eif \ --private-key enclavekey.pem \ --signing-certificate enclavecert.pem ``` PCR8 (the signing certificate hash) enables KMS policies that trust any image signed by a specific certificate, allowing image updates without changing the policy.
Step 3: Configure KMS Attestation-Based Key Policies
Create a KMS key policy that restricts decryption to a verified enclave:
- Policy using PCR0 (image hash): This locks the key to a specific enclave build. Any code change produces a new PCR0, requiring a policy update:
``json { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowEnclaveDecrypt", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/EnclaveParentRole" }, "Action": [ "kms:Decrypt", "kms:GenerateDataKey" ], "Resource": "*", "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:ImageSha384": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210" } } } ] } ``
- Policy using PCR8 (signing certificate): Trusts any enclave signed with a specific certificate, enabling image rotation without policy changes:
``json { "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:PCR8": "ab3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890abcdef" } } } ``
- Multi-PCR policy for defense in depth: Combine PCR0 (image) and PCR1 (kernel) to ensure both the application and the boot environment match expected values:
``json { "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:PCR0": "", "kms:RecipientAttestation:PCR1": "" } } } ``
- IAM role policy: The parent instance's IAM role must have
kms:Decryptpermission, but the KMS key policy condition ensures the actual decryption only succeeds when the request originates from a valid enclave with the correct attestation document attached.
Step 4: Implement Secure Vsock Communication
Establish the parent-to-enclave communication channel:
- Vsock architecture: The only way an enclave communicates with the outside world is through a vsock (virtual socket). Vsock uses a CID (Context Identifier) and port number. The parent instance CID is always
3, and the enclave CID is assigned at launch. - Parent-side proxy server: The parent runs a proxy that forwards KMS API calls from the enclave through the vsock to the AWS KMS endpoint:
```python import socket import json import boto3
VSOCKCID = 3 # Parent CID VSOCKPORT = 5000
def startproxy(): sock = socket.socket(socket.AFVSOCK, socket.SOCKSTREAM) sock.bind((VSOCKCID, VSOCK_PORT)) sock.listen(5)
kmsclient = boto3.client('kms', regionname='us-east-1')
while True: conn, addr = sock.accept() data = conn.recv(65536) request = json.loads(data.decode())
if request['action'] == 'decrypt': response = kmsclient.decrypt( CiphertextBlob=bytes.fromhex(request['ciphertext']), Recipient={ 'KeyEncryptionAlgorithm': 'RSAESOAEPSHA256', 'AttestationDocument': bytes.fromhex(request['attestationdoc']) } ) conn.sendall(json.dumps({ 'ciphertextfor_recipient': response['CiphertextForRecipient'].hex() }).encode()) conn.close() ```
- Enclave-side client: The enclave application requests an attestation document from the Nitro Security Module (NSM) device at
/dev/nsm, attaches it to KMS decrypt requests, and receives data encrypted to the enclave's ephemeral public key:
```python import socket import json from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import hashes, serialization
PARENTCID = 3 VSOCKPORT = 5000
def getattestationdocument(publickeyder): """Request attestation document from NSM device.""" # Uses the aws-nitro-enclaves-nsm-api # NSM provides: moduleid, digest (SHA384), timestamp, PCRs, # certificate (from Nitro PKI), cabundle, publickey, userdata, nonce import nsmutil nsmfd = nsmutil.nsmlibinit() attestationdoc = nsmutil.nsmgetattestationdoc( nsmfd, publickey=publickeyder, userdata=None, nonce=None ) return attestation_doc
def decryptviaparent(ciphertexthex): """Send decrypt request through vsock to parent proxy.""" privatekey = rsa.generateprivatekey( publicexponent=65537, keysize=2048 ) publickeyder = privatekey.publickey().public_bytes( serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo )
attestationdoc = getattestationdocument(publickey_der)
sock = socket.socket(socket.AFVSOCK, socket.SOCKSTREAM) sock.connect((PARENTCID, VSOCKPORT)) sock.sendall(json.dumps({ 'action': 'decrypt', 'ciphertext': ciphertexthex, 'attestationdoc': attestation_doc.hex() }).encode())
response = json.loads(sock.recv(65536).decode()) sock.close()
# KMS encrypted the plaintext to the enclave's public key # Only the enclave's private key can decrypt it ciphertextforrecipient = bytes.fromhex( response['ciphertextforrecipient'] ) plaintext = privatekey.decrypt( ciphertextfor_recipient, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return plaintext ```
Step 5: Validate Attestation Documents
Verify attestation documents from enclaves to establish trust:
- Attestation document structure: The document is CBOR-encoded and COSE-signed (COSE_Sign1). It contains:
module_id: Identifier for the NSM moduledigest: Hashing algorithm (SHA-384)timestamp: Unix epoch milliseconds when the document was createdpcrs: Map of PCR index to measurement value (PCR0-PCR15)certificate: The NSM's x509 certificate, signed by the Nitro PKIcabundle: Certificate chain from the NSM certificate to the AWS Nitro root CApublic_key: The enclave's ephemeral public key (provided at attestation request time)user_data: Optional application-defined data (up to 512 bytes)nonce: Optional nonce for freshness verification
- Validation steps:
- Decode the COSE_Sign1 structure and extract the payload and certificate
- Verify the COSE signature using the public key from the embedded certificate
- Validate the certificate chain from the NSM certificate through the CA bundle to the AWS Nitro Attestation PKI root certificate (available at
https://aws-nitro-enclaves.amazonaws.com/AWS_NitroEnclaves_Root-G1.zip) - Check that the root CA certificate matches the expected AWS root:
aws.nitro-enclavesCN - Verify that no certificate in the chain is expired at the document's timestamp
- Compare PCR0, PCR1, PCR2 values against expected measurements from the enclave build output
- If a nonce was provided, verify it matches to prevent replay attacks
- Attestation validation code:
```python import cbor2 from cose import CoseMessage from cryptography import x509 from cryptography.x509.oid import NameOID
def validateattestation(attestationbytes, expectedpcrs, expectednonce=None): cosemsg = CoseMessage.decode(attestationbytes) payload = cbor2.loads(cose_msg.payload)
# Verify certificate chain cert = x509.loadderx509certificate(payload['certificate']) cabundle = [x509.loadderx509certificate(c) for c in payload['cabundle']]
# Check root CA is AWS Nitro root = cabundle[-1] cn = root.subject.getattributesforoid(NameOID.COMMONNAME)[0].value assert cn == 'aws.nitro-enclaves', f'Unexpected root CA: {cn}'
# Verify PCR measurements pcrs = payload['pcrs'] for idx, expectedvalue in expectedpcrs.items(): actual = pcrs.get(idx, b'').hex() assert actual == expected_value, f'PCR{idx} mismatch: {actual}'
# Verify nonce freshness if expectednonce: assert payload.get('nonce') == expectednonce, 'Nonce mismatch'
return payload ```
Step 6: Launch and Monitor the Enclave
Run the enclave and implement operational monitoring:
- Launch the enclave:
``bash nitro-cli run-enclave \ --eif-path enclave-app.eif \ --cpu-count 2 \ --memory 4096 \ --enclave-cid 16 \ --debug-mode ` Note: --debug-mode` enables the enclave console for development. Remove it in production as it allows reading enclave output, which breaks the isolation guarantee.
- Verify enclave status:
``bash nitro-cli describe-enclaves ` Expected output includes "State": "RUNNING", the assigned EnclaveCID`, memory, CPU count, and enclave flags.
- Read enclave console (debug mode only):
``bash nitro-cli console --enclave-id ``
- Terminate the enclave:
``bash nitro-cli terminate-enclave --enclave-id ``
- CloudWatch monitoring: Configure the parent instance to report enclave health metrics. Since the enclave has no network access, health checks must go through the vsock proxy:
``python # Parent-side health check over vsock def check_enclave_health(enclave_cid, port=5001): try: sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((enclave_cid, port)) sock.sendall(b'HEALTH_CHECK') response = sock.recv(1024) sock.close() return response == b'OK' except (socket.timeout, ConnectionRefusedError): return False ``
Key Concepts
| Term | Definition | |------|------------| | Nitro Enclave | An isolated virtual machine created by the Nitro Hypervisor on a Nitro-based EC2 instance with no persistent storage, no network access, and no interactive access, even from the parent instance's root user | | Attestation Document | A CBOR-encoded, COSE-signed document generated by the Nitro Security Module containing PCR measurements, a certificate chain to the AWS Nitro root CA, and optional user-provided data | | PCR (Platform Configuration Register) | SHA-384 hash measurements that uniquely identify an enclave's image (PCR0), kernel/bootstrap (PCR1), application (PCR2), IAM role (PCR4), instance ID (PCR3), and signing certificate (PCR8) | | Vsock | A virtual socket providing the sole communication channel between a parent EC2 instance and its enclave, using CID (Context Identifier) and port addressing | | EIF (Enclave Image File) | The packaged enclave image built by nitro-cli from a Docker image, containing the kernel, ramdisk, and application, producing PCR measurements at build time | | Nitro Security Module (NSM) | A custom Linux device (/dev/nsm) inside the enclave that provides attestation document generation and hardware random number generation | | COSE_Sign1 | CBOR Object Signing and Encryption single-signer structure used to sign the attestation document with the NSM's private key | | kms:RecipientAttestation | AWS KMS condition key prefi
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: pinkpixel-dev
- Source: pinkpixel-dev/skills-collection-2
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.