Skip to content

필사 모드: Kubernetes RBAC 深度指南:用 Role、ClusterRole 与 OPA Gatekeeper 实现最小权限访问控制

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
Kubernetes RBAC OPA Gatekeeper

前言

在生产环境运维 Kubernetes 集群时,最先需要建立体系的领域就是访问控制(Access Control)。集群管理员、开发者、CI/CD 流水线、监控代理等各类主体都会向 API Server 发送请求,而这些请求必须按谁(Subject)能对哪些资源(Resource)执行哪些动作(Verb)这一维度进行精细控制。仅靠 Kubernetes 提供的默认授权机制 RBAC(Role-Based Access Control)就已经能实现相当程度的访问控制,但诸如“只允许特定的镜像仓库”“所有 Pod 必须设置资源限制”“禁止创建没有特定标签的命名空间”这类基于策略(Policy)的控制已经超出了 RBAC 的能力范围。

填补这一空白的正是OPA Gatekeeper。它是基于 Open Policy Agent(OPA)的 Kubernetes 准入控制器,通过 ConstraintTemplate 和 Constraint CRD 把用 Rego 语言编写的策略应用到集群。如果说 RBAC 解决的是“给谁授予什么权限”,那么 OPA Gatekeeper 就是验证“即使请求被允许,它是否遵守了策略”的补充层。

本文将全面讲解 RBAC 的核心组成要素、ServiceAccount 令牌管理、按命名空间隔离权限的设计模式、OPA Gatekeeper 架构与 Rego 策略编写、实战策略案例、审计(Audit)策略、主流策略引擎对比,以及故障案例与恢复流程。

RBAC 核心概念

Kubernetes RBAC 由四种 API 对象构成。准确理解这四者之间的关系,是访问控制设计的起点。

Role 与 ClusterRole

Role用于定义特定命名空间内针对资源的权限规则(rules)。ClusterRole则不局限于某个命名空间,适用于定义集群级资源(nodes、persistentvolumes 等)的权限,或者需要跨多个命名空间复用的权限集合。

# 命名空间级 Role:dev 命名空间内的 Pod 读取权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: pod-reader
rules:
  - apiGroups: ['']
    resources: ['pods']
    verbs: ['get', 'watch', 'list']
  - apiGroups: ['']
    resources: ['pods/log']
    verbs: ['get']
# 集群级 ClusterRole:在所有命名空间中管理 Deployment
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deployment-manager
rules:
  - apiGroups: ['apps']
    resources: ['deployments']
    verbs: ['get', 'list', 'watch', 'create', 'update', 'patch']
  - apiGroups: ['apps']
    resources: ['deployments/scale']
    verbs: ['update', 'patch']

核心原则是尽最大可能避免使用通配符(*。一旦使用 resources: ["*"]verbs: ["*"],就等于对当前乃至未来新增的资源都放开了无限制访问,安全风险会急剧上升。

RoleBinding 与 ClusterRoleBinding

把定义好的 Role/ClusterRole 关联到实际主体(Subject)上的,就是 RoleBinding 和 ClusterRoleBinding。

# RoleBinding:在 dev 命名空间中把 pod-reader Role 授予 frontend-team 组
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: frontend-pod-reader
  namespace: dev
subjects:
  - kind: Group
    name: frontend-team
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

注意事项:绝对要避免把用户添加到 system:masters 组。该组成员会绕过所有 RBAC 检查,而且即使删除 RoleBinding 或 ClusterRoleBinding 也无法收回其权限。

Subject 类型

在 RBAC 中被授予权限的主体(Subject)有三种类型。

  • User:由外部认证系统(OIDC、证书等)提供的用户身份
  • Group:用户的逻辑分组,由认证系统传递组信息
  • ServiceAccount:由 Kubernetes 直接管理、供 Pod 内部工作负载使用的账号

ServiceAccount 与令牌管理

从 Kubernetes 1.24 起,ServiceAccount 不再自动生成永久令牌。默认行为改为通过TokenRequest API签发带有时间限制的令牌,这在安全层面是一项重要改进。

# 创建 ServiceAccount
kubectl create serviceaccount ci-deployer -n staging

# 签发限时令牌(有效期 1 小时)
kubectl create token ci-deployer -n staging --duration=3600s

# 确认 ServiceAccount 权限
kubectl auth can-i create deployments --as=system:serviceaccount:staging:ci-deployer -n staging

# 查看绑定到指定 ServiceAccount 的 Role
kubectl get rolebindings -n staging -o json | \
  jq '.items[] | select(.subjects[]? | .name=="ci-deployer" and .kind=="ServiceAccount")'

设计 CI/CD 流水线专用的 ServiceAccount 时,请遵循以下原则。

  1. 为每条流水线创建专属 ServiceAccount:不要让多条流水线共用同一个 ServiceAccount
  2. 只在必要的命名空间创建 RoleBinding:用按命名空间划分的 RoleBinding 取代 ClusterRoleBinding
  3. 令牌有效期最小化:按流水线的实际执行时长设置令牌有效期
  4. 禁用 automountServiceAccountToken:避免 Pod 中挂载不必要的 ServiceAccount 令牌
# 禁用 automountServiceAccountToken 的示例
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service
  namespace: production
automountServiceAccountToken: false

RBAC 设计模式

按命名空间隔离

在多租户环境中,按团队或环境维度拆分命名空间,并为每个命名空间应用独立的 RBAC 策略。

# 按团队划分命名空间 + RBAC 一体化配置示例
---
apiVersion: v1
kind: Namespace
metadata:
  name: team-alpha
  labels:
    team: alpha
    environment: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: team-alpha
  name: team-alpha-developer
rules:
  - apiGroups: ['', 'apps', 'batch']
    resources: ['pods', 'deployments', 'services', 'configmaps', 'jobs']
    verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']
  - apiGroups: ['']
    resources: ['secrets']
    verbs: ['get', 'list'] # 限制 Secret 的写入权限
  - apiGroups: ['']
    resources: ['pods/exec']
    verbs: ['create'] # 为调试放开 exec
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-alpha-developer-binding
  namespace: team-alpha
subjects:
  - kind: Group
    name: team-alpha-devs
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: team-alpha-developer
  apiGroup: rbac.authorization.k8s.io

防止权限提升

RBAC 设计中最需要警惕的,就是切断权限提升(Privilege Escalation)的路径。下面这些权限尤其需要留意。

  • pods/exec:可以在 Pod 内部执行任意命令,从而窃取该 Pod 的 ServiceAccount 令牌
  • secrets 读取权限:存在泄露其他 ServiceAccount 令牌或数据库凭据的风险
  • rolebindings/clusterrolebindingscreate:可以给自己授予更高权限
  • escalate/bind 动词:能够修改或绑定 Role/ClusterRole 的元权限

OPA Gatekeeper 架构

OPA Gatekeeper 以 Kubernetes 准入 Webhook(Admission Webhook)的形式运行,在 API Server 创建/修改/删除资源之前执行策略校验。

组成要素

  1. Gatekeeper Controller Manager:处理准入 Webhook 请求并评估 Rego 策略的核心组件
  2. Audit Controller:周期性检查已经部署的资源是否遵守策略
  3. ConstraintTemplate CRD:定义 Rego 策略逻辑与参数 schema 的模板
  4. Constraint CRD:把 ConstraintTemplate 实例化,指定具体的策略作用对象与参数

安装

# 安装 Gatekeeper(Helm)
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update

helm install gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system \
  --create-namespace \
  --set audit.interval=60 \
  --set constraintViolationsLimit=50 \
  --set audit.fromCache=true

# 确认安装结果
kubectl get pods -n gatekeeper-system
kubectl get crd | grep gatekeeper

工作流程

Gatekeeper 的策略评估流程如下。

  1. 用户或控制器向 API Server 发送资源创建/修改请求
  2. API Server 执行认证(AuthN)与授权(AuthZ、RBAC)
  3. 在准入阶段把请求转发给 Gatekeeper Webhook
  4. Gatekeeper 找出与该资源匹配的 Constraint 并评估 Rego 策略
  5. 若存在违规,拒绝请求(Deny)并返回违规信息
  6. 若无违规,批准请求(Allow)并写入 etcd

ConstraintTemplate 与 Rego 策略编写

基本结构

ConstraintTemplate 由两部分组成:CRD 规格(参数 schema)与 Rego 代码(策略逻辑)。

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
              description: '必填标签列表'
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("资源缺少必填标签: %v", [missing])
        }

Constraint 应用

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: ['']
        kinds: ['Namespace']
    excludedNamespaces:
      - kube-system
      - gatekeeper-system
  parameters:
    labels:
      - 'team'
      - 'cost-center'

实战策略案例

案例 1:限制镜像仓库

在生产集群中强制只能从被允许的容器仓库拉取镜像。

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sallowedrepos
spec:
  crd:
    spec:
      names:
        kind: K8sAllowedRepos
      validation:
        openAPIV3Schema:
          type: object
          properties:
            repos:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sallowedrepos

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not image_from_allowed(container.image)
          msg := sprintf("容器 '%v' 的镜像 '%v' 来自未被允许的仓库。允许的仓库: %v",
            [container.name, container.image, input.parameters.repos])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          not image_from_allowed(container.image)
          msg := sprintf("initContainer '%v' 的镜像 '%v' 来自未被允许的仓库。",
            [container.name, container.image])
        }

        image_from_allowed(image) {
          repo := input.parameters.repos[_]
          startswith(image, repo)
        }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: prod-allowed-repos
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: ['']
        kinds: ['Pod']
    namespaces:
      - production
      - staging
  parameters:
    repos:
      - 'gcr.io/my-company/'
      - 'us-docker.pkg.dev/my-company/'
      - 'registry.internal.company.com/'

案例 2:强制设置资源请求/限制

强制所有容器都必须设置 CPU 与内存的 requests/limits。

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequireresourcelimits
spec:
  crd:
    spec:
      names:
        kind: K8sRequireResourceLimits
      validation:
        openAPIV3Schema:
          type: object
          properties:
            requiredResources:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequireresourcelimits

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          resource := input.parameters.requiredResources[_]
          not container.resources.limits[resource]
          msg := sprintf("容器 '%v' 未设置 resources.limits.%v。", [container.name, resource])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          resource := input.parameters.requiredResources[_]
          not container.resources.requests[resource]
          msg := sprintf("容器 '%v' 未设置 resources.requests.%v。", [container.name, resource])
        }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequireResourceLimits
metadata:
  name: require-cpu-memory-limits
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: ['']
        kinds: ['Pod']
    excludedNamespaces:
      - kube-system
      - gatekeeper-system
  parameters:
    requiredResources:
      - 'cpu'
      - 'memory'

案例 3:阻止特权容器

阻止以特权模式(privileged)运行的容器。

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sdisallowprivileged
spec:
  crd:
    spec:
      names:
        kind: K8sDisallowPrivileged
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdisallowprivileged

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          container.securityContext.privileged == true
          msg := sprintf("不允许使用特权容器: '%v'", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          container.securityContext.privileged == true
          msg := sprintf("不允许使用特权 initContainer: '%v'", [container.name])
        }

RBAC 审计(Audit)与监控

基于 kubectl 的审计

# 确认当前用户的权限
kubectl auth can-i --list

# 确认指定 ServiceAccount 在命名空间内的权限
kubectl auth can-i --list --as=system:serviceaccount:production:app-deployer -n production

# 确认能否执行指定操作
kubectl auth can-i delete pods --as=system:serviceaccount:staging:ci-runner -n staging

# 排查全集群范围内可读取 secrets 的主体(kubectl-who-can 插件)
kubectl who-can get secrets --all-namespaces

# 排查权限过大的 ClusterRoleBinding
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name, .subjects'

利用 Gatekeeper 审计(Audit)

Gatekeeper 的 Audit 功能会对已经部署的资源同样检查是否违反策略。设置为 enforcementAction: dryrun 后,就可以只记录违规项而不做拦截。

# 查询策略违规现状
kubectl get k8sallowedrepos prod-allowed-repos -o yaml | \
  grep -A 100 "status:" | head -50

# 汇总全部 Constraint 的违规情况
kubectl get constraints -o json | \
  jq '.items[] | {name: .metadata.name, kind: .kind, violations: (.status.totalViolations // 0)}'

监控集成

Gatekeeper 默认会暴露 Prometheus 指标。

  • gatekeeper_violations:当前审计中发现的违规数量
  • gatekeeper_request_duration_seconds:准入请求的处理耗时
  • gatekeeper_request_count:准入请求总数(按允许/拒绝区分)
  • gatekeeper_constraint_template_status:ConstraintTemplate 的状态

对比表:RBAC vs OPA Gatekeeper vs PSA vs Kyverno

下面对 Kubernetes 的访问控制与策略引擎做一次综合对比。

项目RBACOPA GatekeeperPod Security Admission (PSA)Kyverno
工作层次授权(Authorization)准入(Admission)准入(Admission)准入(Admission)
策略语言YAML 声明式Rego内置 profile(Privileged/Baseline/Restricted)YAML 声明式
学习曲线高(需要学习 Rego)非常低
灵活性低(只能授予/拒绝权限)非常高低(仅 3 个 profile)
Mutation 支持不适用支持(Assign/Modify)不支持支持(mutate)
资源生成不适用不支持不支持支持(generate)
审计(Audit)需要分析 audit log内置 Audit 功能audit/warn 模式PolicyReport CRD
CNCF 阶段Kubernetes 内置GraduatedKubernetes 内置Incubating
资源消耗无(内置于 API Server)高(多个 Pod)非常低中等
推荐用途基础访问控制复杂的策略逻辑强制 Pod 安全基线Kubernetes 原生策略

推荐组合:把 RBAC(基础授权)+ PSA(Pod 安全基线)+ Gatekeeper 或 Kyverno(自定义策略)组合使用,是生产环境的最佳实践。简单策略适合用 Kyverno,复杂的跨资源校验则更适合 OPA Gatekeeper。

故障案例与恢复流程

案例 1:Gatekeeper Webhook 故障导致全部部署被阻止

症状:所有 Pod 创建都被拒绝,错误信息中出现 webhook "validation.gatekeeper.sh" denied the request 或连接超时。

原因:Gatekeeper Controller Pod 异常退出,或者因资源不足而无法响应。

恢复流程

# 1. 确认 Gatekeeper Pod 状态
kubectl get pods -n gatekeeper-system

# 2. 紧急情况:临时禁用 Webhook(把 failurePolicy 改为 Ignore)
kubectl get validatingwebhookconfigurations gatekeeper-validating-webhook-configuration -o yaml > webhook-backup.yaml
kubectl patch validatingwebhookconfigurations gatekeeper-validating-webhook-configuration \
  --type='json' -p='[{"op": "replace", "path": "/webhooks/0/failurePolicy", "value": "Ignore"}]'

# 3. 重启 Gatekeeper Pod
kubectl rollout restart deployment gatekeeper-controller-manager -n gatekeeper-system

# 4. 确认恢复正常后还原 failurePolicy
kubectl apply -f webhook-backup.yaml

预防措施:把 Gatekeeper 的 failurePolicyFail 改成 Ignore 虽然能提升可用性,但也让策略有被绕过的可能,因此生产环境应保持 Fail,同时为 Gatekeeper Pod 准备充足的资源与 replicas。

案例 2:过多的 ClusterRoleBinding 导致权限泄漏

症状:发现所有 ServiceAccount 都能读取集群资源这一异常状态。

诊断与恢复

# 排查绑定到 cluster-admin 的主体
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}'

# 删除不必要的 ClusterRoleBinding
kubectl delete clusterrolebinding suspicious-admin-binding

# 审计全部 ClusterRoleBinding
kubectl get clusterrolebindings -o json | \
  jq '.items[] | {name: .metadata.name, role: .roleRef.name, subjects: [.subjects[]? | .kind + ":" + .name]}'

案例 3:ConstraintTemplate 语法错误导致策略不生效

症状:已经创建了 Constraint,但策略没有生效。kubectl get constrainttemplates 中的 STATUS 显示异常。

诊断

# 确认 ConstraintTemplate 状态
kubectl get constrainttemplate k8srequiredlabels -o json | jq '.status'

# 确认 Rego 语法错误
kubectl describe constrainttemplate k8srequiredlabels | grep -A 10 "Status:"

# 在 Gatekeeper 日志中确认错误
kubectl logs -n gatekeeper-system -l control-plane=controller-manager --tail=100

运维检查清单

这是一份用于检查生产 Kubernetes 集群访问控制状况的清单。

RBAC 检查清单

  • system:masters 组中是否登记了不必要的用户
  • 是否完全掌握了绑定 cluster-admin ClusterRole 的所有主体
  • 是否存在使用通配符(*)权限的 Role/ClusterRole
  • ServiceAccount 是否默认设置了 automountServiceAccountToken: false
  • 是否按命名空间配置了恰当的 Role/RoleBinding
  • pods/execsecretsrolebindings 等敏感权限是否已被压缩到最小
  • 是否清理了不再使用的 ServiceAccount 与 RoleBinding
  • RBAC 配置是否纳入 Git 管理(GitOps)

OPA Gatekeeper 检查清单

  • Gatekeeper Controller 是否以高可用方式部署(replicas 2 以上)
  • failurePolicy 是否按生产需求进行了设置
  • kube-system、gatekeeper-system 等系统命名空间是否被恰当排除
  • 是否有先用 dryrun 模式测试新策略的流程
  • 是否在定期复查 Audit 结果
  • ConstraintTemplate 中的 Rego 代码是否通过了单元测试
  • Gatekeeper 指标是否已集成到 Prometheus/Grafana
  • Webhook 故障时的紧急恢复流程(Runbook)是否已文档化

定期审计项

  • 每季度的 RBAC 权限复查:识别持有过高权限的主体
  • 每月的 Gatekeeper Audit 报告:违反策略的资源现状
  • ServiceAccount 令牌使用模式分析:清理未使用的令牌
  • 引入新的 CRD/API 时同步更新 RBAC 与 Gatekeeper 策略

结语

Kubernetes 的访问控制不会因为有了 RBAC 就大功告成。RBAC 回答的是“给谁授予什么权限”,而要验证“被允许的操作是否遵守策略”,就离不开 OPA Gatekeeper 这样的准入控制器。同时使用这两种机制、用 Pod Security Admission 强制基础安全基线,并执行定期审计,才是在生产环境落实最小权限原则的方法。

尤其是把策略作为代码来管理(Policy as Code)、在变更前必定用 dryrun 模式评估影响、并提前准备好故障时的紧急恢复流程,这样的运维习惯才是预防安全事故的关键。RBAC 与 OPA Gatekeeper 虽然只是技术工具,但要把它们运营好,就必须与组织的访问控制治理联动起来——这一点不能忘记。

현재 단락 (1/381)

在生产环境运维 Kubernetes 集群时,最先需要建立体系的领域就是**访问控制**(Access Control)。集群管理员、开发者、CI/CD 流水线、监控代理等各类主体都会向 API Se...

작성 글자: 0원문 글자: 13,691작성 단락: 0/381