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 函数在替你干活。轮换策略一般分为两步或三步:

  1. 创建新版本(AWSPENDING):Lambda 函数生成一个新密码,并更新到 RDS 的用户中。
  2. 测试新版本(可选):用新密码做一次连接测试,确保密码生效。
  3. 切换版本:将 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

  1. 在 Secrets Manager 控制台,选择“存储新的机密”。
  2. 选择“RDS 数据库的凭据”。
  3. 选择你的 RDS 实例,输入数据库用户名(通常是你创建的管理员用户)。
  4. 设置轮换周期(比如 30 天),选择轮换用的 Lambda 函数(可以自动创建)。
  5. 保存后,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 调用。

一张图看懂核心逻辑

下面这张图是我画的核心架构,你看一遍应该就能理解全部流程了。

AWS Secrets Manager 核心架构与集成流程 AWS Secrets Manager IAM 策略 / 访问控制 KMS 密钥加密 Lambda 轮换函数 RDS / 数据库 应用(Lambda / EC2 / ECS) 🗝️ 加密存储 Secret 值加密后存入 DynamoDB 🔄 自动轮换流程 创建 → 测试 → 切换版本 访问控制 读取 Secret 轮换触发 密码更新

总结几个我的实际建议

  • 不要使用默认的 KMS 密钥:用你自己的密钥,方便做 Key Rotation 和权限隔离。
  • 轮换测试要覆盖失败场景:我曾经遇到过 Lambda 执行角色权限不足,导致轮换失败,应用全部断连。一定在测试环境跑一遍完整的轮换流程。
  • 应用端要缓存 Secret:每次请求都去 Secrets Manager 拿密码,不仅慢,还可能触发 API 限流。在内存或本地文件缓存 5 分钟,足够了。
  • RDS 集成时注意网络:如果 RDS 在 VPC 内,轮换用的 Lambda 函数也必须部署在同一个 VPC 中,并且要有通往 RDS 的网络路径(通过安全组或 Nacl)。

小技巧:如果你使用 AWS CDK 或 Terraform 部署基础设施,可以直接在代码中定义 Secrets Manager 和 RDS 的集成。IaC 配合自动轮换,基本上能做到凭据的“零触碰管理”。