4. 统一身份与权限治理

当生产环境从单集群扩展到多集群后,身份与权限治理会立刻从“配置问题”升级为“架构问题”。最典型的现象是:每个集群各自维护一套本地用户,运维人员需要记住多套凭据;开发团队在 A 集群是 developer 组,在 B 集群却要重新授权;审计时无法回答“某个人到底能访问哪些集群的哪些命名空间”。要解决这些问题,核心思路是把身份认证(Authentication)与权限授权(Authorization)解耦:认证统一交给外部身份源,授权统一用 RBAC 表达,并让同一套身份在多个集群间形成联邦。

一、认证与授权分离:OIDC 负责“你是谁”,RBAC 负责“你能做什么”

OpenShift 原生支持 OIDC 身份提供者。配置后,用户通过浏览器登录时会被重定向到身份提供者(如 Keycloak、Dex、企业 IdP),认证成功后 IdP 返回一个 ID Token,其中包含 sub、email、groups 等声明。OpenShift 的 oauth 组件校验该 Token 的签名、签发者(issuer)和受众(audience),并把 groups 声明映射为 OpenShift 的 Group 对象。此后,RBAC 只针对“用户”和“组”授权,不再关心密码从哪里来。

LDAP 则常作为“身份目录”存在。实践中更推荐 OIDC 做认证、LDAP 做用户与组数据源:IdP 通过 LDAP 联合查询用户和组成员关系,OpenShift 只信任 IdP 的 OIDC 声明。这样避免了在每个集群直接配置 LDAP bind 账号和组同步,减少凭据扩散面。若必须直接使用 LDAP 身份提供者,则需注意组同步是周期性拉取,成员变更存在延迟。

用户 / 运维人员 浏览器访问 Console / oc 外部身份提供者 (IdP) Keycloak / Dex / 企业 OIDC 签发 ID Token(含 groups) LDAP 目录 用户 / 组 / 成员关系 作为 IdP 的数据源 登录 联合查询 身份联邦层:统一 OIDC 信任关系(issuer / clientID / groups claim) 同一身份在多个集群被识别为同一 User / Group ID Token 集群 A(生产) oauth 校验 Token → User/Group RBAC:RoleBinding / ClusterRoleBinding 命名空间隔离 dev / staging / prod 独立 NS 集群 B(灾备) oauth 校验 Token → User/Group RBAC:RoleBinding / ClusterRoleBinding 命名空间隔离 dev / staging / prod 独立 NS 集群 C(边缘) oauth 校验 Token → User/Group RBAC:RoleBinding / ClusterRoleBinding 命名空间隔离 dev / staging / prod 独立 NS 认证统一(OIDC) + 授权统一(RBAC) + 隔离统一(Namespace) = 多集群身份联邦

二、多集群身份联邦的关键配置

要让三个集群信任同一个 IdP,每个集群的 OAuth 配置中必须使用相同的 issuer 和 clientID,并正确声明 groups 声明路径。以下是一个基于 Keycloak 的 OIDC 身份提供者示例(参数为教学示例,实际需替换为真实地址与密钥):

apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
  name: cluster
spec:
  identityProviders:
  - name: corp-oidc
    type: OpenID
    mappingMethod: claim
    openID:
      clientID: openshift-multicluster
      clientSecret:
        name: oidc-client-secret
      issuer: https://sso.example.com/auth/realms/prod
      claims:
        preferredUsername:
        - preferred_username
        groups:
        - groups

其中 mappingMethod: claim 表示按用户名声明映射到 OpenShift User;groups 声明会被同步为 Group 对象。三个集群使用同一份配置,即可实现“一次登录、多集群通行”。需要注意:issuer 必须完全一致,否则 Token 校验会失败;clientSecret 建议通过 Secret 注入,不要明文写入 YAML。

三、命名空间隔离与最小权限落地

身份联邦解决“谁能进来”,命名空间隔离解决“进来后能碰什么”。推荐按环境与团队两个维度划分命名空间,例如 team-a-dev、team-a-prod、team-b-dev。每个命名空间内创建 Role 定义该环境允许的操作,再用 RoleBinding 绑定到 Group,而不是绑定到个人。这样人员变动时只需调整 IdP 中的组成员关系,无需逐个集群改 RBAC。

最小权限的核心是“默认拒绝、按需授予”。例如开发人员在 dev 命名空间可拥有 edit 权限,但在 prod 命名空间只应拥有 view 权限,且不允许创建 ClusterRoleBinding。下面是一个生产命名空间的只读绑定示例:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-prod-view
  namespace: team-a-prod
subjects:
- kind: Group
  name: team-a-developers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io

验证时可用 oc auth can-i 做权限自检。例如以 team-a-developers 组成员身份执行:

oc auth can-i create pods -n team-a-dev
# yes
oc auth can-i create pods -n team-a-prod
# no
oc auth can-i delete deployments -n team-a-prod
# no

若返回 no,说明最小权限生效;若返回 yes 但预期为 no,应检查是否存在过宽的 ClusterRoleBinding 或 system:authenticated 组的默认绑定。多集群场景下,建议对每个集群执行同样的自检,确保权限矩阵一致。

建议把“身份源—集群—命名空间—角色—组”整理成一张权限矩阵表,作为变更评审和审计的依据。矩阵中每一行代表一个组,每一列代表一个集群的命名空间,单元格填写允许的角色(如 view、edit、admin)。任何新增授权都必须先更新矩阵,再落地 RBAC。

四、常见问题与排障要点

多集群身份治理中最常见的三类问题:一是登录后看不到预期项目,通常是 groups 声明未正确映射或组名大小写不一致;二是权限过大,多因误用了 ClusterRoleBinding 绑定到 system:authenticated;三是 Token 校验失败,需检查各集群 issuer 是否一致、时间是否同步、证书链是否受信。排障时优先查看 oc get oauth cluster -o yaml 与 oc logs -n openshift-authentication 中的 oauth-server 日志,再核对 IdP 侧返回的声明内容。

把认证、授权、隔离三层分开治理后,多集群权限就从“每个集群各自为政”变成“一套身份、一套组、一套矩阵”。这既降低了运维成本,也让最小权限原则真正可审计、可复现。