Using AWS KMS to Encrypt a Secret
In the previous section, you created an AWS KMS key in AWS KMS. In this section, you will use this key to encrypt some data.
You should run the commands in this section using an AWS principal (IAM User or Role) with kms:Encrypt permission
on that AWS KMS key.
|
Overview
The Anjuna Nitro Runtime tools provide the ability to automatically configure enclaves with secrets encrypted by a KMS key.
When building the Enclave Image File (EIF), the Anjuna Nitro Runtime can optionally take a configuration file that contains various information on how to set up the enclave when it starts. One of the configuration items in that file is the location of the encrypted data in an S3 bucket or local file. When the enclave starts, the Anjuna Nitro Runtime performs the following steps:
-
Read the location of the encrypted configuration file from the configuration file
-
Download the encrypted configuration file from AWS S3 or access it from a local file
-
Prepare an attestation report
-
Submit a request to KMS to decrypt the file (which includes submitting the attestation report)
-
Store the secrets in environment variables or files which are accessible to applications running in the enclave
In this example, you will learn how to expose secrets to the application running in the AWS Nitro Enclave through environment variables.
The first step is to encrypt some data using a KMS key, and store the encrypted file in an S3 bucket or to a local file. On the next page, you will build an enclave that can automatically download this file or access it from a local file, and decrypt it using AWS KMS.
Prerequisites
This section assumes that a few AWS objects have been created, and permissions to access them are granted to the current user:
-
an AWS KMS key
-
an AWS Nitro-based EC2 instance associated with an IAM role that has access to KMS
-
an S3 bucket for storing KMS-encrypted files (when using S3 to store the encrypted configuration)
The following AWS objects must reside in the same AWS region:
|
If the requirements above are met, declare the following applicable environment variables, depending on whether storing the encrypted secrets in an S3 bucket or in a local file. The example commands shown on this page will use those environment variables:
S3 Bucket Secrets
$ export AWS_DEFAULT_REGION=<your-region>
$ export KMS_KEY_ID=<your-kms-key>
$ export AWS_S3_BUCKET=<your-s3-bucket>
For example:
$ export AWS_DEFAULT_REGION=us-east-2
$ export KMS_KEY_ID=arn:aws:kms:us-east-2:111122223333:alias/nitro-kms-key
$ export AWS_S3_BUCKET=anjuna-encrypted-data
Local File Secrets
$ export AWS_DEFAULT_REGION=<your-region>
$ export KMS_KEY_ID=<your-kms-key>
$ export ENCRYPTED_CONFIG=<config-filename>
For example:
$ export AWS_DEFAULT_REGION=us-east-2
$ export KMS_KEY_ID=arn:aws:kms:us-east-2:111122223333:alias/nitro-kms-key
$ export ENCRYPTED_CONFIG=encrypted-config.bin
The For example:
|
Verification
Before using the AWS objects mentioned above, check that they are valid by running the commands listed below. The commands will produce output to help verify the state of your AWS resources.
This assumes that you have the AWS CLI tools installed in this host and that you have
defined the environment variables AWS_DEFAULT_REGION
, KMS_KEY_ID
, and AWS_S3_BUCKET
(if using S3 bucket storage).
Validate the IAM Role
Run the following command (replacing the string <iam-role-for-instance>
with the IAM role you want
to use for your AWS Nitro-based EC2 instance):
$ aws iam get-role --role-name <iam-role-for-instance> | jq .
The command should produce output similar to this:
{
"Role": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
},
"MaxSessionDuration": 3600,
"RoleId": "AROAYDZEBCMBGI7I65HWP",
"CreateDate": "2021-04-20T06:33:56Z",
"RoleName": "<iam-role-for-instance>",
"Path": "/",
"RoleLastUsed": {
"Region": "<your-region>",
"LastUsedDate": "2021-04-21T08:03:51Z"
},
"Arn": "arn:aws:iam::account:role/<iam-role-for-instance>"
}
}
This output shows that the IAM role exists.
If you receive an error like "An error occurred (NoSuchEntity) when calling the GetRole operation"
,
create a new IAM role using the AWS console or CLI.
Validate the AWS KMS Key
Enter the following command to get some information on the AWS KMS key:
$ aws kms describe-key --key-id ${KMS_KEY_ID} | jq .
If the command succeeds, ensure that the region for this key (in the Arn
field) matches the value
you set for the variable AWS_DEFAULT_REGION
.
Once you have verified that the key is valid, inspect the policy associated with the key to make sure that the permissions are set up correctly.
Run the following command to inspect the policy associated with the KMS key:
$ KMS_KEY_ARN=$(aws kms describe-key --key-id ${KMS_KEY_ID} | jq -r .KeyMetadata.Arn)
$ aws kms get-key-policy --key-id ${KMS_KEY_ARN} --policy-name default | jq -r .Policy | jq .
Inspect the output of the command above, and make sure the policies are correct.
-
As the owner of the key, you should have permission to manage the KMS key (change the policies associated with this KMS key) and encrypt data using the KMS key. One of the entries in the JSON policy attached to the key should look like this:
{
"Sid": "Allow admin and encryption for the user that created the key, but not decryption",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account>:user/<your-username>"
},
"Action": [
"kms:CancelKeyDeletion",
"kms:Create*",
"kms:Delete*",
"kms:Describe*",
"kms:DescribeKey",
"kms:Disable*",
"kms:Enable*",
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:Get*",
"kms:List*",
"kms:Put*",
"kms:ReEncrypt*",
"kms:Revoke*",
"kms:ScheduleKeyDeletion",
"kms:TagResource",
"kms:UntagResource",
"kms:Update*"
],
"Resource": "*"
}
-
The IAM role attached to the AWS Nitro-based EC2 instance can be used to authenticate to KMS and decrypt data when an AWS Nitro Enclave presents a valid attestation report. One of the entries in the JSON policy attached to the key should look like this (in this example, the policy uses the
PCR0
value, but any combination of the enclave measurements can be used):
{
"Sid": "Enable decrypt for Nitro Enclaves running on EC2 instance associated with the proper role",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account>:<iam-role-for-instance>/<>"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEqualsIgnoreCase": {
"kms:RecipientAttestation:PCR0": ""
}
}
}
Since you have not built the AWS Nitro Enclave (EIF), the enclave measurements
are not yet known, and the value for PCR0 can be left empty. You will use the actual values of
the enclave measurements on the next page.
|
Validate the AWS S3 bucket
Run this command to ensure that the AWS S3 bucket you specified is valid (if using an S3 bucket to store the secrets):
$ aws s3 ls --summarize ${AWS_S3_BUCKET}
If the command succeeds, verify that the policy attached to the bucket has GetObject
(i.e. READ
)
permission granted to the IAM role attached to the AWS Nitro-based EC2 instance.
$ aws s3api get-bucket-policy --bucket ${AWS_S3_BUCKET} | jq -r .Policy | jq .
The output of the command should look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow nitro instance role the ability to retrieve the bucket objects",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account>:role/<iam-role-for-instance>"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<your-s3-bucket>/*"
}
]
}
Create a Secret File
Create a file named secret-data.yaml
with list of environment variables and files that should be
made available to the application running in the AWS Nitro Enclave.
The following example provides two environment variables
(INITDB_ROOT_USERNAME
and INITDB_ROOT_PASSWORD
) and a configuration file:
version: 1.8
environment:
- INITDB_ROOT_USERNAME=anjuna
- INITDB_ROOT_PASSWORD=0mMZMU01NChE095yZ7
files:
- path: "/etc/my-application-startup.conf"
mode: 0644
content: |
# This is an example of configuration file.
# It could be anything, including TLS certificates
Hello world!
You can specify as many environment variables or files as you want in the secret-data.yaml
file.
|
The Anjuna Nitro Runtime will not create the directories for the file entries. Make sure the directory exists as part of the Docker container specification. In the example above, the enclave startup will fail if you specify a path like
|
Encrypt the data
Make sure you have the following information available before running the command:
|
-
Encrypted secrets in AWS S3
-
Encrypted secrets in a local file
To store the encrypted secrets on S3, run the following command (assuming you have defined the variables
with the values for AWS_DEFAULT_REGION
, KMS_KEY_ID
, and AWS_S3_BUCKET
):
$ anjuna-nitro-encrypt \
--cmk $KMS_KEY_ID \
--bucket-name $AWS_S3_BUCKET \
--bucket-key kms-encrypted-data.bin \
--config secret-data.yaml
If the command succeeds, a new file will be created in the specified S3 bucket, encrypted by the specified AWS KMS key.
To store the encrypted secrets to a local file, run the following command (assuming you have defined the variables
with the values for AWS_DEFAULT_REGION
, KMS_KEY_ID
, and ENCRYPTED_CONFIG
):
$ anjuna-nitro-encrypt \
--cmk $KMS_KEY_ID \
--output-file $ENCRYPTED_CONFIG \
--config secret-data.yaml
If the command succeeds, a new file will be created at --output-file
, encrypted by the specified AWS KMS key.
Next Steps
If the previous command succeeds, a new file will be created in the specified S3 bucket, encrypted by the specified AWS KMS key.
This file is the encrypted configuration file. Only enclaves that match the condition specified in the AWS KMS key policy will be allowed to decrypt this file.
The next step is to build an enclave, measure it, and update the policy attached to your AWS KMS key to authorize this enclave to decrypt the data.
Most of the steps listed in the previous sections verified that the AWS objects were created
and set up properly. The actual encryption of the secrets is achieved by calling the
anjuna-nitro-encrypt command-line tool.
|