KubeVela Operation 权限组件设计解读:用两个 ComponentDefinition 落地 invoke / operate / use 权限模型 云原生DevOps运维微服务【免费下载链接】kubevelaThe Modern Application Platform.项目地址https://gitcode.com/gh_mirrors/ku/kubevela点击查看免费下载本篇文章围绕 KEP-2.15OperationTemplate与Operation配套的权限设计文档design/vela-core/keps/2.15-operations/design/03-permission-components.md展开。该文档给出两个示意性Illustrative的ComponentDefinition——operation-permissions与operation-access用纯声明式方式把 KubeVela 的 Operation 权限模型翻译成平台管理员早已熟悉的普通 Kubernetes RBAC 对象。读完本文你将理解为何权限要拆成两个由不同角色掌管的组件、两个 CUE 模板如何渲染出 ServiceAccount/Role/RoleBinding以及它们与 KEP 主文档中invoke、operate、use等自定义动词的对应关系并掌握四种典型的管理模式与安全前提。背景这份设计要落地的是什么03-permission-components.md是 KEP-2.15OperationTemplate Operation的配套设计文档本身不新增任何 API、不需要控制器改造——它回答的是一个更务实的问题KEP 主文档在 Permissions 一节定义的权限模型如何用已经存在的对象表达出来。KEP 主文档把权限拆成三个相互独立、且都必须通过的问题May this user act on this target?—— 能否对某个目标Application动手用operate动词在 admission 时对该用户做SubjectAccessReview检查May this user invoke this operation?—— 能否使用某个OperationTemplate用invoke动词而不是get理由是数据访问动词会被批量授予get会悄悄把运行任何模板的权限交给所有只读角色What may the operation do once it runs?—— 运行时的身份由模板的runAs决定默认以平台供应的ServiceAccount运行mode: Platform或要求以发起者身份运行mode: Invoker而指名使用某个 ServiceAccount 本身需要use授权use是一个不映射到任何 HTTP 请求的自定义动词仅由 webhook 咨询因此不授予任何 API 访问能力。主文档还特别指出这些账户、Role、RoleBinding都是普通 Kubernetes 对象因此可以用 Application 来交付——这正是本设计文档的主题。主文档的 Declaring what a template needs, without minting it 一节预先给出了两个组件在 Application 中的用法示例并把完整定义外包给了本设计文档components: - name: backup-permissions type: operation-permissions properties: serviceAccountName: op-backup namespaces: [payments-prod, orders-prod] rules: - {apiGroups: [], resources: [secrets], verbs: [get]} - {apiGroups: [batch], resources: [jobs], verbs: [create, get, list]}配对关系是整个模型的核心模板用requires:声明它需要什么组件用rules供应什么两者是同一张清单主文档原文the templatedeclareswhat it needs, the componentprovisionsit, and the two are the same list。一个模块可以把自己的ComponentDefinition、配套的OperationTemplate、以及这些模板所需的 RBAC 作为一个整体单元发布、评审、升级。为什么是两个组件而不是一个设计文档开头用一张对比表给出了拆分依据operation-permissionsoperation-access回答的问题这个操作可以做什么谁可以运行它、对什么运行创建的对象ServiceAccount、它的Roles、use授予invoke、operate和create授予归属者平台管理员团队负责人何时变更运行手册需要新 API 时有人加入 on-call 轮值表时随什么发布随模块与OperationTemplate一起随团队与其 Applications 一起把两者揉成一个对象就会把轮值表变更和重新评审 ServiceAccount 可以碰什么绑在一起——一次 on-call 人员变动就会触发对整个服务账户权限的复审。文档特意强调下文 同时放进一个 Application 只是为了读起来更清楚并不是推荐做法。operation-permissions为 OperationTemplate 供应执行身份operation-permissions负责供应一个OperationTemplate在runAs中命名的身份ServiceAccount、它在所服务的每个 namespace 中持有的权限以及允许作者把模板指向它的use授予。完整 CUE 定义如下// operation-permissions.cue operation-permissions: { type: component description: Provisions the service account an OperationTemplate runs as attributes: workload: definition: {apiVersion: v1, kind: ServiceAccount} } template: { parameter: { // usageThe account an OperationTemplate names in runAs serviceAccountName: string // usageNamespaces this account may operate in namespaces: [...string] // usageWhat it may do there, the same shape as the templates requires: rules: [...{ apiGroups: [...string] resources: [...string] verbs: [...string] resourceNames?: [...string] }] // usageWho may name this account in an OperationTemplate templateAuthors?: [...{kind: string, name: string}] } // the account itself, in this Applications namespace output: { apiVersion: v1 kind: ServiceAccount metadata: name: parameter.serviceAccountName } outputs: { // what it may do, granted separately in each namespace it serves for ns in parameter.namespaces { role-\(ns): { apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: {name: \(parameter.serviceAccountName)-ops, namespace: ns} rules: parameter.rules } binding-\(ns): { apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: {name: \(parameter.serviceAccountName)-ops, namespace: ns} roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: \(parameter.serviceAccountName)-ops} subjects: [{ kind: ServiceAccount name: parameter.serviceAccountName namespace: context.namespace }] } } // who may point a template at it if parameter.templateAuthors ! _|_ { use-role: { apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: {name: \(parameter.serviceAccountName)-use, namespace: context.namespace} rules: [{ apiGroups: [] resources: [serviceaccounts] resourceNames: [parameter.serviceAccountName] // grants no API access; only the OperationTemplate webhook consults it verbs: [use] }] } use-binding: { apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: {name: \(parameter.serviceAccountName)-use, namespace: context.namespace} roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: \(parameter.serviceAccountName)-use} subjects: parameter.templateAuthors } } } }几个值得展开的要点namespaces是ClusterRole的有界替代品。模板会为清单里的每个 namespace 各渲染一对Role/RoleBinding不在清单里的 namespace 会拒绝该操作。而ClusterRoleBinding会把权限授予到所有 namespace——包括那些从不希望有操作运行的 namespace。这也正是主文档所说的有界版本ServiceAccount本身没有任何权限它只是身份它在某个 namespace 能做什么完全由那个 namespace里的RoleBinding决定。use授予不携带任何 API 权限。注释写得很清楚grants no API access; only the OperationTemplate webhook consults it。KEP 主文档 Choosing the identity, per template 解释了为什么是use而不是impersonate或getimpersonate是真实权限持有者可以kubectl --as该账户干它能干的一切get会被批量授予resources: [*], verbs: [get, list, watch]是集群里最常见的只读角色形态。use是 RBAC 动词字符串中不映射到任何请求的自由词遵循了PodSecurityPolicy用use于podsecuritypolicies的先例——一个携带verbs: [use]的RoleBinding只做一件事评审者一眼能看清。运行时身份的落实机制在仓库中已有现成实现KubeVela 通过auth.ContextWithUserInfopkg/auth/userinfo.go把身份注入到 context来源是对象上的注解GetUserInfoInAnnotationpkg/auth/userinfo.go提取用户信息时优先采用app.oam.dev/username即完整限定的 subject而不是裸的 service-account 名——这正是主文档在模板所在 namespace 解析账户这一修复得以成立的原因。注解常量定义在 pkg/oam/labels.goAnnotationApplicationServiceAccountName app.oam.dev/service-account-name。而 controller 真正借用账户所需的impersonate权限在主文档中指出已存在于 charts/vela-core/templates/kubevela-controller.yaml启用authentication.enabled时。controller 不应该铸造 ServiceAccount。主文档 Declaring what a template needs 专门论证了这一点若控制器按模板声明现场生成账户并绑定权限它就必须持有所有模板可能声明的权限的并集任何能发布模板的人都够得着它——这比借用身份严格更糟。声明需求并核验保留的是有用的一半ServiceAccount 仍然由平台管理员有意地、一次性创建模板只是引用它的名字。operation-access把谁能跑、对什么跑、记录落哪授予给团队operation-access面向消费侧给一组 subject 授予操作员需要的三样东西——对模板的invoke、对目标的operate、以及Operation记录落地的 namespace 中的create。完整 CUE 定义// operation-access.cue operation-access: { type: component description: Grants a team the ability to run operations against its applications attributes: workload: definition: { apiVersion: rbac.authorization.k8s.io/v1 kind: Role } } template: { parameter: { // usageWho is being granted. Users, Groups or ServiceAccounts subjects: [...{kind: string, name: string, namespace?: string}] // usageWhere the teams Applications live and its Operations are created namespace: *context.namespace | string // usageApplications they may operate on. Omit to mean every one in the namespace applications?: [...string] // usageAlso allow steps declaring impact: Irreversible allowIrreversible: *false | bool // usageTemplates they may invoke, grouped by the namespace each lives in templates: [...{namespace: string, names: [...string]}] } // one Role per namespace, gathering every template name listed for it. Keying the // output on t.namespace alone would collide when two entries share a namespace, and // CUE fails the render rather than merging the conflicting resourceNames. _invokeNames: { for t in parameter.templates { \(t.namespace): [for u in parameter.templates if u.namespace t.namespace for n in u.names {n}] } } // Group and User need the rbac apiGroup, ServiceAccount needs a namespace instead _subjects: [for s in parameter.subjects { kind: s.kind name: s.name if s.kind ServiceAccount {namespace: s.namespace} if s.kind ! ServiceAccount {apiGroup: rbac.authorization.k8s.io} }] // what they may do in their own namespace: operate on targets, create the record output: { apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: {name: \(context.name)-operate, namespace: parameter.namespace} rules: [ { apiGroups: [core.oam.dev] resources: [applications] if parameter.applications ! _|_ {resourceNames: parameter.applications} verbs: [operate, if parameter.allowIrreversible {operate-irreversible}] }, { apiGroups: [core.oam.dev] resources: [operations] verbs: [create, get, list, watch] }, ] } outputs: { operate-binding: { apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: {name: \(context.name)-operate, namespace: parameter.namespace} roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: \(context.name)-operate} subjects: _subjects } // invoke is granted where the template lives, not where the team works for ns, names in _invokeNames { invoke-role-\(ns): { apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: {name: \(context.name)-invoke, namespace: ns} rules: [{ apiGroups: [core.oam.dev] resources: [operationtemplates] resourceNames: names verbs: [invoke] }] } invoke-binding-\(ns): { apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: {name: \(context.name)-invoke, namespace: ns} roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: \(context.name)-invoke} subjects: _subjects } } } }设计文档专门抽出两个细节namespace参数而非直接用context.namespace。如果没有这个参数operate授予会落在 Application 所在的地方——而这个 Application 位于vela-system团队却工作在payments-prod。namespace: *context.namespace | string让常见单 namespace 场景保持安静默认取当前 namespace又允许跨 namespace 的场景显式指定。_subjects转换。RoleBinding的 subject 对Group/User需要apiGroup: rbac.authorization.k8s.io对ServiceAccount则需要namespace。搞错的结果是一个能正常 apply、却静默匹配不到任何人的绑定——这是权限对象最恶劣的失败模式。因此模板用 CUE 条件分支为两种 kind 生成不同的字段结构。_invokeNames的按 namespace 归组如果直接用t.namespace作输出键两个条目共享同一 namespace 时会发生键冲突CUE 会直接渲染失败而不是合并冲突的resourceNames。这里的双层推导先按 namespace 分组、再展平名字列表避免了这一问题——注释明确说明这正是刻意为之宁可渲染失败也不静默产生错误 RBAC。另外注意verbs: [operate, if parameter.allowIrreversible {operate-irreversible}]与主文档 May the invoker act on the target 的呼应严重程度通过命名不同动词来表达而不是复用读写动词对。一个所有步骤都标记为Safe的模板请求operate带任何Irreversible步骤的模板请求operate-irreversible。角色因而可以允许对某 Application 执行常规过程、同时扣住破坏性过程——这是get/update完全无法表达的区分。主文档示例kind: Role rules: - apiGroups: [core.oam.dev] resources: [applications] resourceNames: [payments] verbs: [operate] # routine procedures only两个组件同时放进一个 Application设计文档用一个 Application 把两者并排展示以便看清它们渲染出的对象如何互补apiVersion: core.oam.dev/v1beta1 kind: Application metadata: name: payments-operations namespace: vela-system spec: components: # 1. the identity the operation runs as, and who may point a template at it - name: op-backup type: operation-permissions properties: serviceAccountName: op-backup namespaces: [payments-prod] # where it may operate rules: - {apiGroups: [], resources: [secrets], verbs: [get]} - {apiGroups: [batch], resources: [jobs], verbs: [create, get, list]} templateAuthors: - {kind: Group, name: platform-engineers} # 2. who may run the resulting templates, and against what - name: payments-oncall type: operation-access properties: namespace: payments-prod # where they work subjects: - {kind: Group, name: payments-oncall} applications: [payments] templates: - {namespace: vela-system, names: [s3-backup, restart-workload]}渲染结果——什么落在哪里What lands wherevela-system payments-prod ├── ServiceAccount/op-backup ├── Role/op-backup-ops │ identity only, zero permissions │ get secrets, create jobs │ ├── RoleBinding/op-backup-ops ├── Role/op-backup-use │ subject: vela-system/op-backup │ serviceaccounts/op-backup: [use] │ ├── RoleBinding/op-backup-use ├── Role/payments-oncall-operate │ subject: Group/platform-engineers │ applications/payments: [operate] │ │ operations: [create get list watch] ├── Role/payments-oncall-invoke └── RoleBinding/payments-oncall-operate │ operationtemplates/s3-backup, subject: Group/payments-oncall │ restart-workload: [invoke] └── RoleBinding/payments-oncall-invoke subject: Group/payments-oncall三个主体三组互不相交的权限——这是整个设计全部的意义所在That disjointness is the whole point设计文档建议对照实际渲染输出核验而不是凭信任接受platform-engineers可以在模板中指名op-backup持有use却不能创建 Jobpayments-oncall可以对payments运行s3-backup持有invokeoperate其任何授予都不提及op-backupop-backup可以创建 Jobs、读取 Secrets但它是一个没有人登录的身份只有在某个 operation 借用它时才有所作为。还有一个日常运维层面的收益删除这个 Application 会移除全部九个对象。手工在多 namespace 间维护一整套RoleBinding往往做不到这一点——这正是用 Application 交付 RBAC相对手工 YAML 的实质优势与 KEP 主文档中operation-permissions示例Declaring what a template needsThree grants, one object……Removing the Application removes all of it的表述完全一致。管理模式这套形状支持的四种治理方式invoke授予必然落在vela-system因为 RBAC 授予存在于资源所在之处而模板在那里。所以一个团队的访问权限被拆在两个 namespace 里团队管理员无法单独授予 invoke 那一半。设计文档明确承认这是正确的否则团队就能自行授予集群上任意模板但也有真实的管理成本——把新模板加入团队的轮值表需要一位对vela-system有写权限的人。不过这种形状并不强制任何特定运营模式。这里的每个授予都是普通Role因此同样两个组件可以支撑模式如何实现集中治理两个组件都由平台团队应用团队提交变更请求委托目标平台持有operation-permissions与 invoke 授予团队拥有自己的operation-access负责operate和create边界内完全自服务团队通过通配符Role持有某 namespace 全部模板的invoke自由挑选按流程锁定每条规则都带resourceNames加上对需要它的模板设置requireDirectGrant主文档对四种模式不持立场、也不应持立场The verbs and the two checks are the fixed part; how a platform arranges them is a matter for the platform.动词和两道检查是固定部分如何编排是平台自己的事。值得补充的一个细节来自主文档 Templates resolve two-tier, identity does not模板按Operation 所在 namespace →vela-system两级解析团队可以本地发布或遮蔽模板但ServiceAccount 只在模板所在 namespace 按模板命名的名字解析不查任何其他位置——两级查找会让谁创建账户决定过程以谁的身份运行一个团队可以创建同名账户悄悄替换平台模板使用的身份。遮蔽定义是覆盖代码熟悉且有意为之静默拾取碰巧存在的账户是覆盖权限不该意外发生。安全前提与边界Caveats设计文档给出两条明确的边界第一这套组件只在已认证姿态authenticated posture下才真正安全。Kubernetes 会阻止 subject 创建授予比自身持有权限更多的Role——但这个升级检查只在平台模拟应用者身份impersonate时才约束到应用者。当authentication.enabled关闭时Application 以 controller 的身份 apply升级检查不会生效。这对任何携带 RBAC 对象的 Application 都成立所以这是一条需要了解的既有属性而非本文档引入的新问题。主文档也印证了这一点Application 侧的身份注解拒绝规则实现在 pkg/webhook/core.oam.dev/v1beta1/application/mutating_handler.gohandleIdentity。第二这些是示例不是交付物。KEP-2.15 没有任何部分依赖它们存在。它们存在的意义是证明权限模型可以解析为平台管理员早已认识的对象且配对成立——模板在requires:中声明需要什么组件供应它两者是同一张清单。同理invoke、operate、use的 admission 检查基线在 KEP 主文档中有完整的决策树对OperationTemplate作者检查其引用的每个WorkflowStepDefinition、派生的子模板、以及runAs命名的 ServiceAccountuse对Operation创建者检查目标operate、模板invoke、目标集群operateonVirtualCluster、以及标记了requireDirectGrant的子模板。所有拒绝都发生在kubectl apply时刻而不是运行中途。这沿用了 Application 侧checkDefinitionPermission/ValidateDefinitionPermissionspkg/webhook/core.oam.dev/v1beta1/application/validation.go针对 X-Definitions 的既有模式只是把动词从get换成invoke并把目标从 Application 换成模板 目标。待决问题For next week设计文档以四个开放问题收尾展示该模型仍在演进中operation-access是否应该支持按 label selector 而非按名字授予invoke让模块发布的模板能被团队现有授予自动拾取。便捷但会悄悄移除按名字授予所强制的那道评审步骤。allowIrreversible是否是正确的粒度还是operate-irreversible应该像operate一样做到 per-Application。operations上的create规则是否应带resourceNames——鉴于名字由调用方在创建时选择、无法预知。这些组件是否应进入某个 addon还是作为本 KEP 的参考资料留在文档中。这些问题与 KEP 主文档 Open Questions 中关于渲染方式Option 1/2/3、参数声明、$(parameter.*)表达式的讨论相互独立——本设计聚焦的是权限对象的落地形状不依赖那些选择。与同目录其他设计文档的关系本设计是 KEP-2.15 四份设计文档之一理解它时值得放在一起看01-application-wrapper.md未采纳Operation 作为临时 Application 渲染的替代执行模型与本文的 RBAC 组件正交02-permission-scenarios.md把同一权限模型跑遍五个真实场景Alice/Bob/Carol、dev/oncall/oncall-senior 三种角色、四份模板可看到invoke拒绝场景 2、requireDirectGrant拒绝场景 3、按团队有界化的中央账户场景 4与OperationsRunAsInvoker的成本场景 5——它正是本设计文档 TL;DR 中每次权限拒绝都落在kubectl apply的实证04-cluster-scope.md把目标类型扩展到集群本身。从源码结构看以pkg/下现有实现为证这套设计几乎没有引入新机制身份注解与解析复用 pkg/auth 与 pkg/oam/labels.goadmission 检查复用 Application 定义校验的既有模式WorkflowStepDefinition的 scope 标签如 vela-templates/definitions/internal/workflowstep/apply-component.cue 中的labels: {scope: Application}由 pkg/definition/definition.go 写入 CR 元数据——设计文档的全部主张都可以在现有代码中找到对应物。这正是用已经存在的对象表达新权限模型这句话的分量所在。赞分享云原生DevOps运维微服务【免费下载链接】kubevelaThe Modern Application Platform.项目地址https://gitcode.com/gh_mirrors/ku/kubevela点击查看免费下载相关推荐6大实战技巧如何用DBeaver数据可视化功能提升数据库分析效率6大实战技巧如何用DBeaver数据可视化功能提升数据库分析效率 DBeaver 作为一款强大的开源数据库管理工具其内置的数据可视化功能为数据库开发者和数据数据库客户端桌面应用数据库Spacedrive 基于 Action System 的 RBAC 权限系统设计角色、权限与用户组落地指南SEC-004Spacedrive 基于 Action System 的 RBAC 权限系统设计角色、权限与用户组落地指南SEC 004 本文是 Spacedrive桌面应用移动开发后端存储数据同步为什么选择LUYA探索这款CMS框架的10大优势与特性为什么选择LUYA探索这款CMS框架的10大优势与特性 LUYA是一款高性能的Web框架和内容管理系统专为满足开发者、客户和用户的需求而设计。它采用模块化架后端上一篇如何在0.5秒内从单张图片生成专业3D模型TripoSR完整指南下一篇Fedora-Hyprland ZSH与Oh-My-Zsh集成打造终极命令行体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考