Secret Rotation and Lifecycle Management
Secret Rotation and Lifecycle Management
Effective secret management requires automated rotation to limit exposure windows and comply with security policies. Manual rotation doesn't scale and often leads to extended use of compromised credentials. Automated rotation must coordinate between secret stores, IaC deployments, and running applications to prevent service disruptions.
Rotation strategies vary by secret type and usage patterns. Database passwords might rotate daily with zero-downtime techniques using multiple valid credentials. API keys could rotate monthly with grace periods allowing both old and new keys. Certificates require careful rotation before expiration while maintaining trust chains.
# Automated secret rotation framework
import boto3
import json
from datetime import datetime, timedelta
from typing import Dict, List, Optional
import logging
class SecretRotationManager:
def __init__(self, secret_store: str = 'aws_secrets_manager'):
self.secret_store = secret_store
self.logger = logging.getLogger(__name__)
if secret_store == 'aws_secrets_manager':
self.client = boto3.client('secretsmanager')
elif secret_store == 'vault':
self.client = self._init_vault_client()
def rotate_secret(self, secret_id: str,
rotation_lambda_arn: Optional[str] = None) -> Dict:
"""Initiate secret rotation."""
if self.secret_store == 'aws_secrets_manager':
if not rotation_lambda_arn:
raise ValueError("Lambda ARN required for AWS Secrets Manager rotation")
response = self.client.rotate_secret(
SecretId=secret_id,
RotationLambdaARN=rotation_lambda_arn,
RotationRules={
'AutomaticallyAfterDays': 30
}
)
return {
'status': 'initiated',
'version_id': response['VersionId'],
'rotation_enabled': True
}
def implement_rotation_lambda(self) -> str:
"""Example rotation Lambda for RDS."""
lambda_code = '''
import boto3
import json
def lambda_handler(event, context):
service_client = boto3.client('secretsmanager')
rds_client = boto3.client('rds')
arn = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
metadata = service_client.describe_secret(SecretId=arn)
if step == "createSecret":
create_new_secret(service_client, arn, token)
elif step == "setSecret":
set_secret_in_rds(service_client, rds_client, arn, token)
elif step == "testSecret":
test_secret_connection(service_client, arn, token)
elif step == "finishSecret":
finish_secret_rotation(service_client, arn, token)
def create_new_secret(service_client, arn, token):
# Get current secret
current = service_client.get_secret_value(
SecretId=arn,
VersionStage="AWSCURRENT"
)
# Generate new password
new_password = service_client.get_random_password(
PasswordLength=32,
RequireEachIncludedType=True
)['RandomPassword']
# Create new version
secret_dict = json.loads(current['SecretString'])
secret_dict['password'] = new_password
service_client.put_secret_value(
SecretId=arn,
ClientRequestToken=token,
SecretString=json.dumps(secret_dict),
VersionStages=['AWSPENDING']
)
def set_secret_in_rds(service_client, rds_client, arn, token):
# Get pending secret
pending = service_client.get_secret_value(
SecretId=arn,
VersionStage="AWSPENDING",
VersionId=token
)
secret_dict = json.loads(pending['SecretString'])
# Update RDS password
rds_client.modify_db_instance(
DBInstanceIdentifier=secret_dict['dbInstanceIdentifier'],
MasterUserPassword=secret_dict['password'],
ApplyImmediately=True
)
'''
return lambda_code
Zero-downtime rotation requires careful coordination between IaC deployments and running applications. Applications must handle multiple valid credentials during rotation windows. IaC templates should support gradual rollouts, updating infrastructure components in stages to maintain service availability.