亚马逊AWS官方博客
自动复制 AWS Secrets Manager 密码到备份 AWS 区域
通过使用AWS Secrets Manager,您可以使用AWS KMS客户主密钥安全地存储RDS数据库密碼。借助AWS Lambda的集成,您现在可以更轻松地定期轮换这些密碼并为灾难恢复情况复制它们。在本文中,我将向您展示如何使用AWS CloudFormation设置密碼复制和创建AWS Lambda函数。通过跨AWS区域复制密碼,它可以通过使用备份副本来帮助减少灾难恢复时间
解决方案概述
这篇文章中描述的解决方案结合使用了AWS Secrets Manager,AWS CloudTrail,Amazon CloudWatch Events和AWS Lambda。您在Secrets Manager中创建一个包含RDS数据库凭据的密碼。此密碼使用AWS KMS加密。 Lambda通过在与您的只读副本相同的AWS区域中的同名密碼上执行PUT操作,来自动在您的原始AWS区域中复制密碼值。 CloudWatch Events确保每次旋转包含您的AWS RDS数据库凭证的密碼时,它都会触发Lambda函数将密碼的值复制到您的只读副本区域。这样,您的RDS数据库凭据将始终保持同步以进行恢复。
此过程假设您已经在主AWS区域中创建了一个包含RDS数据库的密碼,并已将CloudTrail日志配置为发送到CloudWatch Logs。完成此操作后,在此复制以下步骤:
- Secrets Manager会在您原始的AWS地区中輪換一个密碼。
- CloudTrail收到带有“ eventName”:“ RotationSuceeded”的日志。CloudTrail将此日志传递给CloudWatch Events。
- CloudWatch Events中对此事件名称的过滤器会触发Lambda函数。
- Lambda函数从原始AWS区域检索秘密值。
- 然后,Lambda函数对副本AWS区域中具有相同名称的密钥执行PutSecretValue。
Lambda函数由CloudTrail传递的CloudWatch Event触发。每当秘密成功輪換时,都会触发事件,这将创建CloudTrail日志,并将EventName属性设置为RotationSucceeded。
部署解决方案
该解决方案可以使用AWS cloudformation模板进行部署
以下一步步说明cloudformation模板
附加到SecretsManagerRegionReplicatorRole角色的策略是一个内联策略,该策略授予对原始AWS区域和副本AWS区域中的机密进行解密和加密的权限。此策略还授予从原始AWS区域检索密钥并将密钥存储在副本AWS区域中的权限。您可以在下面看到此策略授予对特定机密的访问权限的示例。如果您选择使用此策略,请记住将参数放入占位符值。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "KMSPermissions",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": [
"arn:aws:kms:us-east-1:111122223333:key/Example_Key_ID_12345",
"arn:aws:kms:eu-west-1:111122223333:key/Example_Key_ID_12345"
]
},
{
"Sid": "SecretsManagerOriginRegion",
"Effect": "Allow",
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:replica/myexamplereplica*"
},
{
"Sid": "SecretsManagerReplicaRegion",
"Effect": "Allow",
"Action": [
"secretsmanager:UpdateSecretVersionStage",
"secretsmanager:PutSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:eu-west-1:111122223333:secret:replica/myexamplereplica*"
},
{
"Sid": "SecretsManagerReplicaRegion",
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret"
],
"Resource": "*",
"Condition": {
"StringLike": {
"secretsmanager:Name": "myexamplereplica*"
}
}
}
]
}
创建的下一个资源是CloudWatch Events规则SecretsManagerCrossRegionReplicator。
{
"detail-type": [
"AWS Service Event via CloudTrail"
],
"source": [
"aws.secretsmanager"
],
"detail": {
"eventSource": [
"secretsmanager.amazonaws.com"
],
"eventName": [
"RotationSucceeded"
]
}
}
CloudFormation模板创建的最后一个资源是Lambda函数,它将完成复制的实际工作。
import boto3
from os import environ
targetRegion = environ.get('TargetRegion')
if targetRegion == None:
raise Exception('Environment variable "TargetRegion" must be set')
smSource = boto3.client('secretsmanager')
smTarget = boto3.client('secretsmanager', region_name=targetRegion)
def lambda_handler(event, context):
detail = event['detail']
print('Retrieving SecretArn from event data')
secretArn = detail['additionalEventData']['SecretId']
print('Retrieving new version of Secret "{0}"'.format(secretArn))
newSecret = smSource.get_secret_value(SecretId = secretArn)
secretName = newSecret['Name']
currentVersion = newSecret['VersionId']
replicaSecretExists = True
print('Replicating secret "{0}" (Version {1}) to region "{2}"'.format(secretName, currentVersion, targetRegion))
try:
smTarget.put_secret_value(
SecretId = secretName,
ClientRequestToken = currentVersion,
SecretString = newSecret['SecretString']
)
pass
except smTarget.exceptions.ResourceNotFoundException:
print('Secret "{0}" does not exist in target region "{1}". Creating it now with default values'.format(secretName, targetRegion))
replicaSecretExists = False
except smTarget.exceptions.ResourceExistsException:
print('Secret version "{0}" has already been created, this must be a duplicate invocation'.format(currentVersion))
pass
if replicaSecretExists == False:
secretMeta = smSource.describe_secret(SecretId = secretArn)
if 'KmsKeyId' in secretMeta:
replicaKmsKeyArn = environ.get('ReplicaKmsKeyArn')
if replicaKmsKeyArn == None:
raise Exception('Cannot create replica of a secret that uses a custom KMS key unless the "ReplicaKmsKeyArn" environment variable is set. Alternatively, you can also create the key manually in the replica region with the same name')
smTarget.create_secret(
Name = secretName,
ClientRequestToken = currentVersion,
KmsKeyId = replicaKmsKeyArn,
SecretString = newSecret['SecretString'],
Description = secretMeta['Description']
)
else:
smTarget.create_secret(
Name = secretName,
ClientRequestToken = currentVersion,
SecretString = newSecret['SecretString'],
Description = secretMeta['Description']
)
else:
secretMeta = smTarget.describe_secret(SecretId = secretName)
for previousVersion, labelList in secretMeta['VersionIdsToStages'].items():
if 'AWSCURRENT' in labelList and previousVersion != currentVersion:
print('Moving "AWSCURRENT" label from version "{0}" to new version "{1}"'.format(previousVersion, currentVersion))
smTarget.update_secret_version_stage(
SecretId = secretName,
VersionStage = 'AWSCURRENT',
MoveToVersionId = currentVersion,
RemoveFromVersionId = previousVersion
)
break
print('Secret {0} replicated successfully to region "{1}"'.format(secretName, targetRegion))
现在,您的CloudFormation已完成,并且所有必需的资源都已设置好,您就可以开始秘密复制了。
密码标签返回到AWSCURRENT会花费一些时间,然后大约需要数分钟才能使CloudTrail日志填充,然后将事件发送到CloudWatch Logs。收到事件后,事件将触发Lambda函数以完成复制过程。如果复制的机密中的值与原始机密中的值相同,并且两个标签都为AWSCURRENT,则说明复制已成功完成。