痛点
生产环境里最常见的 Kubernetes 安全隐患不是漏洞,而是 RBAC 权限膨胀。
典型场景:开发团队要部署服务,运维图省事直接给了 cluster-admin;CI/CD Pipeline 的 ServiceAccount 拥有全命名空间的 * 权限;离职员工的 kubeconfig 三个月没回收。一旦某个 Pod 被攻破或凭证泄露,攻击者直接横向移动拿下整个集群。
CNCF 2025 安全报告显示,67% 的 Kubernetes 安全事件与过度授权的 RBAC 配置直接相关。最小权限原则人人会说,但真正落地需要系统化方法。
方案
用 5 步渐进式收紧策略,在不影响业务的前提下,将 RBAC 从"全放行"演进到精确控制:
- 审计现有权限 → 2. 消除 cluster-admin 滥用 → 3. 按命名空间隔离 → 4. 细化 verb/resource → 5. 持续监控 + 自动告警
实操步骤
第一步:审计现有权限 — 找出谁拥有过多权限
使用 kubectl-who-can 和 rakkess 工具快速审计:
# 安装 kubectl-who-can 插件
kubectl krew install who-can
# 查看谁有权限删除全集群的 Pod
kubectl who-can delete pods --all-namespaces
# 列出所有绑定了 cluster-admin 的主体
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.roleRef.name=="cluster-admin") |
.subjects[]? | "\(.kind)/\(.name) in \(.namespace // "cluster-wide")"'
# 导出所有 ServiceAccount 及其绑定的 Role
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | \
jq -r '.items[] | select(.subjects[]?.kind=="ServiceAccount") |
"\(.metadata.namespace // "cluster")/\(.metadata.name) -> \(.roleRef.name)"'
重点关注:绑定了 cluster-admin 的 ServiceAccount、拥有 * verb 的 Role、跨命名空间的 ClusterRoleBinding。
第二步:消除 cluster-admin 滥用 — 替换为精确 ClusterRole
为 CI/CD Pipeline 创建专用 ClusterRole,只授予实际需要的权限:
# ci-deployer-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ci-deployer
rules:
# 只允许管理 Deployment 和 Service
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "configmaps"]
verbs: ["get", "list", "create", "update", "patch"]
# 允许查看 Pod 状态(用于部署验证)
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
# 禁止:delete pods、exec、secrets 写入、node 操作
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-deployer-binding
namespace: production
subjects:
- kind: ServiceAccount
name: ci-pipeline
namespace: ci-system
roleRef:
kind: ClusterRole
name: ci-deployer
apiGroup: rbac.authorization.k8s.io
注意这里用 RoleBinding 而非 ClusterRoleBinding,将权限限定在 production 命名空间。
第三步:按命名空间隔离 — 每个团队一个边界
# team-backend-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: team-backend-dev
namespace: backend
rules:
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods", "pods/log", "pods/exec"]
verbs: ["get", "list", "watch", "create"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update"]
# 明确禁止访问 secrets(敏感信息通过 External Secrets Operator 注入)
# 不列出 secrets 即为禁止
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-backend-dev-binding
namespace: backend
subjects:
- kind: Group
name: "team-backend"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: team-backend-dev
apiGroup: rbac.authorization.k8s.io
关键原则:开发只给所属命名空间权限,secrets 单独管控,exec 权限仅限非生产环境。
第四步:细化动态权限 — 用 aggregated ClusterRole 实现灵活组合
# 基础只读权限(所有团队继承)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: base-readonly
labels:
rbac.myorg.io/aggregate-to-dev: "true"
rules:
- apiGroups: [""]
resources: ["namespaces", "nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list"]
---
# 聚合角色:自动继承所有带标签的子角色
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: aggregated-developer
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.myorg.io/aggregate-to-dev: "true"
rules: [] # 由聚合自动填充
当需要新增权限模块(如允许查看 Ingress),只需创建新的子 ClusterRole 并打上对应标签,无需修改主角色。
第五步:持续监控 — 审计日志 + 告警
在 kube-apiserver 开启审计日志,配合 Falco 或 OPA Gatekeeper 实时告警:
# audit-policy.yaml — 记录所有 RBAC 变更
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# 记录所有 RBAC 资源的写操作
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]
verbs: ["create", "update", "patch", "delete"]
# 记录 secrets 访问
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
# 其余只记录元数据
- level: Metadata
omitStages: ["RequestReceived"]
配合 Prometheus 告警规则监控异常提权:
# prometheus-rbac-alert.yaml
groups:
- name: rbac-security
rules:
- alert: ClusterAdminBindingCreated
expr: |
sum(increase(apiserver_audit_event_total{
verb=~"create|update",
resource="clusterrolebindings",
objectRef_name=~".*cluster-admin.*"
}[5m])) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "检测到新的 cluster-admin 绑定创建"
description: "有人刚创建或修改了 cluster-admin 级别的 ClusterRoleBinding,请立即核实。"
避坑指南
1. 不要一步到位收紧权限
错误做法:周五下午直接删除所有 cluster-admin 绑定。正确做法:先用 --dry-run=server 模式测试新 Role 是否满足需求,再用金丝雀方式逐步迁移。先在 staging 验证,再推 production。
2. ServiceAccount Token 自动挂载陷阱
Kubernetes 默认为每个 Pod 自动挂载 ServiceAccount Token。大量 Pod 挂载了 default SA 且该 SA 有权限,攻击面极大:
# 在不需要访问 API 的 Pod 中禁用自动挂载
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
automountServiceAccountToken: false
3. OIDC 集成不要跳过 Group Claim
直接绑定到 User 主体会导致人员变动时逐条清理 RoleBinding。正确做法是通过 OIDC Group Claim 映射到 Kubernetes Group,离职自动失效:
# kube-apiserver 配置 OIDC
--oidc-issuer-url=https://auth.myorg.io
--oidc-client-id=kubernetes
--oidc-username-claim=email
--oidc-groups-claim=groups # 关键:映射组信息
总结
Kubernetes RBAC 最小权限落地的核心逻辑:先审计看清现状 → 消除明显的过度授权 → 按命名空间和团队隔离 → 用聚合角色保持灵活性 → 审计日志 + 告警兜底。
关键原则记住三条:
- namespace 隔离优先于 cluster 级授权
- RoleBinding 优先于 ClusterRoleBinding
- 显式列出 verb/resource,永远不用 *
权限收紧是个持续过程,建议每季度跑一次审计脚本,清理不再使用的 ServiceAccount 和 RoleBinding。安全不是一次性配置,而是持续运营。