背景
我负责一套 K8s 1.28 集群(3 节点,VPC 内),跑着 20 个微服务。每个服务平均 5~8 个密钥:MySQL 密码、Redis 密码、Kafka SASL 密码、第三方 API Key(支付、短信、地图)。
现状:全部用 K8s Secret 管理,base64 编码后存在 etcd 里。RBAC 配的是"SA 只能读自己 namespace 的 secrets",etcd 开了 TLS + 认证。
最近一次安全审计,安全团队提了一条:"K8s Secret 不满足等保三级『密钥不应以可恢复形式存储』的要求,建议引入 Vault 或云 KMS。"
我团队第一反应是:"我们 VPC 内网、etcd 有 auth、RBAC 也收紧了,外部攻击者根本进不来,上 Vault 是不是过度工程?"
这个分歧到现在没统一。今天抛出来,想听听大家怎么选的。
常见分歧(我拆成三个阵营)
① "K8s Secret 够用"派
- 威胁模型里根本没有"集群内攻击者"——VPC 隔离 + etcd auth + RBAC,外部打不进来;
- Vault 是一个新的 SPOF:Vault 挂了,20 个服务全部起不来(启动时要从 Vault 拿密钥);
- 运维成本翻倍:Vault Raft 要 3 节点、要 HA、要备份、要有 unseal 流程、要有 on-call 预案;
- "我们 on-call 就 2 个人,维护 Vault 比维护业务还重。"
② "必须 Vault / KMS"派
- K8s Secret 的 RBAC 粒度是 namespace 级,不是"密钥级"。一个泄露的 SA token = 整个 namespace 所有密钥。打个比方:你把 20 个服务的保险柜全放在一个房间里,房间门只有一把钥匙,门一开全没了;
- CI/CD 是最大风险面:你的 GitLab CI / Jenkins 有 deploy 权限 → 有 secrets 读取权限 → 一次 CI runner 被注入(比如某个恶意 npm 包 / 供应链投毒)= 所有密钥泄露;
- on-call 的 kubectl 权限:任何一个拿到
kubectl get secrets权限的人(包括外包、实习生、离职流程中的人),都能拿到全部密钥; - K8s audit log 默认是
None,你甚至不知道"谁在什么时候读了哪个 secret"; - 等保 / ISO27001 / SOC2 审计时,"密钥以 base64 存在 etcd"这一条大概率过不了。
③ "中间路线"派
- 不上 Vault,但用 Sealed Secrets(Bitnami)或 External Secrets Operator(ESO):密钥在 Git 里是加密的(sealed / external ref),etcd 里仍然是 K8s Secret,但 Git 仓库泄露 ≠ 密钥泄露;
- 或者用 SOPS + age:
.yaml文件里密钥是加密的,只有持有 age key 的人能解; - 成本:比裸 Secret 多一步 CI 里的 decrypt 步骤,但不引入新 SPOF。
我的倾向
我倾向于按"信任边界"来选,而不是按"用不用 Vault"来选。
打个比方:K8s Secret 就像小区门禁——门禁的"安全"建立在"小区围墙 = 信任边界"上。围墙里的人你信,围墙外的人你防。Vault 则是每户人家自己的保险柜——即使进了小区,每户还要各自开锁。
具体决策框架:
| 条件 | 建议 | |---|---| | 单团队、≤30 服务、VPC 内、无多租户、on-call ≤5 人、无硬合规 | K8s Secret + RBAC 收紧 + K8s audit log 开启(Policy 级别) + CI 密钥最小化(只给 deploy namespace 的 secrets 权限) | | 多团队共享集群、有外包/实习生接触 kubectl、有等保/ISO 合规、密钥需跨集群复用 | Vault(Raft 3 节点)或云 KMS(AWS KMS / 阿里云 KMS) | | 在两者之间、预算有限 | Sealed Secrets / ESO + SOPS,Git 里不存明文,etcd 里仍是 K8s Secret(但 Git 泄露 ≠ 密钥泄露) |
关键不是"用不用 Vault",而是"一个泄露的 SA token 的爆炸半径有多大"。
如果你现在跑 kubectl auth can-i list secrets -n production,能 list 的人超过 3 个,那你的"信任边界"就已经画到"任何有 kubectl 的人"了——这时候 K8s Secret 的"安全"就名存实亡了。
一个具体的"爆炸半径"检查清单(建议每个团队跑一遍):
# 1. 谁能读哪个 namespace 的 secrets?
kubectl auth can-i list secrets --all-namespaces
kubectl get clusterrolebindings -o json \
| jq '.items[] | select(.roleRef.name | contains("secret"))'
# 2. CI/CD 的 SA 权限范围
kubectl get rolebindings -n <ci-namespace> -o yaml
# 检查:CI 的 SA 是否只有 deploy namespace 的 secrets 权限?还是 cluster-admin?
# 3. K8s audit log 是否开启?
# 检查 audit-policy 是 None / Metadata / Request / RequestResponse 哪一级
# 如果是 None 或 Metadata,你根本不知道"谁读了哪个 secret"
# 4. etcd 的访问控制
# 检查:etcd 是否有 --client-cert-auth=true?TLS 双向认证?
# 如果 etcd 是明文 TCP(即使内网),任何 Pod 都能 etcdctl get /registry/secrets/...
# 5. 密钥轮换频率
# 检查:你的 MySQL 密码、Kafka 密码上次轮换是什么时候?
# K8s Secret 没有"到期"概念,一个密钥用 3 年和用 3 个月的安全等价性完全不同
想听大家
- 你的团队怎么管 K8s 密钥? 裸 Secret / Sealed Secrets / Vault / 云 KMS / SOPS?用的哪个,为什么?
- 你的 on-call 流程里,"拿到 kubectl"和"拿到密钥"是同一个动作吗? 还是说有审批、有审计、有时间窗口?
- CI/CD 的密钥隔离你做到什么粒度? 是"一个 pipeline 一个 SA"还是"全公司一个 SA"?
- 如果你要给一个 5 人团队、10 个服务的集群推荐一个"密钥管理方案"(预算有限、不想上 Vault),你会选什么?
- Vault 的 SPOF 问题你们怎么解的? Raft 3 节点?还是接受单点 + 离线备份?unseal 流程谁执行?