第一原则:永远不要 commit 到 git
# 一秒钟泄露:
git add .env && git commit -m "config" && git push
哪怕仓库 private——历史一旦提交,就要按"已经泄露"处理。所有出现过的密钥立即作废 + 轮换。
防护手段:
.gitignore加.env*.pem*.key等- 装 pre-commit hook 或 gitleaks
- GitHub / GitLab 的 secret scanning 自动开
第二原则:知道自己有多少密钥
完整盘点一次:"我们有多少 secret?分别是谁、多少地方在用?"——多数公司答不出。
把所有 secret 编目(哪个服务、什么用途、何时创建、谁是 owner、上次轮换时间)——这是密钥管理的起点。
工具谱
| 层级 | 工具 |
|---|---|
| 本地开发 | direnv + .envrc.local(不进 git) |
| CI | GitHub Actions Secrets / GitLab CI Variables |
| K8s | K8s Secret(基础)/ Sealed Secrets / External Secrets Operator |
| 应用运行时 | HashiCorp Vault / AWS Secrets Manager / GCP Secret Manager / Azure Key Vault / 阿里 KMS |
| 企业级 | HashiCorp Vault Enterprise / CyberArk / 1Password Business |
不需要全部都用——起步可以只是云商的 Secrets Manager + GitHub Secrets。
短期凭据 > 长期凭据
长期凭据:access_key_id + secret_access_key(永远有效)
短期凭据:1 小时过期的 STS token / Workload Identity 自动签发
短期凭据的好处:
- 泄露窗口小
- 不需要"轮换"——本来就过期
- 自动绑定 Workload Identity,权限审计清晰
云商方案:
- AWS:IRSA(IAM Roles for Service Accounts)让 K8s Pod 直接拿 STS token
- GCP:Workload Identity 把 K8s ServiceAccount 映射到 GCP Service Account
- Azure:Workload Identity(原 Pod Identity 已废弃)
- 阿里 / 腾讯:RAM Role / CAM Role for Pod
新项目优先用短期凭据。长期 access key 本身就是反模式。
轮换(Rotation)
- 数据库密码 / API 凭据:定期轮换(年 / 季度)
- TLS 证书:自动续签(Let's Encrypt / ACM 都自动)
- 应用层 API key:每次 commit 检测;离职 / 怀疑泄露时立即轮换
- 短期凭据:自动过期,无需手动
轮换的难点是"应用怎么平滑切换"——支持双密钥并存期:
T0:新密钥 K2 加进 secret store,应用同时接受 K1 + K2
T1:所有客户端切到 K2
T2:作废 K1
很多 secret store(Vault / Secrets Manager)支持"两个版本同时有效"+ 渐进切换。
加密 at-rest 与 in-transit
- at-rest:磁盘上的数据加密(云盘加密、数据库 TDE、S3 SSE)
- in-transit:传输中加密(TLS)
两者都是基础——不是"加分项"。云上几乎免费就能开,没理由不开。
几个常见坑
echo $TOKEN进 CI 日志:log 里就是明文。CI 工具自动 mask 已声明的 secret,但 echo 后被处理过的字符串可能漏- secret 写进 K8s ConfigMap:K8s 区分 ConfigMap / Secret,不要混用
- Docker image 里塞 secret:build 历史会留下来
- shell history 里:
mysql -p mypassword——password 直接进 history。用~/.my.cnf或--password-file - 同一个 key 多个用途:一处泄露全线作废
应用怎么读 secret
设计原则:注入到环境变量或文件里,应用代码不知道 secret store 存在。
# 反模式:应用直连 Vault
client = hvac.Client()
secret = client.secrets.kv.read("payment/api-key")
# 推荐:环境变量 / 文件,由编排层注入
api_key = os.environ["PAYMENT_API_KEY"]
注入工具:
- K8s + ESO / Sealed Secrets
- Vault Agent Injector(K8s mutating webhook)
- 云商 SDK + Pod 启动 hook
应用代码与具体 secret 实现解耦——换 store 就不用改代码。
推荐阅读
- HashiCorp Vault — 业界引用最多的 secret 管理
- 12-Factor: Config — 配置在环境,不在代码
- AWS IAM Roles Anywhere / IRSA — 短期凭据范式
- Sigstore — 软件签名(下一篇延伸)
- OWASP Secrets Management Cheat Sheet
下一篇:扩到镜像和依赖——软件供应链安全。