Files
pikasTech-HWLAB/docs/reference/spec-v02-openfga-authorization.md
T
2026-06-05 11:11:15 +08:00

237 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# v0.2 OpenFGA 授权与 Admin Access 管理规格
本文是 HWLAB `v0.2` 细粒度资源授权、OpenFGA 接入、管理员 Access WebUI 和同路径 CLI 的长期规格。Keycloak 只回答“用户是谁”;OpenFGA 回答“该用户能不能对该对象做该动作”;`hwlab-cloud-api` 仍是唯一应用层 enforcement point 和授权写入口。
本规格补充 [spec-v02-auth.md](spec-v02-auth.md) 和 [spec-user-access.md](spec-user-access.md):前者定义 Keycloak/Web session/API key 到 `AuthPrincipal` 的认证归一;后者定义用户、session owner、device pod 和工具能力的业务权限口径;本文定义 OpenFGA 如何在 `hwlab-v02` namespace 内以 Kubernetes 原生方式落地,并如何被 Cloud Web 与 `hwlab-cli client` 管理和验证。
实施跟踪 issue 必须记录 spec、GitOps、cloud-api、Cloud Web、CLI、测试、PR/CI/CD 和原入口验收进展。过程记录写 issue 评论,本文只保留稳定目标和验收口径。
## 在系统中的职责划分
| 组件 | 职责 |
| --- | --- |
| Keycloak | 身份认证、注册、OIDC issuer;不直接授予 HWLAB 功能权限。 |
| `hwlab-cloud-api` | `AuthPrincipal` 恢复、OpenFGA store/model bootstrap、授权 check、授权写 API、enforce 策略、AgentRun transient env 过滤和审计摘要。 |
| OpenFGA | `hwlab-v02` namespace 内部 PDP 和 relationship tuple store;只接受 cloud-api 调用,不向浏览器、CLI、AgentRun runner 或公网暴露。 |
| HWLAB v0.2 Postgres | 业务对象、用户、session、API key、access_tuples 摘要、OpenFGA store/model 指针和迁移 ledger 的 durable source。 |
| `hwlab-cloud-web` | Admin Access 页面和同源 API proxy;不直接调用 OpenFGA。 |
| `hwlab-cli client` | Web 等价非视觉授权管理入口;通过 `19666` Cloud Web 同源 path 调 cloud-api,不直连 OpenFGA。 |
| AgentRun v0.1 runner | 只消费 cloud-api 根据用户权限注入的短期工具环境;不持有跨用户共享 device-pod key、GitHub token 或 UniDesk SSH token。 |
OpenFGA 是 HWLAB 应用层授权基础设施,不是 Kubernetes RBAC、ServiceAccount、NetworkPolicy 或 Keycloak realm role 的替代。普通用户不获得 kubeconfig、内部 Service 直连、OpenFGA token 或 Keycloak admin 权限。
## Kubernetes 和 GitOps 落点
OpenFGA 固定部署在 `hwlab-v02` namespace,作为稳定外部服务进入 v0.2 GitOps desired state
- `deploy/gitops/g14/runtime-v02/openfga.yaml` 声明 `Deployment``Service`、migration `Job` 或 init/migrate step、SecretRef 和 health/readiness。
- `deploy/gitops/g14/runtime-v02/kustomization.yaml` 必须引用 `openfga.yaml`render 脚本如果有 service inventory 或 artifact catalog,也必须加入 OpenFGA 的外部服务记录。
- OpenFGA Service 只允许 ClusterIP,默认 DNS 为 `hwlab-openfga.hwlab-v02.svc.cluster.local`;不得配置 FRP、公网 Ingress 或 Cloud Web 直连。
- 数据存储使用 Postgres backend;禁止 production/stable runtime 使用 memory backend。可复用 `hwlab-v02-postgres` 实例,但必须使用独立 database/schema/role 和独立 SecretRef,避免和 cloud-api migration ledger 混写。
- 首次部署和版本升级必须执行 OpenFGA migratemigration 失败时 cloud-api OpenFGA readiness 必须 degraded,不能进入 enforce。
- OpenFGA 鉴权使用 preshared key 或等价内部服务 token SecretRefSecret 值不得出现在 ConfigMap、日志、issue、trace 或 CLI 默认输出。
- Readiness 使用 OpenFGA health endpointcloud-api `/health/live` 汇总 OpenFGA endpoint、store/model status、mode 和 degraded reason,但不输出 token。
OpenFGA 官方 Helm chart 可以作为 YAML 来源,但 `hwlab-v02` desired state 仍以 repo 内 GitOps render 后的 YAML 为准。任何 OpenFGA 镜像、SecretRef、Postgres URL 或 migration 变更都属于运行面/权限高风险变更,必须走 PR、CI/CD 和原入口验收。
## 授权模型
对象命名必须稳定、可从业务 ID 直接推导,并在日志/trace 中可脱敏展示:
| 对象 | 例子 | 说明 |
| --- | --- | --- |
| 用户 | `user:usr_123` | HWLAB `users.id`,不是 Keycloak `sub`。 |
| 系统 | `system:hwlab` | 平台级 admin、access 管理和全局工具授权。 |
| Device Pod | `device_pod:device-pod-71-freq` | 服务端 profile authority 对象。 |
| Code Agent session | `agent_session:ags_123` | 会话 owner/collaborator/viewer 判定。 |
| 工具 | `tool:hwpod``tool:unidesk_ssh``tool:github_pr``tool:trans_cmd` | AgentRun runner 可注入或可调用的功能能力。 |
目标关系模型:
```fga
model
schema 1.1
type user
type system
relations
define admin: [user]
define access_manager: admin
define can_manage_users: admin
define can_manage_device_pods: admin
define can_manage_tools: admin
type device_pod
relations
define owner: [user]
define viewer: [user] or owner or admin from system
define operator: [user] or owner or admin from system
define profile_editor: [user] or owner or admin from system
define job_submitter: [user] or operator
define admin: admin from system
type agent_session
relations
define owner: [user]
define viewer: [user] or owner or admin from system
define operator: [user] or owner or admin from system
define admin: admin from system
type tool
relations
define can_use: [user] or admin from system
define admin: admin from system
```
实现可以在第一版用等价模型名称和 relation 名称,但必须保持以下能力语义:
| 功能 | Check |
| --- | --- |
| 管理用户和权限 | `user:<id> access_manager system:hwlab``admin system:hwlab` |
| 查看 device pod | `viewer device_pod:<id>` |
| 提交 device pod job / 使用 `hwpod` | `operator``job_submitter device_pod:<id>`,并要求 `can_use tool:hwpod` |
| 修改 device pod profile | `profile_editor device_pod:<id>``admin system:hwlab` |
| 查看自己的 Code Agent session | `viewer agent_session:<id>` |
| 继续/取消 Code Agent session | `operator agent_session:<id>` |
| 注入 UniDesk SSH 透传能力 | `can_use tool:unidesk_ssh` |
| 允许 trans 透传 cmd | `can_use tool:trans_cmd`,且仍受 UniDesk route/operation 边界限制 |
| GitHub PR/issue 写操作工具 | `can_use tool:github_pr` 或更细工具对象 |
`admin/user` 仍保留为 UX 和 bootstrap 角色,但 runtime 授权必须逐步迁移到 OpenFGA check。`users.role='admin'` 必须镜像为 `system:hwlab#admin@user:<id>` tuple;降级 admin 时必须同步删除 tuple。Keycloak realm role、group、claim 不能直接变成 OpenFGA tuple,除非由 HWLAB admin API 明确写入。
## Cloud API 授权策略
`hwlab-cloud-api` 是唯一 policy enforcement point。所有用户态请求按以下顺序处理:
```text
authenticate -> AuthPrincipal -> load domain object -> openfga check -> reason check for mutating device jobs -> execute
```
配置项:
| 配置 | 说明 |
| --- | --- |
| `HWLAB_OPENFGA_MODE=enforce` | v0.2 正式运行面固定以 OpenFGA 为准;OpenFGA 不可达或写入失败时 fail closed 并返回结构化 blocker。 |
| `HWLAB_OPENFGA_API_URL` | 集群内 OpenFGA Service URL。 |
| `HWLAB_OPENFGA_AUTHN_TOKEN` 或 SecretRef | cloud-api 调 OpenFGA 的内部 token。 |
| `HWLAB_OPENFGA_STORE_ID` / `MODEL_ID` | 可由 env 注入,也可由 cloud-api bootstrap 后写入 Postgres runtime config。 |
| `HWLAB_OPENFGA_TIMEOUT_MS` | 单次 check/write 超时;超时必须结构化返回。 |
模式语义:
- `enforce` 是唯一正式运行模式:device pod、agent session 和工具能力以 OpenFGA check 为准。OpenFGA 不可达、store/model 未就绪或 check 超时时,高风险写操作 fail closed;低风险只读可以返回 degraded blocker,不得静默放行。
- `off` / `shadow` 不作为 v0.2 runtime 目标路径;旧文档、测试或 render 如再次把它们作为业务 allow source,应优先删除而不是兼容。
tuple 写入必须由 cloud-api admin API 统一完成,并和 Postgres domain state 保持事务级或可恢复一致:
- 用户创建、禁用、角色提升/降级时同步写 `users``system:hwlab` tuples。
- device pod 创建/更新/删除时同步 `device_pods` 和相关 tuples;删除对象时清理 tuple 或标记不可用。
- Code Agent session 创建时写 `agent_session:<id>#owner@user:<owner>`;取消/归档不删除 owner tuple,便于审计和 trace 回放。
- API key `scopes_json` 只能作为用户权限的收窄条件,不能授予超过 OpenFGA 的能力。
## Admin Access API
Cloud Web 和 CLI 只能通过 cloud-api 的 admin API 管理授权。第一版 API surface
| 接口 | 说明 |
| --- | --- |
| `GET /v1/admin/access/summary` | 返回 mode、OpenFGA readiness、storeId/modelId、user/tool/device-pod 数量和最近 mismatch 摘要。 |
| `GET /v1/admin/access/users` | 列出用户、role/status、Keycloak 绑定摘要、API key 数量和 effective capability 摘要。 |
| `GET /v1/admin/access/users/{userId}` | 返回单个用户的 device pod、agent session 和 tool 权限矩阵。 |
| `PATCH /v1/admin/access/users/{userId}` | 更新用户 `role/status`,并同步 OpenFGA admin tuple。 |
| `PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 授予 `viewer/operator/profile_editor/job_submitter` 等 relation。 |
| `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 撤销指定 device pod relation。 |
| `PUT /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | 授予工具能力,例如 `hwpod``unidesk_ssh``github_pr``trans_cmd`。 |
| `DELETE /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | 撤销工具能力。 |
| `POST /v1/admin/access/check` | 管理员调试单次 authorization check;响应必须标明 actor/object/relation/mode,但不得泄漏 token。 |
所有 write API 必须要求当前 actor 具备 `access_manager system:hwlab` 或 admin tuple;普通 `user` 不可调用。响应必须包含结构化 `authorization` 字段:`mode``allowed``decisionSource``storeId``modelId``relation``object` 和 redacted actor。
## Admin Access WebUI
Cloud Web 新增 ActivityRail 顶层入口 `Access`,只对具备 access manager/admin 权限的用户显示。它不是 Keycloak 管理后台,也不放在 Settings 子页里。
页面布局:
- 左栏:用户列表、搜索、role/status 筛选、Keycloak 绑定摘要。
- 中栏:选中用户的权限矩阵,按 `Device Pods``Agent Sessions``Tools``Platform` 分组;权限用 checkbox/toggle 表达,危险工具用显式确认。
- 右栏:effective permission 预览、最近变更、OpenFGA mode/readiness、写入 blocker 和保存结果。
交互规则:
- 初始加载先调用 `GET /v1/admin/access/summary` 和用户列表;OpenFGA degraded 时页面必须显示 blocker,不允许静默按旧 grant 表展示为通过。
- 修改权限时先在本地形成 pending diff;保存后逐项调用对应 PATCH/PUT/DELETE API,并刷新 effective matrix。
- 页面不暴露 OpenFGA tuple 原文作为主要操作面;需要排障时只在详情中显示 redacted object/relation。
- 普通用户或未登录用户访问 Access route 时显示授权 blocker 或跳登录,不渲染空管理表。
## 同路径 CLI
`hwlab-cli client` 必须提供与 WebUI 同一 API surface 的非视觉入口,默认由 runtime namespace/lane 解析到 `19666` Cloud Web origin
| CLI | Web/API 等价 |
| --- | --- |
| `client access summary` | `GET /v1/admin/access/summary` |
| `client access users list` | `GET /v1/admin/access/users` |
| `client access users inspect USER` | `GET /v1/admin/access/users/{userId}` |
| `client access users set-role USER --role admin|user` | `PATCH /v1/admin/access/users/{userId}` |
| `client access device-pods grant USER POD --relation viewer|operator|profile_editor|job_submitter` | `PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` |
| `client access device-pods revoke USER POD --relation ...` | `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` |
| `client access tools grant USER TOOL` | `PUT /v1/admin/access/users/{userId}/tools/{toolId}/can-use` |
| `client access tools revoke USER TOOL` | `DELETE /v1/admin/access/users/{userId}/tools/{toolId}/can-use` |
| `client access check --user USER --relation REL --object OBJECT` | `POST /v1/admin/access/check` |
CLI 输出必须是 JSON,包含 `runtimeEndpoint`、HTTP route、actor 摘要、mode、decision 和变更后的 effective matrix 摘要。它不能直接调用 OpenFGA API,不能手动传 OpenFGA token,不能把 `--base-url 19667` 作为 WebUI 等价验收路径。
## AgentRun 工具能力边界
Cloud API 在创建 AgentRun command/runner 时必须按 OpenFGA 决策装配 transient env 和工具说明:
- 用户没有 `can_use tool:hwpod` 时,不注入 `HWLAB_API_KEY``hwpod`,也不在 prompt/tools 中声明 device pod 操作可用。
- 用户没有目标 device pod 的 `operator`/`job_submitter` 时,即使拥有 `tool:hwpod`,具体 device pod job 也必须被 cloud-api 拒绝。
- 用户没有 `can_use tool:unidesk_ssh` 时,不注入 UniDesk SSH client token、workspace route 或相关 alias。
- `tool:trans_cmd` 只代表允许通过受控 UniDesk route 调用透传命令;它不绕过 UniDesk CLI 的 route/operation 安全边界,不允许 pod 内任意 Secret 读取或 Kubernetes 写操作。
- GitHub issue/PR 写入能力必须单独由工具对象授权;拥有 Code Agent session 不等于拥有 GitHub 写权限。
## 测试规格
## T1
阅读 docs/reference/spec-v02-openfga-authorization.md,然后检查 `deploy/gitops/g14/runtime-v02` 和渲染结果,确认 OpenFGA 以 ClusterIP-only 服务部署在 `hwlab-v02`,使用 Postgres backend、migration job/step 和 SecretRef`kustomization.yaml` 引用 OpenFGA 资源,且没有 FRP/Ingress 公网暴露。
## T2
阅读 docs/reference/spec-v02-openfga-authorization.md,然后访问 `GET /health/live``GET /v1/admin/access/summary`,确认响应显示 `openfga.mode`、readiness、storeId/modelId 摘要和 degraded reason;响应不得包含 OpenFGA token、Postgres URL 或 Secret 值。
## T3
阅读 docs/reference/spec-v02-openfga-authorization.md,然后用 admin 通过 CLI 或 WebUI 给普通用户授予某个 device pod 的 `viewer` 但不授予 `operator`,确认该用户可以看到 device pod 摘要,但提交 device job 返回 403;再授予 `operator/job_submitter` 后 job 可提交。
## T4
阅读 docs/reference/spec-v02-openfga-authorization.md,然后撤销普通用户的 `tool:hwpod``tool:unidesk_ssh`,创建新的 Code Agent session 并检查 trace/runner env 摘要,确认对应工具 alias/env 未注入;尝试调用时返回结构化 authorization blocker。
## T5
阅读 docs/reference/spec-v02-openfga-authorization.md,然后在 `enforce` 运行面授予和撤销普通用户的 device pod relation,确认 `/v1/device-pods`、status、job 和 Access summary 都按 OpenFGA 结果执行;OpenFGA 写失败时 Admin Access API 必须返回结构化 5xx blocker,不能更新本地 access matrix。
## T6
阅读 docs/reference/spec-v02-openfga-authorization.md,然后从浏览器打开 Access 页面,确认只有 admin/access manager 可见 ActivityRail 入口;普通用户访问 route 显示授权 blocker。保存一次权限变更后,CLI `client access users inspect USER` 必须看到同一 effective matrix。
## T7
阅读 docs/reference/spec-v02-openfga-authorization.md,然后在 runtime endpoint locked 环境运行 `hwlab-cli client access summary/users/check/grant/revoke`,确认全部走 Cloud Web 同源 `19666` path,输出 JSON、route、actor、mode、decision 和 effective matrix;不手动传 OpenFGA URL/token,不直连 `19667` 作为最终 Web 等价验收。
## 规格的实现情况
| 规格项 | 状态 | 说明 |
| --- | --- | --- |
| OpenFGA 作为 v0.2 内部授权服务 | 目标状态 | 需要 GitOps resource、Postgres backend、migration 和 cloud-api readiness。 |
| Cloud API OpenFGA client/bootstrap/check/write | 目标状态 | 需要 `enforce`、store/model 指针、tuple write 和 structured decision。 |
| 细粒度 device pod / session / tool 授权 | 目标状态 | 替代旧“device pod grant 即全权限”口径。 |
| Admin Access WebUI | 目标状态 | 新增 admin-only ActivityRail 页面,不直接调用 OpenFGA。 |
| 同路径 CLI | 目标状态 | 新增 `client access ...`,走 Cloud Web 同源 path。 |
| AgentRun 工具注入按用户权限过滤 | 目标状态 | `hwpod``unidesk_ssh``trans_cmd`、GitHub 写工具都必须独立授权。 |