Secrets Management & KMS
๐ฅ Vibe Prompt
"Set up KMS with automatic key rotation. Store secrets in AWS Secrets Manager. Implement envelope encryption."
KMS (Key Management Service)
resource "aws_kms_key" "app" {
description = "Application encryption key"
deletion_window_in_days = 30
enable_key_rotation = true # Auto rotate yearly
policy = jsonencode({
Statement = [{
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = ["kms:*"]
Resource = "*"
}, {
Effect = "Allow"
Principal = { Service = "logs.${data.aws_region.current.name}.amazonaws.com" }
Action = ["kms:Encrypt*"]
Resource = "*"
}]
})
}
resource "aws_kms_alias" "app" {
name = "alias/app-key"
target_key_id = aws_kms_key.app.key_id
}
Envelope Encryption
# 1. Generate a data key (DEK) via KMS
import boto3
kms = boto3.client('kms')
response = kms.generate_data_key(
KeyId='alias/app-key',
KeySpec='AES_256'
)
plaintext_dek = response['Plaintext'] # USE ONCE then discard
ciphertext_dek = response['CiphertextBlob'] # Store with data
# 2. Encrypt data with DEK (locally)
from cryptography.fernet import Fernet
fernet = Fernet(plaintext_dek)
encrypted_data = fernet.encrypt(b"Sensitive data")
# 3. Store: encrypted_data + ciphertext_dek
# Store both together
# 4. Decrypt: decrypt DEK first, then data
plaintext_dek = kms.decrypt(CiphertextBlob=ciphertext_dek)['Plaintext']
fernet = Fernet(plaintext_dek)
data = fernet.decrypt(encrypted_data)
print(f"Decrypted: {data.decode()}")
AWS Secrets Manager
resource "aws_secretsmanager_secret" "db" {
name = "prod/db_password"
rotation_rules {
automatically_after_days = 30
}
}
resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = jsonencode({
username = "admin"
password = random_password.db.result
host = aws_db_instance.postgres.address
port = 5432
})
}
# Lambda rotation
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotate_db.arn
}
AWS Parameter Store (Cheaper Alternative)
import boto3
ssm = boto3.client('ssm')
# Write (SecureString)
ssm.put_parameter(
Name="/prod/app/api_key",
Value="sk-1234...",
Type="SecureString",
KeyId="alias/app-key"
)
# Read
response = ssm.get_parameter(
Name="/prod/app/api_key",
WithDecryption=True
)
api_key = response['Parameter']['Value']
Secrets Manager vs Parameter Store
| Feature | Secrets Manager | Parameter Store | |---------|----------------|----------------| | Max secret size | 64 KB | 8 KB (free), 8 KB (advanced) | | Rotation | Built-in | Manual | | Cross-account | Yes | Yes | | Price | $0.40/secret/month | Free (standard) | | Auto-generate | Yes (random password) | No |
Best Practices
- Use envelope encryption for large data
- Rotate keys and secrets regularly
- Use least privilege KMS key policies
- Never hardcode secrets in code/config
- Use IAM roles for EC2/Lambda (not keys)
- Separate keys by environment (dev/prod)
- Enable CloudTrail for KMS API calls