部署一个需要与 Kubernetes API 交互的组件时,比如日志采集代理、CI/CD 流水线或自动扩缩容控制器,通常需要为它分配一个集群内身份。直接使用默认命名空间里的 default Service Account 往往权限不足,而粗暴地挂载 cluster-admin 又会让一个被入侵的 Pod 拥有删除所有资源的权力。正确的做法是单独创建 Service Account,再通过 Role 和 RoleBinding 把权限限制在最小范围。这篇文章将带你走完整个配置流程,并给出可以直接落地的 YAML 示例。

理解 Service Account、Role 与 RoleBinding 的关系
Kubernetes 基于角色的访问控制(RBAC)由四个核心对象组成:ServiceAccount、Role、ClusterRole、RoleBinding 和 ClusterRoleBinding。ServiceAccount 代表一个进程或工作负载的身份,它不属于人,而是属于运行在 Pod 中的应用程序。每一个 Pod 在创建时如果没有显式指定,会被自动关联到所在命名空间的 default ServiceAccount。Role 则是权限规则的集合,它声明了可以对哪些 API 资源执行哪些操作,例如允许对 pods 资源执行 get、list、watch。RoleBinding 把 ServiceAccount 和 Role 连接起来,使前者的调用请求能够通过后者的规则进行鉴权。
Role 与 ClusterRole 的区别主要体现在作用域上。Role 是命名空间级别的对象,创建时必须指定 namespace,它只能授权访问该命名空间内的资源。ClusterRole 是集群级别的,可以授权访问所有命名空间的资源,也可以访问节点、PersistentVolume、Namespace 等集群范围资源。对于大多数只服务于单个命名空间的工作负载,使用 Role 就足够了,这样即使权限泄露,影响范围也被限制在一个命名空间内。如果多个命名空间需要相同的权限集合,可以先创建一个 ClusterRole,再通过 RoleBinding 在每个命名空间中绑定同一个 ClusterRole,从而避免重复维护 Role 定义。
一条 Role 规则由 apiGroups、resources 和 verbs 三个字段构成。apiGroups 指定资源所属的 API 组,核心资源(如 pods、services、configmaps)使用空字符串 "",而 Deployment 属于 apps 组,Ingress 属于 networking.k8s.io 组。resources 列出资源名称,也可以用 resourceNames 进一步限制到具体的资源实例。verbs 是允许执行的动作,常见的有 get、list、watch、create、update、patch、delete。如果只想允许读取 Pod 但不允许修改,可以只写 get、list、watch。这种精确控制是实现最小权限的关键。
创建 Service Account 与 Role 的完整实践
先创建一个专门给日志采集器使用的 ServiceAccount。下面的 YAML 在 logging 命名空间中定义了一个名为 log-collector 的 ServiceAccount,同时禁用了默认的 token 自动挂载。禁用自动挂载是一个好习惯,只有当 Pod 模板中显式声明 serviceAccountName 时才挂载 token,避免无关 Pod 意外携带该身份。
apiVersion: v1 kind: ServiceAccount metadata: name: log-collector namespace: logging automountServiceAccountToken: false
接下来定义 Role。假设日志采集器只需要读取 logging 命名空间内的 Pod 和 ConfigMap,以便获取应用状态和采集配置。Role 中应当只声明这两个资源以及只读 verbs。注意 apiGroups 对于核心资源要写作空字符串,在 YAML 里可以表示为 "",不能省略成 apps。下面这份 Role 只授予 get、list、watch 权限,不包含任何写操作。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: log-reader
namespace: logging
rules:
- apiGroups: [""]
resources: ["pods", "configmaps"]
verbs: ["get", "list", "watch"]
如果把规则写错成允许 configmaps 的 update 权限,那么日志采集器理论上就可以修改应用配置,这显然超出了它的职责。很多人感觉只读权限不够用,就顺手加上 * 号授予所有 verbs,这种粗放的做法会让 service account 变成一个潜在的提权入口。因此在编写 Role 时,建议先列出应用实际调用的 API 方法,再反向推导所需的 verbs,而不是先给宽泛权限再逐步收窄。
最后创建 RoleBinding,把刚才的 Role 绑定到 log-collector ServiceAccount。RoleBinding 也必须位于同一个命名空间 logging 中。subjects 字段里 kind 要写成 ServiceAccount,name 对应 ServiceAccount 的名称。roleRef 部分指定绑定的是 Role 而不是 ClusterRole,所以 kind 必须为 Role,apiGroup 固定为 rbac.authorization.k8s.io。
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: log-collector-binding
namespace: logging
subjects:
- kind: ServiceAccount
name: log-collector
namespace: logging
roleRef:
kind: Role
name: log-reader
apiGroup: rbac.authorization.k8s.io
三个对象可以放在同一个 YAML 文件中用 --- 分隔,也可以拆成三个文件分别应用。推荐使用 kubectl apply -f 一次性创建,因为这样能保证 ServiceAccount、Role、RoleBinding 的创建顺序一致。创建完成后,在 Pod 模板中通过 serviceAccountName: log-collector 指定身份,同时把 automountServiceAccountToken 设为 true 或者直接省略该字段,即可让 Pod 挂载该身份的 token。
验证权限与最小权限最佳实践
权限配置完成后,不要直接部署业务就认为万事大吉。Kubernetes 提供了 kubectl auth can-i 命令来模拟某个身份发起请求,验证其是否具备特定权限。例如要确认 log-collector 这个 ServiceAccount 能否读取 logging 命名空间中的 pods,可以执行:
kubectl auth can-i get pods -n logging --as=system:serviceaccount:logging:log-collector
如果返回 yes,说明只读权限已经生效。如果返回 no,则需要检查 Role 的 namespace、RoleBinding 的 roleRef 指向以及 subjects 中的名称是否正确。还可以继续测试越权操作,比如执行 kubectl auth can-i delete pods -n logging --as=system:serviceaccount:logging:log-collector,应该返回 no。这种反证测试能有效防止把多余权限授予 ServiceAccount。
另外要注意,ServiceAccount 的 token 会以 Secret 形式挂载到 Pod 内部,任何能进入该 Pod 的人都可以读取 token 并以此身份访问 API。因此除了权限最小化,还应当结合 NetworkPolicy 限制 API Server 访问来源,使用 PodSecurity 标准阻止特权容器,并定期审计每个 ServiceAccount 实际使用的权限。例如可以使用 kubectl get rolebindings --all-namespaces -o yaml 配合脚本统计每个 SA 绑定了哪些 Role,发现长期未使用的绑定及时清理。
一个常见误区是把所有自动化任务共用一个 ServiceAccount,这样一旦需要撤销某个任务权限,就会影响其他任务。更合理的方式是按职责拆分,例如 CI 流水线使用 cicd-runner,日志采集使用 log-collector,监控代理使用 metrics-agent。每个 SA 单独绑定最小 Role,即使某个组件的 token 泄露,攻击者也无法横向移动到其他资源的写入操作上。这种隔离思维和最小权限原则相辅相成,能显著降低集群面临的安全风险。
排查绑定不生效的常见原因
当你完成上述配置后,Pod 仍然返回 403 Forbidden,通常可以从以下几个方面排查。首先检查 RoleBinding 是否创建在正确的命名空间中。Role 和 RoleBinding 必须与被访问资源处于同一个命名空间,如果 Pod 运行在 default 命名空间却去读取 logging 命名空间的 Pod,即使 RoleBinding 指定了 logging 命名空间,也会因为 Pod 的 ServiceAccount 不在该命名空间而无法生效。此时需要为 default 命名空间中的 ServiceAccount 创建对应的 RoleBinding,或者让 Pod 使用 logging 命名空间下的 ServiceAccount。
其次检查 API 组字符串是否写对。核心资源的 apiGroups 必须是空字符串 "",写成 ["*"] 或 ["core"] 都不会匹配。deployments 的 apiGroups 是 ["apps"],ingresses 是 ["networking.k8s.io"],custom resources 则需要查找具体 CRD 定义的 group。可以通过 kubectl api-resources 查看某个资源所属的 API 组和资源名,避免拼写错误导致规则不命中。
新版 Kubernetes 中默认启用了 TokenRequest API,ServiceAccount token 的生命周期为 1 小时,而不是以前的长期有效 Secret。如果应用依赖长期 token 并频繁续期,需要正确使用客户端库自动刷新 token。如果手动复制 token 到集群外部使用,应当使用 kubectl create token log-collector -n logging 生成临时 token,而不是从 Secret 中提取。理解这些机制能够帮助你在遇到鉴权失败时快速定位是 token 问题还是 RBAC 配置问题。
最后建议把 RBAC 配置纳入 Git 仓库管理,通过 CI 自动执行 kubectl apply,并在合并前由代码评审检查每个 Role 的 verbs 是否最小。对于生产集群,还可以启用 Kubernetes 审计日志,记录所有 ServiceAccount 的 API 调用,定期分析是否有异常访问模式。将权限管理作为基础设施代码的一部分,才能让最小权限策略持续有效。
Kubernetes RoleService AccountRBAC修改时间:2026-08-24 00:29:10