Encrypt environment variables in Lambda with encryption helpers

Environment variables in Lambda are encrypted by default after deployment, but this doesn't provide full security. With the help of customer master keys in KMS, it's possible to encrypt these variables during deployment as well..

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.

Encryption helpers
Can't use encryption helpers without a customer master key

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.

Encrypted environment variables
James Bond is safe

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.