Securely Storing and Retrieving Environment Variables in Node.js with AWS Secrets Manager and KMS using Terraform

Adham El Banhawy
4 min readMar 19, 2023

--

Photo by Towfiqu barbhuiya on Unsplash

Recently at work I had run into a requirement of having to store & retrieve the value of an environment variable securely using AWS.

The environment secret should be encrypted at rest and in transit and only be decrypted at run time by the application.

In this blog post, we will explore how to use Node.js with AWS Secrets Manager and AWS KMS to store and retrieve environment variables securely. We will also use Terraform to set up the necessary AWS resources.

AWS Secrets Manager

AWS Secrets Manager is a service that allows you to store and retrieve secrets such as database credentials, API keys, and other sensitive information. Secrets Manager encrypts and stores secrets using AWS KMS and allows you to control access to secrets using IAM policies. Secrets Manager also provides automatic rotation of secrets to improve security.

AWS KMS

AWS KMS is a service that allows you to create and manage encryption keys for your AWS resources. KMS can be used to encrypt data at rest and in transit, and can also be used to encrypt environment variables stored in AWS Secrets Manager.

Node.js with AWS Secrets Manager and AWS KMS

To use AWS Secrets Manager and AWS KMS with Node.js, we need to install the AWS SDK for Node.js:

npm install aws-sdk

Next, we can use the following code to fetch a secret from AWS Secrets Manager and decrypt it using AWS KMS:

const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
const kms = new AWS.KMS();

async function getSecret(secretName) {
const params = {
SecretId: secretName
};
const response = await secretsManager.getSecretValue(params).promise();
const ciphertext = response.SecretBinary;
const decrypted = await kms.decrypt({ CiphertextBlob: ciphertext }).promise();
return decrypted.Plaintext.toString('utf-8');
}

// Usage
const MY_ENV_VAR = await getSecret('my-app/dev/my-secret');
console.log(MY_ENV_VAR);

In this example, we are using the getSecretValue method of the secretsManager object to fetch the secret with the name my-app/dev/my-secret from AWS Secrets Manager. The SecretBinary property of the response object contains the ciphertext value of the secret. We then use the decrypt method of the kms object to decrypt the ciphertext value. The Plaintext property of the response object contains the decrypted plaintext value of the secret.

Terraform

To set up the infrastructure using Terraform, we can use the following code:

resource "aws_kms_key" "my_kms_key" {
description = "Encryption key for my secrets"
}

resource "aws_secretsmanager_secret" "my_secret" {
name = "my-app/dev/my-secret"
kms_key_id = aws_kms_key.my_kms_key.id
}

resource "aws_secretsmanager_secret_version" "my_secret_version" {
secret_id = aws_secretsmanager_secret.my_secret.id
secret_binary = base64encode("my-secret-value")
}

In this example, we are creating an encryption key using the aws_kms_key resource. We are also creating a secret with the name my-app/dev/my-secret using the aws_secretsmanager_secret resource and specifying the kms_key_id property to use the encryption key we created earlier. We are also creating a secret version using the aws_secretsmanager_secret_version resource with the base64-encoded ciphertext value my-secret-value. Note that the ciphertext value should be replaced with the actual ciphertext value.

IAM Permissions

This wouldn’t be an article about AWS without mentioning the roles IAM play in this use case. In order for the nodejs code to successfully work and execture API calls to the Secrets Manager and KMS services you will need the an IAM role with the following permissions:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SecretsManagerPermissions",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecrets"
],
"Resource": "arn:aws:secretsmanager:<region>:<account-id>:secret:<secret-name>"
},
{
"Sid": "KMSPermissions",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:<region>:<account-id>:key/<key-id>"
}
]
}

Conclusion

In this blog, we explored how to use Node.js with AWS Secrets Manager and AWS KMS to securely store and retrieve environment variables. We also used Terraform to set up the necessary AWS resources. By using AWS Secrets Manager and AWS KMS, we can ensure that our secrets are encrypted and secure, and we can control access to them using IAM policies. This approach also allows us to automatically rotate our secrets to improve security.

Using AWS Secrets Manager and AWS KMS with Node.js is simple and straightforward. We can use the AWS SDK for Node.js to fetch secrets from Secrets Manager and decrypt them using KMS. With Terraform, we can easily create the necessary AWS resources to store our secrets.

I hope this blog post was helpful in understanding how to use AWS Secrets Manager and AWS KMS with Node.js to securely store and retrieve environment variables. If you have any questions or feedback, please leave a comment below. Thank you for reading!

--

--

Adham El Banhawy

Software Developer, blogger, and tech enthusiast. Previously @ IBM. Currently Cloud consulting.