AWS Secrets Manager:核心原理、自动轮换与集成实战
好,我们直接进入正题。AWS Secrets Manager 是我个人在生产环境中用得最多的云 Secrets 服务。说实话,刚开始接触它的时候,我觉得就是一个“加密的钥匙柜”,但用深了才发现,它的自动轮换和集成能力才是真正的杀手锏。这一章,我就带你把它拆开看看。
核心原理:它到底是怎么存秘密的?
你想想看,一个 Secrets 服务最基础的功能是什么?存和取。但 AWS 在设计上做了几件事,让“存”和“取”都变得很安全。
- 存储层:Secret 的值(比如数据库密码)会被加密后存储在 DynamoDB 中。嗯,你没看错,底层的存储是 DynamoDB,但用户完全感知不到。
- 加密层:默认使用 AWS 托管密钥(aws/secretsmanager),你也可以换成自己的 KMS 密钥。我建议你用自己的 KMS 密钥,方便做密钥的权限隔离和审计。
- 访问控制:通过 IAM 策略控制谁可以读、写、轮换某个 Secret。这其实是最容易被忽略的点——很多事故都是因为 IAM 策略放得太宽。
- 版本管理:每个 Secret 可以有多个版本(AWSCURRENT、AWSPREVIOUS、AWSPENDING 等)。轮换时就是通过切换这些版本来实现的,这样在更新凭据的瞬间,应用不会断连。
一句话总结核心原理:Secrets Manager 就是一个“加密的键-值仓库”,只不过它围绕“凭据”这个场景,把自动轮换、版本控制、与 AWS 服务的集成,做成了开箱即用的功能。
自动轮换:为什么轮换?怎么轮换?
我记得有一次帮客户做安全审计,发现一个 RDS 数据库的密码已经 3 年没换过。我当时心里就咯噔一下。你想想看,如果这个密码泄露了,攻击者可以一直访问你的数据库。自动轮换就是为了解决这个问题——让密码定期过期,并且自动生成新密码。
轮换策略
AWS Secrets Manager 的轮换,说白了就是一个 Lambda 函数在替你干活。轮换策略一般分为两步或三步:
- 创建新版本(AWSPENDING):Lambda 函数生成一个新密码,并更新到 RDS 的用户中。
- 测试新版本(可选):用新密码做一次连接测试,确保密码生效。
- 切换版本:将 AWSPENDING 标记为 AWSCURRENT,旧版本变成 AWSPREVIOUS。
我曾经在生产环境遇到过一个问题:轮换策略中测试步骤写得太简单,导致新密码在 RDS 上写入了但未提交,结果 Secrets Manager 以为轮换成功,实际上数据库里的密码没变。嗯,这里要注意,测试逻辑一定要覆盖“写+读+验证”全流程。
避坑指南:轮换频率不要设置得太短。我个人建议生产环境用 30 天或 60 天。如果遇到轮换失败(比如 RDS 不可用),Secrets Manager 会保留旧版本凭据,应用还能继续工作。但如果你把轮换频率设为 1 天,一旦连续失败几次,你就要头疼了。
轮换的触发方式
- 按时间周期:例如每 30 天自动触发一次。这是最常见的设置。
- 手动触发:通过 CLI 或控制台立即执行一次轮换。适合在安全事件后重置凭据。
- 按需触发:通过 API 调用
rotate_secret触发。适合与 CI/CD 流水线集成。
与 RDS 集成:这才是真正的降本增效
AWS Secrets Manager 和 RDS 的集成,是我觉得最有价值的功能之一。它不仅仅是存密码,而是能在 RDS 上直接管理用户和密码。
两种集成模式
| 模式 | 描述 | 适用场景 |
|---|---|---|
| 动态轮换 | Secrets Manager 自动在 RDS 中创建一个用户,并管理其密码。轮换时,旧密码失效,新密码生效。 | 新建应用,推荐使用。你什么都不用管,Secrets Manager 会帮你维护数据库用户。 |
| 静态凭据 | 你手动在 RDS 中创建用户和密码,然后把密码存到 Secrets Manager。轮换时,Secrets Manager 会更新 RDS 中的密码。 | 已有应用,不能修改数据库用户。但要注意,轮换脚本必须正确更新数据库中的密码。 |
我个人更推荐动态轮换。为什么?因为全自动,不需要你写任何数据库操作代码。Secrets Manager 提供的 Lambda 函数模板已经替你写好了。
注意:动态轮换时,Secrets Manager 会在 RDS 中创建一个名为 rds_admin 的备份用户(具体名称取决于模板)。这个用户拥有较高的权限,请确保 IAM 角色和网络规则都严格限制,不要被滥用。
实战:快速集成 RDS
- 在 Secrets Manager 控制台,选择“存储新的机密”。
- 选择“RDS 数据库的凭据”。
- 选择你的 RDS 实例,输入数据库用户名(通常是你创建的管理员用户)。
- 设置轮换周期(比如 30 天),选择轮换用的 Lambda 函数(可以自动创建)。
- 保存后,Secrets Manager 会生成一个 ARN。应用通过这个 ARN 获取最新的数据库密码。
与 Lambda 集成:无服务器应用的安全凭据
Lambda 函数需要访问数据库,那数据库密码放哪里?环境变量?硬编码?都不对。你应该让 Lambda 函数从 Secrets Manager 中读取。
集成方式
Lambda 函数通过 AWS SDK 或 HTTP API 调用 Secrets Manager 的 GetSecretValue 接口。我建议你在 Lambda 初始化阶段(Handler 之外)获取 Secret,而不是每次调用都去请求。这样可以避免 API 调用延迟和费用。
import boto3
from botocore.exceptions import ClientError
import json
def get_secret():
secret_name = "my-rds-secret"
region_name = "us-east-1"
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
raise e
secret = get_secret_value_response['SecretString']
return json.loads(secret)
# 在全局初始化时获取 Secret
secret = get_secret()
def lambda_handler(event, context):
# 直接使用 secret 中的用户名和密码
username = secret['username']
password = secret['password']
# 连接数据库...
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
设计要点:
- 确保 Lambda 函数的执行角色有 secretsmanager:GetSecretValue 权限。
- 如果使用 VPC 内的 RDS,Lambda 函数必须部署在同一个 VPC 中。
- 考虑使用 SecretCache 客户端(如 AWS 的缓存 SDK)来减少 API 调用。
一张图看懂核心逻辑
下面这张图是我画的核心架构,你看一遍应该就能理解全部流程了。
总结几个我的实际建议
- 不要使用默认的 KMS 密钥:用你自己的密钥,方便做 Key Rotation 和权限隔离。
- 轮换测试要覆盖失败场景:我曾经遇到过 Lambda 执行角色权限不足,导致轮换失败,应用全部断连。一定在测试环境跑一遍完整的轮换流程。
- 应用端要缓存 Secret:每次请求都去 Secrets Manager 拿密码,不仅慢,还可能触发 API 限流。在内存或本地文件缓存 5 分钟,足够了。
- RDS 集成时注意网络:如果 RDS 在 VPC 内,轮换用的 Lambda 函数也必须部署在同一个 VPC 中,并且要有通往 RDS 的网络路径(通过安全组或 Nacl)。
小技巧:如果你使用 AWS CDK 或 Terraform 部署基础设施,可以直接在代码中定义 Secrets Manager 和 RDS 的集成。IaC 配合自动轮换,基本上能做到凭据的“零触碰管理”。