3. Secret的访问控制与RBAC
在 Kubernetes 中,Secret 本身只是“被加密存储的键值数据”,真正决定“谁能读、谁能改、谁能创建”的,是 API Server 的认证与授权链路。理解 Secret 的访问控制,核心不是记住某个 YAML 字段,而是搞清楚一条请求从“我是谁”到“我能不能做这件事”的完整判定过程。这条链路可以拆成三个层次:身份(Authentication)、授权(Authorization)、准入(Admission)。Secret 的安全问题,绝大多数出在授权层配置过宽,而不是加密本身。
身份从哪里来:ServiceAccount 与用户
Kubernetes 的请求主体分两类:普通用户(User)和服务账户(ServiceAccount,简称 SA)。普通用户由外部身份系统(证书、OIDC、Webhook)认证,Kubernetes 不存储其对象;ServiceAccount 则是集群内的 API 对象,专为 Pod 中的进程设计。当 Pod 以某个 SA 运行时,kubelet 会把该 SA 的令牌投射到容器内(现代集群使用 TokenRequest 生成短期、受众受限的令牌),Pod 内的客户端凭此令牌访问 API Server,API Server 解析出用户名形如 system:serviceaccount:<namespace>:<name>,并附带一组组名,如 system:serviceaccounts 和 system:serviceaccounts:<namespace>。
这里有一个关键点:Pod 与 Secret 的“绑定”并不是通过某个字段直接声明的,而是通过 SA 的授权间接实现的。Pod 使用哪个 SA,就继承了该 SA 被授予的权限。因此,Secret 访问控制的第一原则是:不要让工作负载默认使用高权限 SA。如果 Pod 未指定 serviceAccountName,它会使用所在命名空间的 default SA。很多集群的 default SA 被误授予了过宽权限,这是最常见的隐患之一。
授权:Role/RoleBinding 与 ClusterRole/ClusterRoleBinding
授权由 RBAC 完成,四个对象两两配对:
- Role:命名空间级权限集合,只能授予本命名空间内资源的操作。
- RoleBinding:把 Role 绑定到主体(User、Group、ServiceAccount),作用范围限于该命名空间。
- ClusterRole:集群级权限集合,可授予集群级资源(如 nodes、persistentvolumes),也可授予命名空间资源但跨所有命名空间生效。
- ClusterRoleBinding:把 ClusterRole 绑定到主体,作用范围是整个集群。
对于 Secret,RBAC 的 resources 是 secrets,verbs 常见为 get、list、watch、create、update、patch、delete。需要特别注意的是:list 和 watch 会暴露 Secret 的内容。即使没有 get,只要拥有 list,调用 kubectl get secrets -o yaml 就能拿到全部 Secret 的 data 字段。因此,最小权限原则要求:除非确有必要,不要授予 list/watch,只授予对特定 Secret 名称的 get。
下面这张图展示了从 Pod 到 Secret 的访问判定链路,以及 RBAC 四个对象在其中的位置:
最小权限原则的落地写法
把上面的判定链路落到 YAML,一个“只读单个 Secret”的 Role 应该这样写:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: production
name: secret-reader-binding
subjects:
- kind: ServiceAccount
name: app-reader
namespace: production
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
这段配置的关键在于 resourceNames 与 verbs: ["get"] 的组合:它把权限精确限制到“production 命名空间下名为 db-credentials 的 Secret,且只能 get”。如果去掉 resourceNames,该 SA 就能读取该命名空间内所有 Secret;如果把 get 换成 list,则即使没有 get,也能通过列表接口拿到全部内容。这是 RBAC 中一个容易被忽视的“权限放大”效应。
常见配置错误与排查
实际运维中,Secret 访问控制的高频错误集中在以下几类:
- 默认 SA 权限过大:很多团队为图方便,把
defaultSA 绑定到cluster-admin或包含secrets: ["*"]的 ClusterRole。一旦某个 Pod 被攻破,攻击者即可读取全集群 Secret。 - 使用通配符:
resources: ["*"]或verbs: ["*"]会隐式包含 secrets 的 list/watch,权限边界完全失控。 - ClusterRoleBinding 误用:把命名空间级需求写成 ClusterRoleBinding,导致权限扩散到所有命名空间。应优先使用 RoleBinding 绑定 ClusterRole,把作用域限制在单个命名空间。
- 忽略 list/watch 的泄露:只检查
get而忘记list,导致“看似只读一个,实际能读全部”。 - SA 令牌长期有效:旧版集群使用不过期的 Secret 令牌,一旦泄露长期可用。应启用
TokenRequest投射短期令牌,并设置合理的过期时间。
排查时,可以用 kubectl auth can-i 做快速验证。例如,模拟某个 SA 是否能读取指定 Secret:
kubectl auth can-i get secrets/db-credentials \
--as=system:serviceaccount:production:app-reader \
-n production
# 检查是否意外拥有 list 权限
kubectl auth can-i list secrets \
--as=system:serviceaccount:production:app-reader \
-n production
如果第二条返回 yes,说明该 SA 能列出命名空间内全部 Secret,需要回到 Role 定义中移除 list 或补上 resourceNames 限制。此外,kubectl get role,rolebinding,clusterrolebinding -A -o yaml 配合 grep secrets 可以快速定位所有涉及 Secret 的授权规则,是审计时的常用手段。
secrets 且 verbs 含 list、watch 或 * 的 Role/ClusterRole,都应在合并前人工复核。把权限审计左移,比事后排查更有效。总结来看,Secret 的访问控制不是“给 Secret 加密”就结束,而是要把身份、授权、作用域三者对齐:Pod 用最小权限的 SA,SA 通过 RoleBinding 获得限定名称的 get 权限,避免 ClusterRoleBinding 与通配符带来的权限扩散。做到这一点,Secret 才真正处于可控的访问边界之内。