OpenShift RBAC 与平台安全策略:比 K8s 多出来的那几道锁,每道都是为了堵一个曾经发生过的事故

如果你从原生 Kubernetes 切换到 OpenShift,RBAC 是第一个让你感到"多了点什么但说不太清"的领域。OpenShift 没有发明新的 RBAC 机制------它完全兼容 Kubernetes 原生的 RBAC API------但它在此基础上加了三层原生 K8s 不具备的防护:更丰富的内置角色体系、SCC(Security Context Constraints)与 RBAC 的联动、以及面向企业用户管理的 OAuth + Group 集成。

这三样东西之所以存在,是因为 Red Hat 在大量企业集群的运维中反复看到同一类事故------某个有权限的人做了不该做的操作(不是恶意的,是真的不知道不能做),或者某个 Pod 以过高的特权运行导致节点被攻陷。OpenShift 的 RBAC 扩展本质上是把这些事故的教训写成了默认开启的防护。


一、内置角色体系:不是你装的插件,是集群自带的

原生 Kubernetes 启动后只有一个内置 ClusterRole:cluster-admin。其他角色------admineditview------虽然文档里总被提到,但它们不是"默认就有的",是集群管理员手动创建或者被其他组件带进来的。

OpenShift 不一样。一套 OpenShift 集群启动后,以下 ClusterRole 已经存在且与平台的各组件的功能直接联动:

vbnet 复制代码
cluster-admin    --- 完全控制(跟 K8s 一致)
cluster-status   --- 查看集群状态(如 /healthz)
cluster-reader   --- 只读全集群资源(包括节点信息)
admin            --- 单个 Namespace 内的管理员
edit             --- 修改单个 Namespace 内的资源
view             --- 只读单个 Namespace 内的资源
basic-user       --- 最基本的"我能登录"角色,只能 get 自己的用户信息
self-provisioner --- 创建自己的 Namespace(Project)

basic-user 是一个容易被忽视但影响很大的角色。所有通过 OAuth 登录的用户默认属于 system:authenticated Group,而 OpenShift 有一个默认的 ClusterRoleBinding 把 basic-user 绑定到了 system:authenticated

bash 复制代码
oc get clusterrolebinding basic-users -o yaml
# subjects:
# - apiGroup: rbac.authorization.k8s.io
#   kind: Group
#   name: system:authenticated
# roleRef:
#   kind: ClusterRole
#   name: basic-user

这意味着任何人只要能登录你的 OpenShift 集群(通过 LDAP/GitHub/Google OAuth),默认就能看到一些基础信息。你不需要单独给每个新员工"创建账户 + 绑定角色"------他们登录的那一刻就有 basic-user 权限了。这在原生 K8s 里是不存在的------原生 K8s 没有任何"登录就送"的默认权限。

self-provisioner 是另一个核心差异。在原生 K8s 里,创建 Namespace 是集群级别权限------需要一个 ClusterRole 包含 create namespaces 的 verb。但在 OpenShift 里,self-provisioner 角色被默认绑给了所有 system:authenticated 用户:

bash 复制代码
# 查看 self-provisioner 的绑定
oc describe clusterrolebinding self-provisioners

任何登录用户都能执行 oc new-project my-project,创建一个自己管理的 Project(带 admin 权限的 Namespace)。这显著降低了平台使用的摩擦------开发者不需要提工单"请给我建个 Namespace",直接自己建就行。

如果需要关闭这个行为(比如严格的生产集群):

bash 复制代码
oc patch clusterrolebinding self-provisioners \
  -p '{"subjects": null}'

然后把创建 Project 的权限手动授给指定的 Group:

bash 复制代码
oc adm policy add-cluster-role-to-group self-provisioner platform-team

二、oc adm policy:用人的语言管理 RBAC

原生 Kubernetes 的 RBAC 管理是 YAML 级别的------你要建 Role、再建 RoleBinding、确保引用关系正确、确保 namespace 不搞错。每一步都是在 YAML 里手动配。

OpenShift 提供了一套命令让这个过程变成"自然语言"级别的操作:

bash 复制代码
# 给用户赋予 namespace 管理员权限
oc adm policy add-role-to-user admin alice -n team-a

# 给一个 Group(通过 LDAP 同步来的团队组)赋权
oc adm policy add-role-to-group edit ldap-team-engineering -n team-a

# 给集群级别的只读权限
oc adm policy add-cluster-role-to-user cluster-reader bob

# 查看某个 namespace 里所有绑定了某角色的成员
oc adm policy who-can edit -n team-a

oc adm policy 的底层操作跟你手写 RoleBinding YAML 完全一致------它只是在 kubectl create rolebinding 上包了一层更符合人类直觉的界面。你不需要关心 RoleBinding 对象叫什么名字,不需要手动构造 subjects 数组。所有操作可以直接在生产上审计------因为它们在审计日志里对应的对象和原生 K8s 完全一致。

一条特别有用的命令------找出某个用户在所有 Project 里的权限。这在审计或新人交接时必不可少:

bash 复制代码
oc get rolebindings --all-namespaces -o json | \
  jq '.items[] | select(.subjects[]?.name == "alice") | {ns: .metadata.namespace, role: .roleRef.name}'

同一条逻辑也适用于 ClusterRoleBinding:

bash 复制代码
oc get clusterrolebindings -o json | \
  jq '.items[] | select(.subjects[]?.name == "alice") | {binding: .metadata.name, role: .roleRef.name}'

三、SCC:OpenShift 独有的"第二道权限门"

这是 OpenShift RBAC 体系中最重要也是最常被误解的部分。Security Context Constraints(SCC)不是 RBAC 的替代品,它工作在 RBAC 决策之后------RBAC 说"这个人可以创建 Pod",SCC 说"这个人创建的 Pod 能不能用 privileged container / hostPath / hostNetwork"。

在原生 Kubernetes 里,Pod 的安全配置(privileged、runAsUser、capabilities 等)由两个东西控制:

  • PodSecurityAdmission(K8s 1.25+):Namespace 级别的 Label 控制(privileged/baseline/restricted)
  • 创建 Pod 的人配了 securityContext:靠自觉

OpenShift 换了一种方式。SCC 是一个集群级别资源,它定义了"什么样的 Pod 安全配置是允许的"。然后通过 RBAC 绑定:谁(User/SA)可以使用哪个 SCC

bash 复制代码
# 查看集群上所有 SCC
oc get scc
# 输出类似:
# NAME               PRIV   CAPS         SELINUX     RUNASUSER
# anyuid             false  []           MustRunAs   RunAsAny
# hostaccess         false  []           MustRunAs   MustRunAsRange
# hostmount-anyuid   false  []           MustRunAs   RunAsAny
# privileged         true   [*]          RunAsAny    RunAsAny
# restricted         false  []           MustRunAs   MustRunAsRange

每个 SCC 的关键字段:

yaml 复制代码
apiVersion: security.openshift.io/v1
kind: SecurityContextConstraints
metadata:
  name: restricted
allowPrivilegedContainer: false
allowHostDirVolumePlugin: false
allowHostNetwork: false
allowHostPID: false
runAsUser:
  type: MustRunAsRange       # 容器不能以 root (UID 0) 运行
fsGroup:
  type: MustRunAsRange
seLinuxContext:
  type: MustRunAs

创建 Pod 时 OpenShift 的校验流程:

sql 复制代码
1. User/SA 请求创建 Pod
2. RBAC: 这个 User/SA 有 create pods 权限吗?
3. SCC: 这个 User/SA 被允许使用哪个 SCC?
   → 找到所有可用的 SCC,选择最严格的那个
   → 如果 Pod 的 securityContext 请求超过了 SCC 允许的范围 → 拒绝

一个经典的翻车场景:开发要从镜像仓库拉镜像,配了 securityContext.privileged: true

在原生 K8s 里,只要 RBAC 允许他创建 Pod 且有 PodSecurityAdmission 允许 privileged profile,这个 Pod 就能起来。在 OpenShift 里,这个 Pod 能起来的前提是该开发(或其 SA)被授权使用 privileged SCC------绝大多数平台的配置都不给普通开发者这个 SCC。

SCC 的绑定方式:

bash 复制代码
# 允许某个 SA 使用 privileged SCC(极其危险,仅供核心平台组件)
oc adm policy add-scc-to-user privileged -z default -n kube-system

# 允许某个 SA 使用 anyuid SCC(允许容器以任意 UID 运行)
oc adm policy add-scc-to-user anyuid -z ci-deployer -n team-a

# 允许某个 Group 使用 hostaccess SCC
oc adm policy add-scc-to-group hostaccess infrastructure-team

SCC 的坑在于:当一个 SA 能使用多个 SCC 时,OpenShift 会自动选择最严格的那个。如果你想用的 SCC 恰好不是被选中的那个------Pod 创建失败,报错信息告诉你"Pod 安全配置不满足任何一个可用 SCC 的条件"。排查这种错误的关键动作是:

bash 复制代码
# 查看某个 SA 能使用哪些 SCC
oc describe serviceaccount ci-deployer -n team-a

# 查看所有 SCC 以及谁可以使用它们
oc get scc -o custom-columns=NAME:.metadata.name,USERS:.users,GROUPS:.groups

四、OAuth + Group 的企业化用户管理

OpenShift 的 RBAC 跟它的身份认证体系是绑在一起的。原生 Kubernetes 的用户体系本质上是"证书里的 CN 字段"------你给用户签一个 Client 证书,CN 就是用户名,O 就是 Group。这里面没有密码、没有 SSO、没有 MFA。

OpenShift 内置了 OAuth 服务器,支持对接:

  • LDAP / Active Directory
  • GitHub OAuth
  • Google OAuth
  • OpenID Connect(OIDC)
  • HTPasswd(文件方式,仅用于测试)

配置方式是指定一个 OAuth identity provider:

yaml 复制代码
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
  name: cluster
spec:
  identityProviders:
  - name: ldap-provider
    type: LDAP
    ldap:
      url: ldaps://ldap.company.com:636/ou=users,dc=company,dc=com?uid
      insecure: false
      bindDN: cn=admin,dc=company,dc=com
      bindPassword:
        name: ldap-secret
      attributes:
        id: ["dn"]
        name: ["cn"]
        email: ["mail"]
        preferredUsername: ["uid"]

OpenShift 支持将 LDAP 的 Group 同步到 OpenShift 的 Group 对象中:

bash 复制代码
# 同步 LDAP Group
oc adm groups sync \
  --sync-config=ldap-sync-config.yaml \
  --confirm

# 查看同步来的 Group
oc get groups

同步后你就可以用 Group 来管理权限------新人入职,LDAP Group 自动同步,OpenShift 里的 Group 自动更新,RoleBinding 自动生效。不需要手动操作任何 RBAC YAML。入职赋权、离职撤权都由 LDAP 驱动。

这种模式下的 RBAC 管理流程变成了:

  1. 在 LDAP 里给用户分配到对应的 Group(如 ldap-team-a-dev
  2. OpenShift 定期同步 LDAP Group
  3. OpenShift 里的 RoleBinding 绑定了这些 Group
  4. 用户的权限自动生效或失效

跟原生 K8s 比少了两个手动环节:手动建 ClusterRoleBinding、手动撤销离职员工的 ClusterRoleBinding。

五、一个平台 RBAC 设计的三层模型

综合以上,一个生产 OpenShift 集群的 RBAC 设计可以按三层来组织:

第一层:集群级别权限(极少的人,极少的变化)

makefile 复制代码
cluster-admin: 平台运维团队(2-3 人)
cluster-reader: 所有工程师(通过 system:authenticated Group + basic-user)
self-provisioner: 工程师团队(自己做自己的 Project)

第二层:Project 级别权限(按团队分配,高频变化)

vbnet 复制代码
admin: Tech Lead / Team Lead(管理团队自己的 Namespace)
edit:  全体开发工程师(部署、改配置、看日志)
view:  其他团队需要了解的跨团队成员(产品经理、QA)

第三层:SCC 限制(严格默认,按需开放)

yaml 复制代码
restricted SCC: 所有常规工作负载(默认)
anyuid SCC:     需要特定 UID 的 CI 工具(如 Jenkins agent)
privileged SCC: 仅平台核心组件(OpenShift 自身的 Router、Monitoring)
hostaccess SCC: 日志采集 DaemonSet、节点监控 Agent

这个模型的核心理念是:三层中每一层都在限制下一层的攻击面。

  • 第一层限制了"谁能跨 namespace 做操作"------只有平台团队
  • 第二层限制了"谁能在自己的 namespace 里做什么"------通过 edit/view 区分
  • 第三层限制了"谁能跑高特权 Pod"------即使有人获得了 admin 权限,跑不了 privileged 容器

没有任何一层是"完备的"。三层合在一起,攻击者即使突破了其中一层,下一层仍然在限制他的行动范围。

六、定期审计:没有定期验证的 RBAC 等于没有 RBAC

最后一件事------权限审计不是"一年做一次"的合规任务。真正有效的审计是自动化的、定期的、有趋势的。

一套最简单的审计脚本逻辑:

bash 复制代码
#!/bin/bash
# 1. 找出所有 cluster-admin 使用者
echo "=== Cluster Admin Bindings ==="
oc get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name == "cluster-admin") | {name: .metadata.name, subjects: .subjects}'

# 2. 找出所有能 exec 进 Pod 的 SA
echo "=== Subjects with pod/exec ==="
oc get clusterroles,roles -A -o json | \
  jq '.items[] | select(.rules[]?.resources[]? == "pods/exec") | {name: .metadata.name}'

# 3. 最近 24 小时内新增的 RoleBinding
echo "=== RBAC changes in last 24h ==="
oc get rolebindings,clusterrolebindings -A -o json | \
  jq '.items[] | select(.metadata.creationTimestamp > "'$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ)'") | {kind: .kind, name: .metadata.name, ns: .metadata.namespace}'

每周跑一次,输出 diff------"这周谁拿到了什么新权限"。"无声无息的权限扩散"是 RBAC 体系里最危险的攻击向量,唯一的解法是持续审计。


OpenShift 没有重新发明 Kubernetes 的安全模型,但它在原生的基础上加了内聚性。K8s 的 RBAC 给了你积木------Role、ClusterRole、Binding、SA。OpenShift 给积木配了默认组装方案(内置角色 + 默认绑定 + Group 同步)、强化了一件工具的摩擦力(SCC + pod/exec 管控)、补齐了用户生命周期管理(OAuth + LDAP Group 同步)。你要做的不是去理解一堆新的概念------是去理解这些默认方案在你自己的集群里应该怎么改。

相关推荐
众人皆醒我独醉2 小时前
为什么你的 RBAC 配了还是 403?—— 一套可以复现的排障方法论
面试·kubernetes
爱勇宝3 小时前
DeepSeek实习生日薪5500:真正恐怖的不是工资
人工智能·深度学习·面试
面试鸭3 小时前
面试官:“知识库搜到多少资料,你就全喂给 AI?”我:“信息越多越准。”她亮出错答案:“那它怎么被废话带跑了?”
后端·面试·求职
刘沅5 小时前
LeetCode 93.复原IP地址
算法·面试
用户40966601317515 小时前
实时数据大屏的 4 种推送方案:短轮询 → 长轮询 → SSE → WebSocket
后端·面试
不简说7 小时前
JS 代码技巧 vol.2 — 20 个数组/对象/字符串/正则的实用技巧
前端·javascript·面试
想要成为糕糕手9 小时前
NO.48 旋转图像 —— LeetCode 热题 100 面试导向深度解析
javascript·算法·面试
JieE2129 小时前
LeetCode 138 随机链表的复制|两种解法详解(哈希表 + 原地 O (1) 空间)
javascript·算法·面试