饮墨

子安饮墨馀三斗,留与卿儿作赋来

Kubernetes RBAC 最小权限实战:从过度授权到精确控制的 5 步落地方案

痛点

生产环境里最常见的 Kubernetes 安全隐患不是漏洞,而是 RBAC 权限膨胀

典型场景:开发团队要部署服务,运维图省事直接给了 cluster-admin;CI/CD Pipeline 的 ServiceAccount 拥有全命名空间的 * 权限;离职员工的 kubeconfig 三个月没回收。一旦某个 Pod 被攻破或凭证泄露,攻击者直接横向移动拿下整个集群。

CNCF 2025 安全报告显示,67% 的 Kubernetes 安全事件与过度授权的 RBAC 配置直接相关。最小权限原则人人会说,但真正落地需要系统化方法。

方案

5 步渐进式收紧策略,在不影响业务的前提下,将 RBAC 从"全放行"演进到精确控制:

  1. 审计现有权限 → 2. 消除 cluster-admin 滥用 → 3. 按命名空间隔离 → 4. 细化 verb/resource → 5. 持续监控 + 自动告警

实操步骤

第一步:审计现有权限 — 找出谁拥有过多权限

使用 kubectl-who-canrakkess 工具快速审计:

# 安装 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。安全不是一次性配置,而是持续运营。

您还没有登录,请登录后发表评论。