GCP Secret Manager:创建、版本与权限

GCP 的 Secret Manager,说白了就是一个专门存敏感信息的保险柜。跟你在本地配置文件里写死密码不同,它把密钥、API 、数据库连接串这些东西集中管理,还带审计和版本控制。

我个人习惯在项目启动第一天就把 Secret Manager 建好。你想想看,等微服务都跑起来了,再回头改密钥管理方案,那得多折腾。

创建 Secret:两种方式都试试

GCP 控制台和 gcloud 命令行都能创建。我一般用 gcloud 写脚本,方便重复执行。

gcloud secrets create my-db-password \
  --replication-policy="automatic" \
  --labels="env=prod,team=backend"
经验之谈:--labels 参数别看小,项目大了你就知道有多重要。有一次我接手一个团队的项目,Secret 没打标签,几十个密钥根本分不清是给哪个服务用的。

创建完 Secret 本身,你还得把值写进去:

echo -n "SuperSecret123!" | \
  gcloud secrets versions add my-db-password --data-file=-

这里注意 -n 参数。不加的话,echo 会补一个换行符,很多系统不认识带换行的密码。我之前在这个坑里栽过跟头,排查了半天才发现是换行符在捣鬼。

版本管理:默认就是 Git 思维

Secret Manager 的版本管理很简单,每次 versions add 都会生成一个新版本号。版本从 1 开始递增。默认情况下,最新版是 enabled 状态。

为什么会需要版本管理?我记得有一次线上出故障,调用的 突然失效了。排查后发现是运维更新 Secret 时写错了值。好在 GCP 保留了旧版本,一键回滚就恢复了。

版本状态有四种:

状态 含义 能读取吗
enabled 正常可用 是
disabled 手动禁用 否
destroyed 永久删除(不可恢复) 否
scheduled_destroy 设定在未来某个时间删除 否
⚠️ 千万别手滑:destroyed 状态的版本是彻底没了的,GCP 不会像回收站那样让你恢复。我曾经在测试环境里想清理旧版本,结果误删了生产环境的 Secret,还好有备份,不然就尴尬了。

IAM 角色:权限必须最小化

Secret Manager 的 IAM 权限分得很细。常跟这几个角色打交道:

  • Secret Manager Admin(roles/secretmanager.admin):完全控制,包括删除。一般只给 SRE 或平台组。
  • Secret Manager Viewer(roles/secretmanager.viewer):只能看 metadata(比如版本列表),读不到实际值。
  • Secret Manager Secret Accessor(roles/secretmanager.secretAccessor):只读,能拿到 Secret 的值。给应用服务用。
  • Secret Manager Secret Version Manager(roles/secretmanager.secretVersionManager):管理版本状态(启用/禁用/销毁),但不能读值。

我建议给每个微服务单独建一个 Service Account,只授予它需要的那几个 Secret 的 Accessor 权限。别偷懒搞个全局角色。有一次我为了省事,给所有服务绑了一个 Admin 角色,结果被安全审计通报了,还写了整改报告。

API 使用:REST 与 gRPC 我都要

Secret Manager 提供 REST 和 gRPC 两种 API。REST 方式更通用,用 curl 就能调:

curl -H "Authorization: Bearer $(gcloud auth print-access-)" \
  https://secretmanager.googleapis.com/v1/projects/my-project/secrets/my-db-password/versions/1:access

返回的 payload 是 base64 编码的,需要自己 decode。代码里集成可以直接用 Google 的客户端库,比如 Python:

from google.cloud import secretmanager

client = secretmanager.SecretManagerServiceClient()
name = "projects/my-project/secrets/my-db-password/versions/latest"
response = client.access_secret_version(request={"name": name})
secret_value = response.payload.data.decode("UTF-8")
核心建议:版本号用 "latest" 别名。这样更新 Secret 时应用不需要改代码,上线新版本后应用自动读取最新值。不过要注意,latest 指向的是 enabled 状态的最新版本,不是所有版本里最新的那个。如果最新版被禁用了,latest 会指向上一个 enabled 版本。

这里有个容易忽略的点:API 调用是有配额的。GCP 默认每分钟每个项目可以调用 6000 次 AccessSecretVersion。如果你在容器启动时并发读取大量 Secret,很可能被限流。我在一次新服务上线时就碰到了,还好提前做了本地缓存,不然启动都启动不了。

一张图看懂全局

下面这个图把 Secret 的生命周期、版本流转、权限分层串起来了。我个人建议你把它存下来,排查问题的时候对照着看,思路会清晰很多。

GCP Secret Manager 架构概览 GCP Secret Manager 核心架构 Secret 创建 版本管理 版本1 (enabled) ← 版本2 (disabled) ← 版本3 (latest) 每次 add 生成新版本 | 支持回滚、禁用、定时销毁 ⚠️ destroyed 状态不可恢复 IAM 权限分层 Admin(全控制) → Viewer(只读元数据) SecretAccessor(只读值) → VersionManager(管理版本) API 访问方式 REST API(curl / HTTP) gRPC(客户端库 Python / Java / Go) Console UI 最佳实践 1. 使用 latest 别名 2. 按服务分 SA 3. 加标签分类 4. 本地做缓存

注意配额与审计

GCP Secret Manager 的 API 配额虽然默认不低,但如果你用 CI/CD 流水线大批量读取,还是有可能触发限流。我一般建议在应用层做本地缓存,比如内存缓存 5 分钟,避免每次请求都去调 API。

还有审计日志。Secret Manager 的所有操作都会写入 Cloud Audit Logs。Security Team 可以监控谁在什么时间读取了哪个 Secret。如果你发现某个 Service Account 频繁读取大量 Secret,那就要小心了——可能是密钥泄露的前兆。

我的习惯:每次新项目启动,我都会先检查 Secret Manager 的 IAM 角色分配是否合理。权限最小化这个原则,在密钥管理上尤其重要。宁可多建几个角色,也别给一个万能角色。