Getting started: Connect an enclave with AWS KMS for cryptographic attestation and secrets management
Use this pattern to access secrets only from inside a validated enclave.
You build an enclave from an enclave image file (EIF) that you generate from a Docker image. The EIF contains unencrypted copies of all code and data, so unencrypted secrets must not be part of the EIF. To make secret data accessible only from inside an enclave, use AWS KMS. AWS KMS can grant permission for decrypt operations exclusively to enclaves launched from a particular EIF. For more information, see Using cryptographic attestation with AWS KMS and Cryptographic attestation.
To authenticate the enclave to AWS KMS, provide an attestation document as part of your AWS KMS operation request. This attestation document contains cryptographic hashes (PCR values) of the enclave state, including PCR0, which is a hash over the entire EIF. AWS root of trust signs the document. This signature gives AWS KMS assurance that the request originates from an AWS Nitro confidential compute environment.
An enclave does not have network access. It communicates only with its
parent instance over vsock. To access AWS KMS from within the enclave, use the
vsock-proxy and kmstool-enclave-cli tools provided by
AWS.
Topics
Communication overview
The following diagram shows how an enclave uses AWS KMS and cryptographic attestation to decrypt a secret.
Figure 2: Decrypt with AWS KMS and cryptographic attestation
The preceding diagram shows how your application interacts with AWS KMS through a
prepackaged CLI tool named kmstool-enclave-cli.
-
The parent instance passes credentials and ciphertext into the enclave over virtio-vsock. For more information, see Getting started: Using the virtio-vsock.
-
Your application invokes the
kmstool-enclave-clibinary through a system call. -
The CLI utility generates an ephemeral private/public key pair.
-
The CLI utility requests an attestation document from the Nitro Secure Module (NSM) device and includes the public key.
-
The CLI creates a AWS KMS decrypt request by using a standard AWS SDK and attaches the attestation document from the previous step. The SDK dials out to an HTTPS AWS KMS endpoint over vsock.
-
The
vsock-proxyprocess on the parent instance listens on CID3, port8000. It accepts the connection and transparently forwards the packets to the configured AWS KMS endpoint. A secure, bidirectional TLS connection is established between the AWS KMS endpoint and the enclave. -
AWS KMS receives the decrypt request, validates the attestation document, and decrypts the payload. If the attestation document contains a public key, AWS KMS uses this key to re-encrypt the plaintext so that only the owner of the matching private key can read the sensitive information.
-
The CLI receives the AWS KMS response and decrypts the payload with the ephemeral private key from step 3.
-
The connection closes.
Integrate this pattern in your own application
Inside the enclave
Inside the enclave, complete the following steps.
-
Receive AWS credentials from the parent instance. You need these credentials to authenticate to AWS KMS. Pass them as part of your own data channel. For more information, see Getting started: Using the virtio-vsock.
-
Generate a local ephemeral private/public key pair for plaintext encryption of decrypt responses.
-
Obtain an attestation document from the Nitro Secure Module (NSM) device and include the ephemeral public key.
-
All AWS SDKs natively support attestation documents through the
Recipientfield. For more information, see Decrypt in the AWS Key Management Service Developer Guide.
Inside the parent instance
Inside the parent instance, complete the following steps.
-
Provide a mechanism to get fresh AWS credentials from IMDSv2 and pass them into the enclave with the ciphertext.
-
Use the default
vsock-proxyservice provided by AWS to connect to AWS KMS. Add the specific AWS KMS endpoint to the allow list.
Example: Decrypt a secret with AWS tooling
The following example walks you through setting up a vsock-based communication
channel between the enclave and the parent instance, and decrypting a secret from
inside the enclave by using a CLI tool provided by AWS named
kmstool-enclave-cli.
Note
Use the same Region consistently throughout this example. The Region that
you specify when you encrypt the secret (Step 1), the AWS KMS endpoints in the
vsock-proxy allow list (Step 2), and the Region that you pass
to kmstool-enclave-cli (Step 3) must all match. This example uses
us-east-1.
Topics
Prerequisites
Important
This example builds on the Getting started: Using the virtio-vsock example, and it reuses the scripts created in that example. Complete that example before you begin.
Ensure that you have the following before you begin.
-
The
vsock-proxysystemd service on the parent instance, which is required to communicate with the AWS KMS endpoint. -
The kmstool-enclave-cli
binary on GitHub, or a similar binary, inside the enclave.
Step 1: Create a AWS KMS key and encrypt a secret
-
Create a symmetric AWS KMS key by following the steps in Creating symmetric encryption AWS KMS keys in the AWS Key Management Service Developer Guide. Note the key ID. Use the following command templates to encode your secret and pass it to AWS KMS for encryption. Replace the key ID placeholder with your key ID.
ciphertext=$( echo "Hello World" | base64 ) aws kms encrypt --key-id "<alias/your-key-alias-or-key-id>" --plaintext ${ciphertext} --output text --query CiphertextBlob --region us-east-1 -
Run the
nitro-cli describe-enclavescommand to get the PCR0 value for the AWS KMS key resource policy.If you started the enclave in debug mode, use the following value for PCR0.
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Otherwise, use the PCR0 value from the output of the
nitro-cli describe-enclavescommand, for example:b078785234059e040259dd555d9008041240568772950f08602812b301e4b2b94c0ca68c50218564437d6d43b38f1de2 -
Update the following AWS KMS key policy template with the correct Amazon EC2 role ARN and
kms:RecipientAttestation:PCR0value, and then follow the steps in Key policies in AWS KMS in the AWS Key Management Service Developer Guide to set the policy.{ "Sid" : "Enable enclave data processing", "Effect" : "Allow", "Principal" : { "AWS" : "<arn:aws:iam::111122223333:role/data-processing>" }, "Action": [ "kms:Decrypt" ], "Resource" : "*", "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:PCR0": "<EIF image sha384>" } } }
Step 2: Prepare the parent instance
Use the vsock-proxy systemd service provided by AWS as a
vsock-to-TCP outbound proxy. This proxy allows the enclave to dial out to AWS KMS. Before
you start the systemd service on the parent instance, add the specific AWS KMS
endpoints, including the Region, to the vsock-proxy.yaml
allow list, as shown in the following example.
VSOCK_PROXY_YAML=/etc/nitro_enclaves/vsock-proxy.yaml cat <<'EOF' > $VSOCK_PROXY_YAML allowlist: - {address: kms.us-east-1.amazonaws.com, port: 443} - {address: kms-fips.us-east-1.amazonaws.com, port: 443} EOF systemctl enable --now nitro-enclaves-vsock-proxy.service
Step 3: Run inside the enclave
-
Create a Dockerfile that includes the
kmstool_enclave_clibinary and the libraries provided by AWS. For more information, see the kmstool-enclave-clidocumentation on GitHub. The CLI requires parameters such as the Region, AWS credentials, and the ciphertext. Transfer these into the enclave and export them as environment variables. For more information, see Step 1: Prepare and run the enclave. By default, you don't need to specify the AWS KMS key ID because it is encoded in the ciphertext.
-
Update the
per_request.shfile from Step 1: Prepare and run the enclave to invoke thekmstool_enclave_clibinary and return the standard output as the result.REGION=us-east-1 /app/kmstool_enclave_cli decrypt \ --region ${REGION} \ --proxy-port 8000 \ --aws-access-key-id ${AWS_ACCESS_KEY_ID} \ --aws-secret-access-key ${AWS_SECRET_ACCESS_KEY} \ --aws-session-token ${AWS_SESSION_TOKEN} \ --ciphertext ${CIPHERTEXT} -
Rebuild the enclave Dockerfile, convert the container to an enclave image file, and run the enclave as described in Step 1: Prepare and run the enclave.
Step 4: Provide the ciphertext by using curl
Use curl to post your ciphertext to the local HTTPS
endpoint.
curl -k --header "Content-Type: application/json" \ --request POST \ --data '{"ciphertext":"MySecretCipherText"}' \ https://localhost:8443 | jq '.'
The output is similar to the following.
{
"enclaveResult": "\"Hello World\\n\""
}
The HTTPS server on the parent instance parsed the curl request.
It augmented the payload with fresh AWS credentials
from IMDSv2 and sent it into the enclave. Inside the enclave, the payload was passed to
kmstool-enclave-cli through environment variables.
kmstool-enclave-cli created an attestation document and dialed out
to AWS KMS through the vsock-proxy that you configured. AWS KMS validated
the attestation document and decrypted the payload. The decrypted result was
returned to the enclave and used as the response to your
POST request.