Encrypt environment variables in Lambda with encryption helpers
As it was mentioned in this post, when environment variables are used with Lambda, they are only encrypted after deployment. Lambda then automatically decrypts them when the function is invoked without any extra effort from the account owner.
1. Encryption helpers with KMS keys
Sometimes this level of security is not enough, and we want to encrypt environment variables having sensitive information after Lambda has deployed the function.
If we want to achieve this, for example, because we want to protect a database password or the life of the secret agents, we have to use the encryption helpers.
1.1. Default KMS key
By default, Lambda encrypts environment variables with the default KMS key, which is available in each region when one creates an account.
When Lambda invokes the function, it decrypts the environment variables, and makes them available for use in the code.
1.2. Custom KMS key
But if we want to use encryption helpers and intend to apply a higher level of encryption, we have to use custom KMS keys or customer master keys (CMKs).
If we try to use encryption helpers by expanding the Encryption configuration option, and we don’t have a CMK, we’ll receive an error message.
Additional encryption can be enabled by ticking the Enable helpers for encryption in transitbox, but it won’t work without having a custom KMS key.
1.3. Create a CMK
Customer master keys cost money and they are not covered by the free-tier offer. A CMK costs $1 a month and they charge you for requesting the key as well. They have a free tier on the requests, though.
If the key is used for less than a month, it’s billed pro-rata.
If you decide to follow the instructions below, be prepared that it will cost you some cents, because AWS doesn’t let you delete the key immediately. There is a minimum of 7, maximum of 30 days of waiting period, and as such, they will charge you for these days.
Creating a CMK is easy with the following CLI command:
aws kms create-key --description 'Secret agent sensitive info encryption' --region ap-southeast-2
This is the minimum command that generates a key. It’s important that the key should be generated in the same region as the Lambda function, which contains the environment variables to encrypt.
If everything goes well, the response should look something like this:
{
"KeyMetadata": {
"AWSAccountId": "123456789012",
"KeyId": "8268a548-267e-4755-b5ca-e104a848c134",
"Arn": "arn:aws:kms:ap-southeast-2:123456789012:key/8268a548-267e-4755-b5ca-e104a848c134",
"CreationDate": 1569234564.42,
"Enabled": true,
"Description": "Secret agent sensitive info encryption",
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER"
}
}
The response contains the KeyId
, which is often required when a KMS key is referred to, and the Arn
, which is the unique AWS identifier of the entity (in this case, the entity is the customer master key). Take note of the ARN.
2. Add a policy to the Lambda function
But that’s not enough to encrypt James Bond. Because the default KMS key is not used here, Lambda needs permission to use the CMK to decrypt the secrets when the function is invoked.
Let’s add the kms:Decrypt
policy to the function. If you have a Lambda function with an environment variable, then use that function, if not, create one by following the instructions in the post.
First, let’s run the following command to get the most important pieces of information about the secret-agents-in-environment
function (or whatever function you want to use, replace the name accordingly):
aws lambda get-function --function-name secret-agents-in-environment --region ap-southeast-2
The response should contain the role attached to the function. In this case, it’s called LambdaBasicExecutionRole
. In your case, it might be called differently.
The policy we need to add looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:ap-southeast-2:123456789012:key/8268a548-267e-4755-b5ca-e104a848c134"
}
]
}
The policy basically states that it’s allowed to use the KMS key identified by the ARN in the Resource
key for decryption. By defining only the resource we want to use we leverage the least privilege principle, which is the cornerstone of the security in AWS.
Let’s add it to the LambdaBasicExecutionRole
.
In the console, go to IAM, then Roles. Type LambdaBasicExecutionRole
in the search bar, and then click on the role. Next, click on Add inline policy, choose KMS as the service, and then type Decrypt. Finally, copy the ARN of the KMS you obtained above in the Resource
section.
3. Update and invoke the function
Let’s add the CMK to the Lambda function:
aws lambda update-function-configuration \\
--function-name secret-agents-in-environment \\
--kms-key-arn arn:aws:kms:ap-southeast-2:123456789012:key/8268a548-267e-4755-b5ca-e104a848c134 \\
--region ap-southeast-2
Now it’s possible to tick the Enable helpers for encryption in transit box.
An extra button called Encrypt
will appear next to the variables. If you click on it, the environment variables (James Bond and its secret mission) will indeed become secret and that’s something.
The button can be toggled between Encrypt
and Decrypt
.
Let’s invoke the function:
aws lambda invoke --function-name secret-agents-in-environment --region ap-southeast-2 response.json
The response.json
file should contain the return value, which in this case is an object with the environment variables as per the function’s return value:
{
"agent": "James Bond",
"mission": "Get pendrive with agent list"
}
That’s it, we have successfully encrypted the environment variables, and no agent life is at risk any more.
4. Clean up
Because AWS charges customers for having a CMK, it’s a good idea to delete it, which takes effect after a specified waiting period (minimum of 7, maximum of 30 days).
When a CMK is scheduled for deletion, the already encrypted environment variables cannot be decrypted again. Also, after the command has been executed, the key cannot be used any more for encryption, unless the deletion process is cancelled (which I won’t do here).
4.1. Delete the Lambda function
It’s easy to delete the function with the following command:
aws lambda delete-function --function-name secret-agents-in-environment --region ap-southeast-2
There will be no response in the console, but the aws lambda list-functions --region ap-southeast-2
can help you check if the deletion has been successful.
4.2. Delete the role
It’s easier to delete the role in the console. Go to Roles, type the name of the role (LambdaBasicExecutionRole
), and choose Delete role. The
aws iam delete-role --role-name LambdaBasicExecutionRole
command won’t work, you’ll need to detach the separately added inline policy first. So it’s more convenient to do everything in the console (along with other IAM-related activities).
4.3. Delete the customer master key
Finally, let’s delete the CMK (or better say, schedule it for deletion):
aws kms schedule-key-deletion \\
--key-id arn:aws:kms:ap-southeast-2:123456789012:key/8268a548-267e-4755-b5ca-e104a848c134 \\
--pending-window-in-days 7 --region ap-southeast-2
It’s important to specify the pending-window-in-days
option because the default waiting period in the case of the CLI command is 30 days.
The response contains the time of deletion:
{
"KeyId": "arn:aws:kms:ap-southeast-2:123456789012:key/8268a548-267e-4755-b5ca-e104a848c134",
"DeletionDate": 1569888000.0
}
The key will be in Pending deletion
status for 7 days after which it gets deleted.
5. Summary
Full encryption of environment variables in Lambda is only possible with customer master KMS keys.
When a CMK is specified and added to the Lambda function, sensitive information is encrypted both during and after deployment.
Thanks for reading and see you next time.