Donovan HOANG
Posted on June 24, 2024
TL; DR;
Securely managing secrets and credentials is crucial for maintaining the integrity of your applications in the cloud. AWS Secrets Manager simplifies this process by providing a centralized, secure repository for storing, managing, and rotating secrets such as database credentials, API keys, and application credentials. This article explains how to use AWS Secrets Manager, highlights its key features, and provides a practical example using Terraform to set up secret rotation with a Lambda function. You'll learn how to securely store secrets, eliminate hard-coded credentials, automate secret rotation, and enhance your application's security.
Maintaining the security and integrity of your apps and data in today's cloud environments depends on securely managing secrets and credentials. You can securely store, manage and rotate secrets with the aid of AWS Secrets Manager. This post will walk you through the process of safely storing managing and rotating credentials and secrets using AWS Secrets Manager. Furthermore, I will provide an example case of AWS Secrets Manager with Terraform.
Understanding AWS Secrets Manager
AWS Secrets Manager enables you to manage, retrieve, and rotate database credentials, application credentials, OAuth tokens, API keys, and other sensitive secrets throughout their lifecycle. Many AWS services can store and use secrets directly from Secrets Manager.
By using Secrets Manager, you can enhance your security posture by eliminating the need to hard-code credentials in your application source code. Storing credentials in Secrets Manager helps prevent potential exposure to anyone who can access your application or its components. Instead of hard-coding credentials, you make a runtime call to Secrets Manager to retrieve them dynamically when needed.
Key features :
Secure Storage:
- Encryption: Secrets are encrypted at rest using AWS Key Management Service (KMS) keys, ensuring that your sensitive data is protected.
- Access Control: Fine-grained access control policies can be set using AWS IAM to restrict access to secrets based on roles and permissions.
Automatic Rotation:
- Rotation Schedules: AWS Secrets Manager allows you to automatically rotate secrets according to a predefined schedule, helping to ensure that your secrets are regularly updated according to your security policies and without manual intervention.
- Lambda Integration: Integrate genuinely with AWS Lambda to define custom rotation logic for your secrets.
Access Control and Auditing:
- IAM Policies: Define who or what can access your secrets through IAM, ensuring that only authorized users and applications can retrieve sensitive data.
- Audit Logging: Integration with AWS CloudTrail provides detailed logs of secrets access, enabling you to monitor and audit sensitive data usage for compliance and security purposes.
Secrets Management:
- Versioning: AWS Secrets Manager supports versioning of secrets, allowing you to maintain multiple versions of a secret and revert to previous versions if necessary.
- Secret Retrieval: Retrieve secrets programmatically using AWS SDKs, CLI, or HTTP-based requests, ensuring that your applications can securely access secrets when needed.
Integration with AWS Services:
- Amazon RDS: Easily rotate database credentials for Amazon RDS instances with built-in support.
- Other AWS Services: Seamless integration with other AWS services like Amazon EC2, AWS Lambda, and Amazon ECS to securely manage access to secrets across your application.
Setting Up AWS Secrets Manager with Terraform
First let's create a simple secret with two values : a username and a password :
resource "aws_secretsmanager_secret" "credentials" {
name = "sensitive-credentials"
}
resource "aws_secretsmanager_secret_version" "example_version" {
secret_id = aws_secretsmanager_secret.credentials.id
secret_string = jsonencode({
username = "example_user"
password = "example_password"
})
}
Then create the lambda with its specific permissions :
resource "aws_iam_role" "lambda_rotation_role" {
name = "lambda_rotation_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "lambda_rotation_policy_attachment" {
role = aws_iam_role.lambda_rotation_role.name
policy_arn = aws_iam_policy.lambda_rotation_policy.arn
}
resource "aws_iam_policy" "lambda_rotation_policy" {
name = "lambda_rotation_policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage",
"secretsmanager:DescribeSecret"
]
Resource = [ aws_secretsmanager_secret.credentials.arn ]
}
]
})
}
resource "aws_lambda_function" "rotation_lambda" {
filename = "lambda_rotation.zip" # Path to your zip file
function_name = "SecretRotationFunction"
role = aws_iam_role.lambda_rotation_role.arn
handler = "rotation.lambda_handler"
runtime = "python3.8"
source_code_hash = filebase64sha256("lambda_rotation.zip")
environment {
variables = {
SECRET_ARN = aws_secretsmanager_secret.credentials.arn
}
}
}
With the corresponding python script that will handle the secret rotation logic depending on your need :
import boto3
import json
import os
import secrets
import string
def generate_random_password(length=16):
# Define the characters to use for password generation
alphabet = string.ascii_letters + string.digits + string.punctuation
# Generate a random password
password = ''.join(secrets.choice(alphabet) for i in range(length))
return password
def lambda_handler(event, context):
# Retrieve the secret details
secretsmanager_client = boto3.client('secretsmanager')
secret_arn = os.environ['SECRET_ARN']
# Get the current secret value
current_secret_value = secretsmanager_client.get_secret_value(
SecretId=secret_arn
)
# Generate a new random password
new_password = generate_random_password()
# Update the secret with the new password
secretsmanager_client.put_secret_value(
SecretId=secret_arn,
SecretString=json.dumps({
"username": json.loads(current_secret_value['SecretString'])['username'],
"password": new_password
})
)
return {
'statusCode': 200,
'body': json.dumps('Secret rotated successfully')
}
And finally, configure Secret Manager to use the Lambda to rotate the secret every 30 days :
resource "aws_secretsmanager_secret_rotation" "example_rotation" {
secret_id = aws_secretsmanager_secret.credentials.id
rotation_lambda_arn = aws_lambda_function.rotation_lambda.arn
rotation_rules {
automatically_after_days = 30
}
}
resource "aws_lambda_permission" "allow_secretsmanager" {
statement_id = "AllowSecretsManagerInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.rotation_lambda.function_name
principal = "secretsmanager.amazonaws.com"
source_arn = aws_secretsmanager_secret.credentials.arn
}
Deploy it π
zip lambda_rotation.zip rotation.py
terraform init
terraform apply
Test the solution
In your AWS console, go to the AWS Secret Manager page and select the secret that we just created. Click on "Retrieve secret value", you should see the inital value that we set with Terraform :
Now on the "Rotation" tab click on "Rotate secret immediately" and confirm. After retrieving the secret value another time, you should now have a fresh new generated password:
This action will now be performed each 30 days. You can change the period of rotation and even define a cron schedule expression to fit more accurately your need.
Thanks for reading ! Hope this helped you to use or understand how to take advantages of Secret Manager thanks to the rotation of secrets. Donβt hesitate to give me your feedback or suggestions.
Posted on June 24, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.