From fb0ae15b12f3b2f217737ad7359861462ccc5445 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 30 Jun 2026 07:02:11 +0000 Subject: [PATCH] docs: add ESO secret UI spec --- docs/reference/spec-v03-eso-secret-ui.md | 198 +++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/reference/spec-v03-eso-secret-ui.md diff --git a/docs/reference/spec-v03-eso-secret-ui.md b/docs/reference/spec-v03-eso-secret-ui.md new file mode 100644 index 00000000..63a9ca03 --- /dev/null +++ b/docs/reference/spec-v03-eso-secret-ui.md @@ -0,0 +1,198 @@ +# HWLAB v0.3 ESO 密钥管理 UI 规格 + +SPEC: HWLAB-ESO-UI-20260630 ESO UI draft-2026-06-30-p0. +Issue: [#2292](https://github.com/pikasTech/HWLAB/issues/2292). +上级: [#2233](https://github.com/pikasTech/HWLAB/issues/2233), [#2277](https://github.com/pikasTech/HWLAB/issues/2277). +参考: Weave GitOps 0.22.0 Manage Secrets UI, HWLAB `config/hwlab-v03/secrets.yaml`. + +本文只定义 `hwlab.pikapython.com` 的 ESO / External Secrets Operator 密钥管理界面。它不是生产 Secret 迁移方案,也不触发部署、apply、rollout 或后端选型变更。 + +## 范围 + +- 在 Cloud Web 管理侧边栏新增 `密钥` 入口,路由建议为 `/admin/secrets`,`navId` 为 `admin.secrets`,排序固定在 `授权` 与 `用户` 之间。 +- 仅覆盖 ESO、`ExternalSecret`、`SecretStore`、`ClusterSecretStore`、目标 Kubernetes Secret cache 和 consumer rollout 可视化。 +- 默认上下文由同源 API 返回的 active target 决定;当前 JD01 作为 [#2277](https://github.com/pikasTech/HWLAB/issues/2277) 设计目标展示,不在前端硬编码。D518 和 D601 只作为历史或明确选择的 target。 +- SOPS、age/GPG、Git 加密文件、PR 生成、手工 `kubectl` 指令和 secret value 输入框不进入本界面。 + +## 信息架构 + +管理区导航顺序: + +```text +管理 + 授权 /admin/access admin.access + 密钥 /admin/secrets admin.secrets + 用户 /admin/users admin.users + 账务 /admin/billing admin.billing + HWPOD /admin/hwpod-groups admin.hwpodGroups + Profiles /admin/provider-profiles admin.providerProfiles +``` + +`admin.secrets` 必须受 HWLAB RBAC 控制。无权限用户看不到导航项;只读用户可看脱敏状态,不能进入 apply、rotate、rollback 或 destructive 操作。 + +## 页面结构 + +### 概览 + +首屏顶部展示当前 target 摘要,不把 target 写死在前端: + +- node、lane、public URL、runtime namespace、infra namespace。 +- ESO controller / webhook / cert-controller readiness。 +- `SecretStore` / `ClusterSecretStore` readiness 和 store scope。 +- 最近一次 sync 时间、最近一次 validate 时间、当前 fingerprint 对齐摘要。 +- consumer rollout 摘要:关联 Deployment/Job 数量、ready 数量、pending 数量、最近一次 fingerprint 触发的 rollout revision。 +- `valuesPrinted=false` 固定可见。 + +概览区域只显示状态、引用和下一步 drill-down;不显示教程式长文案,也不提供复制 secret value 的入口。 + +### ExternalSecrets 列表 + +列表列字段: + +- `ExternalSecret` 名称、namespace、target node/lane、目标 Secret 名称。 +- `SecretStore` / `ClusterSecretStore` 名称、store scope。 +- 状态:`Ready`、`NotReady`、`Pending`、`SyncError`、`Unknown`。 +- 最近刷新时间、创建时间、refresh interval。 +- 目标 Secret key presence、fingerprint 摘要、resourceVersion。 +- 关联 consumer 与 rollout 状态。 +- 操作:详情、事件、复制脱敏诊断摘要、打开受控 validate 结果。 + +列表不得提供复制、下载、解码或 reveal secret value 的操作。 + +### ExternalSecret 详情 + +详情页分区: + +- 状态摘要:Ready/NotReady、LastRefreshTime、LastTransitionTime、reason、message 摘要。 +- 基本信息:ExternalSecret、target Secret、node/lane、namespace、store kind/name/scope。 +- 数据映射:remoteRef path、property、version、target key、fingerprint;路径可脱敏或 hash,不能显示 value。 +- 目标 Secret:key presence、fingerprint、resourceVersion、valuesPrinted=false。 +- Consumer:Deployment/Job、image、pod readiness、rollout revision、template fingerprint annotation、最后 validate 结果。 +- Provenance:配置来源文件、Git source commit、GitOps revision、Argo revision 或 status job id。 + +### 创建/编辑向导 + +本 issue 只设计向导,不启用生产 apply。向导提交后的默认动作是 dry-run/plan。 + +1. 选择 node/lane/namespace。默认值来自 API active target;JD01/v0.3 只有在 API 标记为 active 时自动选中。 +2. 输入 ExternalSecret 名称和目标 Kubernetes Secret 名称。 +3. 选择 `SecretStore` 或 `ClusterSecretStore`。namespace scoped store 只能绑定同 namespace;cluster scoped store 必须显示目标 namespace。 +4. 配置 remoteRef path/property/version、target key、refresh interval、consumer rollout 标记。 +5. 展示 dry-run/plan 结果、受影响 consumer、脱敏摘要和风险提示。 + +未来 apply 能力必须走 HWLAB/UniDesk 受控 API;前端不展示裸 `kubectl`、Helm 或 YAML 复制执行步骤。 + +### 事件与排障 + +事件页按资源归组,覆盖: + +- Store auth failed。 +- Store unavailable。 +- ExternalSecret sync failed。 +- Target Secret missing。 +- Consumer rollout pending。 +- Consumer validation failed。 +- Unknown / stale status。 + +每条事件显示时间、资源、reason、message 摘要、建议 drill-down 和关联 trace/job id。message 需要经过后端脱敏;如果后端判定消息可能含有凭据,前端只显示 redacted reason 和 raw drill-down id。 + +## 状态模型 + +状态优先级从高到低: + +1. `SecretLeakBlocked`: 发现响应含疑似 value、base64 payload、token、DSN、password 或 auth header,整页进入 blocked,要求后端修脱敏。 +2. `StoreUnavailable`: ESO 可达但 Store 不 Ready 或认证失败。 +3. `SyncError`: Store Ready,但 ExternalSecret 同步失败。 +4. `TargetMissing`: ExternalSecret Ready 但目标 Secret/key 不存在。 +5. `RolloutPending`: 目标 Secret fingerprint 更新,但 consumer 未滚动到新 fingerprint。 +6. `ValidationFailed`: rollout 已完成,但原入口 validate 失败。 +7. `Ready`: Store、ExternalSecret、target Secret、consumer fingerprint 和 validate 全部对齐。 +8. `Unknown`: 信息缺失、API 超时或 status 过期。 + +UI 可以把 `Pending` 作为 `Unknown` 与明确错误之间的中间态,但不得把 `Pending` 渲染成成功。 + +## API 契约草案 + +所有接口走 Cloud Web 同源 `/v1/admin/secrets/*`,由后端调用受控 node/lane API、GitOps status 或 UniDesk CLI。前端不得直连 Kubernetes API、Vault/OpenBao、ESO webhook 或 platform-infra 服务。 + +```text +GET /v1/admin/secrets/summary +GET /v1/admin/secrets/external-secrets?node=&lane=&namespace= +GET /v1/admin/secrets/external-secrets/:namespace/:name +GET /v1/admin/secrets/events?node=&lane=&limit= +POST /v1/admin/secrets/external-secrets/plan +POST /v1/admin/secrets/external-secrets/validate +``` + +未来写入接口需要单独 issue 评审: + +```text +POST /v1/admin/secrets/external-secrets/apply +POST /v1/admin/secrets/external-secrets/rollback +POST /v1/admin/secrets/rotate +``` + +响应共同字段: + +```json +{ + "ok": true, + "contractVersion": "hwlab-admin-secrets-v1", + "target": { + "node": "JD01", + "lane": "v03", + "runtimeNamespace": "hwlab-v03", + "infraNamespace": "platform-infra", + "source": "yaml-active-target" + }, + "valuesPrinted": false, + "secretMaterialStored": false, + "generatedAt": "2026-06-30T00:00:00.000Z" +} +``` + +错误响应必须包含 `valuesPrinted=false`、`layer`、`code`、`retryable` 和可审计 request/trace id。后端不能把 live Secret 解码结果作为 response 的任何字段。 + +## 脱敏规则 + +允许展示: + +- 对象名、namespace、node、lane、store kind/name、scope。 +- target Secret 名称、target key 名、presence、resourceVersion。 +- remoteRef path/property/version 的脱敏形式。 +- fingerprint、字节数、状态、reason、时间、trace id、job id。 +- `valuesPrinted=false`、`secretMaterialStored=false`。 + +禁止展示: + +- Secret value、base64 payload、token、password、API key、Authorization header、Cookie、DSN、private key、Vault root token。 +- 可直接还原凭据的完整 JSON、TOML、YAML、env dump、stdout/stderr。 +- 从 runtime Secret、pod env、日志或数据库反推 sourceRef 的结果。 + +如果任一后端字段命中凭据模式,后端必须删除或替换为 fingerprint;前端发现疑似泄露时进入 `SecretLeakBlocked`。 + +## 权限 + +- `admin.secrets.read`: 读取概览、列表、详情、事件和 validate 结果。 +- `admin.secrets.plan`: 提交 dry-run/plan,不写运行面。 +- `admin.secrets.apply`: 未来 apply/rollback/rotate;本 SPEC 不启用。 +- `admin.secrets.audit`: 查看操作历史、trace、job id 和 rollout provenance。 + +权限来自 HWLAB Access/RBAC,同一用户 session 下不新增第二套登录或临时 token。 + +## 验收标准 + +- 侧边栏设计明确 `密钥` 位于 `授权` 和 `用户` 之间。 +- 页面覆盖概览、ExternalSecrets 列表、详情、创建/编辑 dry-run 向导、事件与 rollout/validate 可视化。 +- 所有 target 默认值由 API/YAML active target 解析,前端不硬编码 JD01、D518、D601 或 G14。 +- UI 只覆盖 ESO;没有 SOPS、age/GPG、Git 加密文件、PR 工作流或 secret value 输入框。 +- API 草案只通过同源受控 API 暴露脱敏状态,不要求用户执行裸 `kubectl`。 +- 任意页面、事件和错误态都只能展示 presence、fingerprint、状态、reason 和引用,不展示 secret value。 +- 本设计可以映射现有 `config/hwlab-v03/secrets.yaml` 的 `secretPlane`、`ExternalSecret`、target Secret 和 consumer envRef。 + +## 后续阶段 + +- P1 只读 MVP:新增导航、概览、列表、详情和事件页,后端只读 status。 +- P2 dry-run:新增创建/编辑向导和 plan API,不执行 apply。 +- P3 validate/rollout 可视化:串联 Secret fingerprint、consumer rollout revision 和原入口 validate 结果。 +- P4 受控 apply/rollback:必须另开 issue,确认生产 backend、RBAC、审计、rollback 和 rollout 验收后才能实现。