AWS Secrets Manager 详解

AWS Secrets Manager 是什么?说白了,就是 AWS 帮你管密码、、数据库连接串这些敏感信息的托管服务。我刚开始接触多云项目时,团队还在手动把密钥写进环境变量,结果一次误操作把生产库密码打到了日志里……嗯,从那以后,我坚定地上了 Secrets Manager。

它解决了什么问题?

先想想平时你怎么管密钥:写在代码里?不安全。存在配置文件里?容易泄漏。用 KMS 加密后存 S3?轮换时还得写一堆脚本。Secrets Manager 把这些痛点一锅端了:

  • 集中存储:所有密钥放一个地方,不用再翻几十个文件。
  • 自动轮换:设置好轮换周期,它自己换,不用半夜爬起来改密码。
  • 细粒度访问控制:结合 IAM,谁可以读哪个密钥,清清楚楚。
  • 审计追踪:每次读密钥都记在 CloudTrail 里,出了问题可以溯源。

核心概念:Secret 与 Secret Value

在 Secrets Manager 里,一个“Secret”是一个逻辑容器,包含元数据(名称、标签、描述、轮换配置)和实际的“Secret Value”。Secret Value 可以是任意字符串,但最常见的是 JSON 格式,比如:

{
  "username": "admin",
  "password": "SuperSecret!2025",
  "host": "db.example.com",
  "port": 3306
}

我个人习惯把同一个服务的所有连接信息放在一个 Secret 里,用 JSON 组织,这样读取时一次拿到全部,避免多次 API 调用。

自动轮换机制

自动轮换是 Secrets Manager 最吸引人的功能。它怎么做到的?通过一个 Lambda 函数。AWS 提供了针对 RDS、Redshift、DocumentDB 等服务的预置轮换模板,也可以自己写。

我的经验:第一次配轮换时,记得先测试 Lambda 函数的 VPC 配置。如果 Lambda 连不上数据库,轮换会失败,而且会把旧密码删掉——那时你就得手动重置数据库了。我曾经吃过这个亏,后来养成了“先手动轮换一次”的习惯。

轮换流程大致是:

  1. Secrets Manager 调用 Lambda,生成一个新密码。
  2. Lambda 用当前“待定”版本更新数据库。
  3. 验证新密码可用,然后将新版本标记为“当前”,旧版本标记为“以前”。
  4. 应用可以在过渡期内同时使用新旧两个版本,避免断连。

这里有个关键细节:切换不是瞬间的,中间有短暂的“双版本”共存窗口。如果你的应用只认一个密码,需要做好重试机制。

访问控制:IAM 与资源策略

Secrets Manager 支持两种策略:

  • IAM 策略:附加给用户、角色,控制他们能访问哪些 Secret。
  • 资源策略:直接挂在 Secret 上,控制谁能访问这个 Secret,甚至可以跨账号。

我建议优先用 IAM 策略,因为它更符合最小权限原则。资源策略适合跨账号场景,比如你需要让另一个 AWS 账号里的应用读取你的密钥。

策略类型适用场景示例
IAM 策略同账号下用户/角色允许某 EC2 实例的角色读取特定 Secret
资源策略跨账号或公开访问允许另一账号的 Lambda 访问本账号的 Secret
注意:资源策略中如果写“Principal”: “*”,等于公开了你的密钥!千万不要在生产环境这么做。我曾经见过有人为了省事,直接开放给所有人,结果被爬虫扫到,一天内密钥就被滥用了。

如何用 SDK 读取 Secret

来看一段 Python 代码,这是最常用的方式:

import boto3
import json

def get_secret(secret_name):
    client = boto3.client('secretsmanager')
    try:
        response = client.get_secret_value(SecretId=secret_name)
        secret_string = response.get('SecretString')
        if secret_string:
            return json.loads(secret_string)
        else:
            # 如果是二进制密钥,走 SecretBinary
            return response.get('SecretBinary')
    except Exception as e:
        print(f"读取密钥 {secret_name} 失败: {e}")
        raise

注意 get_secret_value 这个 API 会返回整个 Secret 的当前版本。如果启用了多版本,你可以指定 VersionStage 参数来获取历史版本。我个人只在回滚场景下才用历史版本,平时不要依赖它,因为轮换后旧版本会逐渐被清理。

性能与成本考量

Secrets Manager 的 API 调用是按次数计费的。每次读取大概 0.05 美元/万次,看着便宜,但如果你在热搜路径里每毫秒都读一次,账单会很难看。建议:

  • 在应用启动时读取一次,缓存在内存中,定时刷新。
  • 不要每次请求都去读 Secret,除非密钥会高频轮换。
  • 利用 SDK 内置缓存,比如 AWS Lambda 扩展的 Secrets Manager 缓存层。
实际案例:我排查过一个线上抖动问题,数据库连接偶尔超时。最后发现是应用每 5 秒重新读一次 Secret,而 Secrets Manager 的 API 调用在某些区域延迟超过 100ms,导致连接池初始化阻塞。改成启动时读一次、每小时刷新后,问题消失。

Secrets Manager 整体架构

下面这张图展示了核心工作流:

AWS Secrets Manager 核心架构 应用 / 用户 SDK + IAM 鉴权 Secrets Manager (加密存储) KMS 加密密钥 CloudTrail 审计 轮换 Lambda 目标服务 (RDS等) 读取时自动解密 所有操作被审计

避坑指南(实测过的)

用 Secrets Manager 时,有几个坑我反复踩过,列出来希望你能跳过:

  • 不要直接使用根账号密钥:Secrets Manager 本身不存储 AWS 根账号的密钥,但如果你把 IAM 用户的 Access Key 当作 Secret 存进去,记得也要自动轮换,否则泄露后很难收场。
  • 注意 VPC 端点:如果你的应用在 VPC 内且没有公网访问,必须创建 Secrets Manager 的 VPC 端点,否则 API 调用会超时。我见过运维小伙子配了三天没搞定,最后发现忘了加端点策略。
  • 轮换超时问题:默认轮换 Lambda 超时是 15 分钟,如果你的数据库在另一个区域,网络延迟加上 SSL 握手可能超时,建议改成 5 分钟以上,或者把 Lambda 部署在离数据库近的区域。
  • 版本管理:Secrets Manager 最多保留 100 个版本,超过后会删除最旧的。如果你的轮换频率很高(比如每 5 分钟一次),最好调大保留数量,或者关掉自动清理。
曾经有一次,我把一个数据库密钥的轮换周期设成了 1 天,结果开发环境每天凌晨 2 点瞬间断连,因为应用没有缓存机制,轮换那几秒钟内新旧密码交替,部分请求拿到了旧密码。后来我调整了轮换策略,让新旧版本共存 5 分钟,才解决这个问题。

总结性建议

AWS Secrets Manager 不是银弹,但相比自建 Vault 或明文存储,它省心太多了。我现在的习惯是:所有多云项目的密钥,统一通过 AWS Secrets Manager 管理,然后再用 Terraform 或 Crossplane 同步到其他云。至于怎么同步,那是后面章节的事。

嗯,最后补一句:永远不要把 Secret 打出来或者写进日志。我在代码审查时遇到过同事为了调试,把整个 Secret JSON 打印出来,结果被运维告警系统捕获……嗯,那场面挺尴尬的。