如何创建自定义平台角色

1. 概述

本文档说明自定义角色的核心概念,以及如何配置 RoleTemplate。构建可用的自定义角色主要有两种方式——任选其一并端到端完成:

  • FunctionResource 方法(5.1):从产品内置的权限目录中选择权限。最简单,不需要编写 RBAC。
  • ClusterRole 聚合方法(5.2):编写自己的原生 ClusterRole,再通过标签匹配将其引入 RoleTemplate。配置更多,但可独立于任何已迁移的产品模块,并且是平台的长期方向。

customRules(5.3)是第三种更底层的选项,适用于一次性的原生 RBAC 规则。

不确定该选哪个?方法 A(5.1) 开始——它最简单,且几乎覆盖所有需求。只有在你明确需要基于自己的 ClusterRole 构建角色,或者该角色必须跨多个集群时,才选择 方法 B(5.2)

一旦创建了 RoleTemplate,就可以通过 UserBinding 将其授予用户——控制台操作步骤请参见 管理角色

2. 核心概念

  • RoleTemplate:自定义角色模板。它定义角色语义和权限集,并由 controller 转换为 ClusterRoles。
  • FunctionResource:产品功能使用的 K8s 资源抽象;在 RoleTemplate 中通过 functionResourceRef 引用。
  • ClusterRole:RBAC 规则集合。手写的 ClusterRole 可以通过标签聚合到 RoleTemplate(或内置系统角色)中。
  • UserBinding:用户与角色/作用域之间的绑定;controller 会基于 UserBinding 生成 RoleBinding/ClusterRoleBinding,完成最终授权。
NOTE
  • functionResourceRef 的值来自 FunctionResource 的 metadata.name
  • 显示名称通常位于 metadata.annotations 中,可用于映射到 UI 模块。

3. 技术变更

自 ACP v4.3 起,功能权限正逐步从 FunctionResource 迁移到原生 K8s ClusterRole 管理,并通过 ClusterRole 标签聚合到系统角色中;基于 FunctionResource 的管理方式将在 v4.5 中退役。创建角色不需要了解这些背景——可直接跳到第 5 节查看步骤。

NOTE

迁移仍在进行中:大多数产品模块尚未发布可聚合的 ClusterRoles,因此 5.4 中提到的内置系统标签(如 aggregate-to-namespace-developer 等)目前解析不到任何内容。不要指望通过给某个 aggregationRules 指向它来创建自定义角色——5.2 展示了可行模式,5.5 则可用于验证。(你还可能在 kubectl 输出中看到隐藏的 legacy-* RoleTemplates;它们是内置角色的兼容桥接,可以忽略。)

4. 选择一种方法

这三种方法都能生成可用的 RoleTemplate;区别在于你需要编写什么,以及它们能表达什么。

方法 A:FunctionResource(5.1)方法 B:ClusterRole 聚合(5.2)方法 C:customRules(5.3)
最适合复用产品已暴露的权限基于自己的 ClusterRole 构建独立角色,尤其适用于跨集群场景快速编写一次性的原生 RBAC 规则
你需要编写的内容什么都不用——从目录中选择一个 ClusterRole 以及其聚合标签内联 RBAC 规则
多集群由平台处理为你的 ClusterRole 添加 sync.scope: "platform"由平台处理
跨作用域规则支持支持——每个作用域一个 ClusterRole不支持——仅限基础 ClusterRole

5. 配置

CAUTION

自定义 RoleTemplate 必须始终设置 rbac.cpaas.io/version: "v2"auth.cpaas.io/roletemplate.frozen: "false" 对于自定义角色,如果 controller 发现缺少 version,会自动补成 v1(这会 忽略 aggregationRules);如果缺少 frozen,会自动补成 true(这会 阻止后续 spec 编辑更新 ClusterRoles)。这两个字段是独立处理的,因此只设置其中一个,另一个仍会保持旧默认值。下面的示例还设置了 auth.cpaas.io/roletemplate.official: "false"(可选,但不要在自定义角色上设为 "true"——那会把它变成内置系统角色),以及 auth.cpaas.io/roletemplate.level,该字段决定角色授予的范围——请选择最小满足需求的范围:namespace(每个 namespace)、project(项目内所有 namespace)、cluster(整个集群)或 platform(整个平台)。

5.1 方法 A:FunctionResource

当你需要的权限已经以 FunctionResource 形式暴露出来时使用此方法——目前几乎所有产品模块都属于这种情况。

步骤 1 — 找到所需的 FunctionResources

kubectl get functionresource -o custom-columns='NAME:.metadata.name,MODULE:.metadata.labels.auth\.cpaas\.io/functionresource\.module,DISPLAY:.metadata.annotations.cpaas\.io/functionresource\.function\.display-name'

metadata.name 会填入 functionResourceRef;显示名称注解可告诉你它映射到哪个 UI 功能。

步骤 2 — 编写 RoleTemplate

apiVersion: auth.alauda.io/v1beta1
kind: RoleTemplate
metadata:
  name: demo-funcrole
  annotations:
    cpaas.io/display-name: Example Function Role
    cpaas.io/description: FunctionResource example
  labels:
    auth.cpaas.io/roletemplate.level: namespace
    auth.cpaas.io/roletemplate.official: "false"
    rbac.cpaas.io/version: "v2"
    auth.cpaas.io/roletemplate.frozen: "false"
spec:
  rules:
    - functionResourceRef: acp-app
      verbs: [get, list, watch]

步骤 3 — 应用并在 5.5 中验证

NOTE

acp-namespace-resource-manage 不允许 出现在自定义角色中。

5.2 方法 B:ClusterRole 聚合(独立自定义角色)

与 FunctionResource 方法不同,aggregationRules 本身并不定义权限——它只会从带有匹配标签的现有 ClusterRoles 中拉取 rules。若要通过这种方式构建真正独立的自定义角色,你需要自己提供两部分:包含真实规则的 ClusterRole,以及将其连接到 RoleTemplate 的标签。

步骤 1 — 编写一个包含真实权限的原生 ClusterRole,并为其添加你自己的聚合标签以及一个作用域标签。不要复用内置系统角色标签(5.4),除非你就是想把这些权限授予所有已经拥有该内置角色的用户——如果要做独立角色,请自定义标签名称。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cpaas:demo-auditor:business-ns:view
  labels:
    rbac.cpaas.io/aggregate-to-demo-auditor: "true"
    rbac.cpaas.io/aggregate-to-scope-business-ns: "true"
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]

步骤 2 — 如果该角色还需要在业务集群中生效,而上面的 ClusterRole 只创建在 global 集群中,则添加 rbac.cpaas.io/sync.scope: "platform",这样平台会将其复制到每个集群。没有这个标签时,ClusterRole 只会存在于你创建它的那个位置,而下面的 RoleTemplate 在任何没有该 ClusterRole 的集群上都会解析为空权限。

  labels:
    rbac.cpaas.io/aggregate-to-demo-auditor: "true"
    rbac.cpaas.io/aggregate-to-scope-business-ns: "true"
    rbac.cpaas.io/sync.scope: "platform"

步骤 3 — 创建 RoleTemplate,通过 aggregationRules 引用相同的标签:

apiVersion: auth.alauda.io/v1beta1
kind: RoleTemplate
metadata:
  name: demo-auditor
  annotations:
    cpaas.io/display-name: Example Aggregation Role
    cpaas.io/description: ClusterRole aggregation example
  labels:
    auth.cpaas.io/roletemplate.level: namespace
    auth.cpaas.io/roletemplate.official: "false"
    rbac.cpaas.io/version: "v2"
    auth.cpaas.io/roletemplate.frozen: "false"
spec:
  aggregationRules:
    - clusterRoleSelectors:
        - matchLabels:
            rbac.cpaas.io/aggregate-to-demo-auditor: "true"
            rbac.cpaas.io/aggregate-to-scope-business-ns: "true"
      scope: business-ns
  rules: []

如果需要覆盖多个作用域(例如还包括 clustersystem-ns),则每个作用域都添加一个 ClusterRole,并为每个作用域在 aggregationRules 下添加一条对应项——多作用域示例见 6.2。

步骤 4 — 应用并在 5.5 中验证。如果结果为空,说明要么 ClusterRole 上的标签与 clusterRoleSelectors 中的标签并不真正匹配,要么该 ClusterRole 还没有同步到你正在检查的集群(步骤 2)。

CAUTION

不要把 aggregationRules 指向你希望存在的系统/产品标签(例如 rbac.cpaas.io/aggregate-to-namespace-auditor)——大多数模块尚未发布这些标签,因此会解析为空,角色会悄悄地不授予任何权限。像上面那样使用你自己的 ClusterRole 和你自己的标签,始终可行。

5.3 方法 C:customRules

如果只是一次性的原生 RBAC 规则,不值得单独创建一个 ClusterRole 对象,可以直接内联写入 RoleTemplate:

apiVersion: auth.alauda.io/v1beta1
kind: RoleTemplate
metadata:
  name: demo-customrules
  annotations:
    cpaas.io/display-name: Example Custom Rules Role
    cpaas.io/description: customRules example
  labels:
    auth.cpaas.io/roletemplate.level: namespace
    auth.cpaas.io/roletemplate.official: "false"
    rbac.cpaas.io/version: "v2"
    auth.cpaas.io/roletemplate.frozen: "false"
spec:
  customRules:
    - apiGroups: [""]
      resources: ["configmaps"]
      verbs: ["get", "list", "watch"]
NOTE

customRules 只会写入角色的基础 ClusterRole——不能像 clustersystem-ns 那样针对特定作用域(customRules 上没有 scope 字段)。如果你需要跨作用域规则,请使用方法 A 或方法 B。

5.4 字段参考

第一个表涵盖元数据以及每个自定义角色都需要的标签;第二个表涵盖权限字段,并按所属方法分组(5.1–5.3)。

元数据和标签(适用于每个自定义 RoleTemplate)

字段描述
metadata.nameRoleTemplate 的唯一名称;UserBinding 在为用户授予该角色时会引用它。自定义字符串
metadata.annotations.cpaas.io/display-name, .../description控制台中显示的可读名称和描述。自定义字符串
metadata.labels.auth.cpaas.io/roletemplate.level角色授予的位置,以及 controller 为其生成哪些 ClusterRoles。platform / cluster / project / namespace
metadata.labels.auth.cpaas.io/roletemplate.official是否为内置系统角色。自定义角色不能声称自己是 official。"false" 或省略;自定义角色绝不能是 "true"
metadata.labels.rbac.cpaas.io/versioncontroller 使用哪种转换逻辑。v2 支持 aggregationRules;旧版 v1 会忽略它。"v2"(当前);省略则默认为 "v1"
metadata.labels.auth.cpaas.io/roletemplate.frozen生成的 ClusterRoles 是否锁定。"false" 允许后续 spec 编辑生效。"false";省略则默认为 "true"(冻结)

权限字段(按你的方法选择相应字段)

字段方法描述
spec.rules[].functionResourceRefA要包含其权限的 FunctionResource 名称。可通过 kubectl get functionresource 查找名称。FunctionResource 的 metadata.name
spec.rules[].verbsA角色允许在该 FunctionResource 资源上执行的操作。get / list / watch / create / update / patch / delete / deletecollection
spec.aggregationRules[].scopeB匹配到的规则会落入哪个生成的 ClusterRole 中——也就是它们适用的范围:business-ns(项目下创建的 namespace)、project-ns(项目自己的 namespace)、system-nscpaas-system)、cluster(整个集群)、kube-public。必须与 level 兼容(见下方说明)。cluster / project-ns / business-ns / system-ns / kube-public
spec.aggregationRules[].clusterRoleSelectorsB通过标签选择要从哪些现有 ClusterRoles 中拉取规则。matchLabels(精确的键/值)和/或 matchExpressions(基于集合:In / NotIn / Exists / DoesNotExist
spec.customRules[].apiGroupsC规则覆盖的 K8s API 组。""(核心组)/ "*"(全部)/ 某个组名
spec.customRules[].resourcesC规则覆盖的资源类型。例如 podsconfigmaps"*" 表示全部
spec.customRules[].verbsC允许对这些资源执行的操作。spec.rules[].verbs 相同
spec.customRules[].resourceNamesC可选:将规则限制为特定名称的对象。名称列表
spec.customRules[].nonResourceURLsC可选:授予对非资源 URL 的访问权限(例如 /healthz);仅适用于集群级角色。URL 列表
NOTE
  • 对于某个给定作用域,应使用直接规则(spec.rules / spec.customRules)或 spec.aggregationRules 二选一,不要同时使用——当它们在同一个生成的 ClusterRole 上重叠时,聚合规则会生效,直接规则会被丢弃。
  • auth.cpaas.io/roletemplate.level(角色授予的位置——platform / cluster / project / namespace)与 spec.aggregationRules[].scope(每条聚合规则落入哪个生成的 ClusterRole)是两个不同的概念。controller 对二者之间只强制一条约束:platformcluster 级角色只能使用 scope: clusterprojectnamespace 级角色可以使用五种 scope 中的任意一种(包括 cluster)。违反此规则会使 RoleTemplate 进入 Failed 状态。

用于 matchLabels 的标签

对于独立自定义角色,请自定义一个聚合标签——任何 rbac.cpaas.io/aggregate-to-<name> 字符串都可以(5.2)。再配一个作用域标签,用于控制规则在哪儿生效:

  • rbac.cpaas.io/aggregate-to-scope-cluster: "true"
  • rbac.cpaas.io/aggregate-to-scope-project-ns: "true"
  • rbac.cpaas.io/aggregate-to-scope-business-ns: "true"
  • rbac.cpaas.io/aggregate-to-scope-system-ns: "true"
  • rbac.cpaas.io/aggregate-to-scope-kube-public: "true"

如果你想要为内置系统角色追加权限(高级用法——这会把权限授予所有已经拥有该角色的用户),请将你的 ClusterRole 标记为 rbac.cpaas.io/aggregate-to-<built-in-role>,例如 aggregate-to-namespace-developeraggregate-to-platform-admin

多集群分发(5.2 步骤 2):将 rbac.cpaas.io/sync.scope: "platform" 添加到你手工创建的 ClusterRole 上,以便将其从 global 集群复制到所有集群。这个标签对 RoleTemplate 本身或 RoleTemplate 生成的 ClusterRoles 都没有影响。

5.5 验证配置是否真正生效

检查 RoleTemplate 状态

RoleTemplate 会报告它生成了什么,以及是否已同步——先从这里开始:

kubectl get roletemplate <name> -o jsonpath='{.status.phase}{"\n"}{.status.clusterRoles}{"\n"}'
kubectl get roletemplate <name> -o jsonpath='{.status.conditions}'

status.phase 应为 Readystatus.clusterRoles 列出它生成的 ClusterRoles;当发生错误时,status.conditions 会包含原因/消息。如果 phase 不是 Readystatus.clusterRoles 为空,或者其引用的 ClusterRoles 的 rules 为空(kubectl get clusterrole <name> -o yaml),那么该角色并没有按预期授予权限。常见原因按大致频率排列如下:

  • RoleTemplate 缺少 rbac.cpaas.io/version: "v2",或者被自动冻结了——请参见第 5 节中关于必需标签的注意事项。这是 aggregationRules 看起来“什么都没做”的最常见原因。
  • functionResourceRef 名称拼写错误——controller 会静默跳过未知引用,并生成一个空的 ClusterRole,不报错。
  • 你的 aggregationRules 标签没有匹配到任何 ClusterRole,或者源 ClusterRole 尚未同步到当前集群。

确认用户实际获得了权限

生成 ClusterRoles 不是最终目的——真正要看的是用户是否能访问。通过 UserBinding 授权角色后(参见 管理角色),使用 impersonation 检查确认最终结果:

kubectl auth can-i get deployments --as=<user> -n <namespace>

在编写 aggregationRules 之前,先检查某个聚合标签是否可用

clusterRoleSelectors 中引用 rbac.cpaas.io/aggregate-to-* 标签之前,先确认它确实能解析到一个 ClusterRole:

kubectl get clusterrole -l rbac.cpaas.io/aggregate-to-<your-label>=true \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.auth\.cpaas\.io/roletemplate}{"\n"}{end}'
  • 没有输出 → 没有任何 ClusterRole 带有这个标签。如果你使用的是自己的自定义标签(5.2),请再次确认该标签确实已应用到你的 ClusterRole,并且已同步到当前集群。如果你使用的是系统标签(5.4),并希望某个产品模块已经发布了它,那它很可能还没有——见第 3 节。
  • 输出中 auth.cpaas.io/roletemplate 显示 legacy-* 值 → 这个 ClusterRole 来自第 3 节所述的基于 FunctionResource 的兼容桥接,而不是来自已经真正迁移的模块。权限仍然来源于 FunctionResource,这个桥接预计会在 v4.5 中消失。

5.6 更新与删除

  • 更新:编辑 RoleTemplate 的 spec。只有当 auth.cpaas.io/roletemplate.frozen"false" 时,修改才会生效(见第 5 节的注意事项)。
  • 删除:删除 RoleTemplate。controller 会自动清理它在所有集群中生成的 ClusterRoles——不需要手动清理。(如果是你为方法 B 手工编写的 ClusterRole,则由你自己决定是否删除。)

6. 复制与裁剪(示例)

这里有两个完整示例:裁剪一个内置模板(方法 A,6.1),以及基于你自己的 ClusterRoles 构建一个多作用域角色(方法 B,6.2)。对于 FunctionResource 角色来说,复制一个内置系统模板并进行裁剪,是避免遗漏模块权限的最快方式。

6.1 裁剪系统 RoleTemplate YAML(方法 A)

步骤:

  • 保留必需的 FunctionResources。
  • 将 verbs 缩减为只读(get/list/watch)。
  • 删除未使用的模块。

角色示例:不包含 secret 权限的开发人员审计员

NOTE
  • 在自定义角色中,不要配置 Namespace Resource Management(FunctionResource:acp-namespace-resource-manage)。
  • 删除 User Secret Dictionary(FunctionResource:acp-user-secret)。
  • 将所有 verbs 设为 get/list/watch。

示例 YAML:

apiVersion: auth.alauda.io/v1beta1
kind: RoleTemplate
metadata:
  annotations:
    cpaas.io/description: Responsible for development, deployment, and maintenance within the namespace.
    cpaas.io/display-name: Developer Copy
  labels:
    auth.cpaas.io/roletemplate.level: namespace
    auth.cpaas.io/roletemplate.official: "false"
    rbac.cpaas.io/version: "v2"
    auth.cpaas.io/roletemplate.frozen: "false"
  name: namespace-developer-system-copy
spec:
  rules:
    - functionResourceRef: views-acp-userview
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: infrastructure-clusters
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: infrastructure-nodesmanage
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: infrastructure-domains
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: acp-subnet
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: acp-networkpolicies
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: infrastructure-storageclasses
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: infrastructure-persistentvolumes
      verbs:
        - get
        - list
        - watch
    - functionResourceRef: acp-virtualmachineimagetemplates
      verbs:
        - get
        - list
        - watch

6.2 构建多作用域审计员角色(方法 B)

这是 6.1 的聚合方法等价实现:一个覆盖业务 namespace 和项目 namespace 的 namespace 级只读角色,基于你自己的 ClusterRoles 和你自己的标签构建——遵循 5.2 中的模式。

ClusterRoles——每个要覆盖的作用域一个,全部使用相同的自定义标签,并各自带有自己的作用域标签:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cpaas:demo-auditor:business-ns:view
  labels:
    rbac.cpaas.io/aggregate-to-demo-auditor: "true"
    rbac.cpaas.io/aggregate-to-scope-business-ns: "true"
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["configmaps", "services"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cpaas:demo-auditor:project-ns:view
  labels:
    rbac.cpaas.io/aggregate-to-demo-auditor: "true"
    rbac.cpaas.io/aggregate-to-scope-project-ns: "true"
rules:
  - apiGroups: [""]
    resources: ["configmaps", "services"]
    verbs: ["get", "list", "watch"]

RoleTemplate——每个作用域一条 aggregationRules,复用同一个自定义标签:

apiVersion: auth.alauda.io/v1beta1
kind: RoleTemplate
metadata:
  name: demo-namespace-auditor
  annotations:
    cpaas.io/display-name: Custom Namespace Auditor
    cpaas.io/description: Read-only audit across business and project namespaces
  labels:
    auth.cpaas.io/roletemplate.level: namespace
    auth.cpaas.io/roletemplate.official: "false"
    rbac.cpaas.io/version: "v2"
    auth.cpaas.io/roletemplate.frozen: "false"
spec:
  aggregationRules:
    - clusterRoleSelectors:
        - matchLabels:
            rbac.cpaas.io/aggregate-to-demo-auditor: "true"
            rbac.cpaas.io/aggregate-to-scope-business-ns: "true"
      scope: business-ns
    - clusterRoleSelectors:
        - matchLabels:
            rbac.cpaas.io/aggregate-to-demo-auditor: "true"
            rbac.cpaas.io/aggregate-to-scope-project-ns: "true"
      scope: project-ns
  rules: []

如果还需要 system-ns,也可以用同样方式添加;并且请记住,如果某个 ClusterRole 只创建在 global 集群中,但该角色还必须在业务集群中生效,则要在每个 ClusterRole 上都加上 rbac.cpaas.io/sync.scope: "platform"(5.2 步骤 2)。