- 云原生
- DevOps
- 运维
- 微服务
【免费下载链接】kubevela
The Modern Application Platform.
KubeVela vNext(2.0)架构将控制平面拆分为Hub(应用控制器)与Spoke(组件控制器)两级,而 KEP-2.5 正是支撑这套多集群体系的安全基石:它定义了 spoke 注册时的引导凭据建立、基于 Secret 消息总线的令牌轮换协议,以及派发完整性的 HMAC 签名机制。读完本文,你将掌握该凭据模型的三层密码学设计(ECDH 密钥协商、AES-256-GCM 信封、RSA-OAEP 前向保密)、令牌轮换的完整时序与循环依赖陷阱,以及派发完整性在 Component CR 上的落地形态,并能在当前仓库的 cluster-gateway 实现中找到对应的现实锚点。
阅读前提:本文描述的 KEP-2.5 是 vNext Roadmap 下的设计文档,仓库内该文件状态标注为Drafting(Not ready for consumption)——属于早期概念草案,方向尚未定稿,不应作为已承诺行为的实现依据,预期会有较大变更。文中所涉 KubeVela 现有多集群能力以当前仓库代码为准。
背景:Hub 与 Spoke 之间唯一的通信方向
KEP-2.5 的凭据设计建立在一条明确的通信拓扑之上:Hub 与 Spoke 之间的通信完全由 Hub 发起。Spoke 永远不会主动向 Hub 建立出站连接——它通过 cluster-gateway 被 Hub 读写,"响应"Hub 请求的方式是写入自己的本地 API,再由 Hub 通过 cluster-gateway 读回。
| 方向 | 机制 |
|---|---|
| Hub → Spoke | cluster-gateway(kubectl proxy) |
| Spoke → Hub | Spoke 写入本地 API;Hub 经 cluster-gateway 读回 |
这一"单向往返"模型意味着:spoke 侧无需任何持久化出站连接,也无需在防火墙/网络策略上为 spoke 反向访问 hub 开放端口。Hub 只需要一个能在目标集群上执行读写操作的凭据通道即可。
在仓库现有实现中,这条通道的真实形态可以从多集群管理代码里找到对应物。pkg/multicluster/utils.go中GetClusterGatewayService会校验 cluster-gateway 的 APIService 是否处于Available状态,WaitUntilClusterGatewayReady则最多等待 5 分钟(60 次重试、每次 5 秒间隔)等待网关就绪;cmd/core/app/config/multicluster.go中--enable-cluster-gateway标志(默认关闭)控制是否启用该多集群通道。当前实现的凭据以 Secret 形式存放在 cluster-gateway 命名空间(默认vela-system,见pkg/multicluster/utils.go中的ClusterGatewaySecretNamespace),并通过cluster.core.oam.dev/cluster-credential类型标签区分凭据种类(如 ServiceAccountToken 或 X509 证书,见pkg/multicluster/cluster_management.go)。
Bootstrap:Spoke 注册时的引导凭据
当运维人员从 Hub 侧注册一个 Spoke 时(提供一份由操作者提供的 kubeconfig),KEP-2.5 定义了一个一次性引导流程,其核心目标是在不传输共享秘密的前提下,让双方各自独立推导出相同的对称密钥:
- Hub 生成 ECDH 密钥对(P-256),专用于该 spoke 关系;
- 双方执行密钥协商,推导出一个共享的AES-256-GCM 对称密钥——任何一方都不在网络上传输这个秘密,双方仅凭密钥对材料各自独立计算出相同值;
- Hub 存储推导出的对称密钥与 spoke 的公钥,存放于 Hub 上的
vela-system/spoke-credential-<spoke-name>; - Hub 通过引导 kubeconfig 将自己的公钥写入 spoke,存放为
vela-system/hub-credential; - Spoke 在本地推导出相同的共享对称密钥,并与 hub 公钥一起保存;
- 引导 kubeconfig 在注册完成后即被丢弃——此后所有通信都改用注册好的凭据走 cluster-gateway,不再依赖这份初始 kubeconfig。
这里引入一个关键概念:共享的 AES-256-GCM 对称密钥是"长期引导凭据"(long-lived bootstrap credential)。它跨越多轮令牌轮换周期持续存在,只有在显式的重新注册流程(见下文"引导凭据刷新")中才会被刷新。
这一"引导即丢弃"的形态与仓库中现有集群接入逻辑形成呼应:pkg/multicluster/cluster_management.go中的JoinClusterByKubeConfig从 kubeconfig 路径加载集群配置(LoadKubeClusterConfigFromFile),经Validate(拒绝空集群名、拒绝保留名local)与PostRegistration(创建命名空间、失败自动回滚 detach)后完成注册,注册后的长期凭据即转为 Secret 形式管理。
令牌轮换协议:以 Secret 为消息总线
令牌轮换完全由Hub 驱动。spoke 上的一个"请求/响应 Secret"充当消息总线:Hub 写入请求,spoke 更新为响应,Hub 再经 cluster-gateway 读回。整个协议分三步。
Step 1 — Hub 发起
- Hub 为本轮轮换周期生成全新的RSA-OAEP 密钥对(2048 位);
- Hub 构造载荷:
{ newPublicKey: <RSA 公钥>, requestId: <uuid>, issuedAt: <时间戳> }; - Hub 用共享 AES-256-GCM 对称密钥加密该载荷;
- Hub 将密文写入 spoke:
vela-system/token-rotation-<requestId>,并打上标签oam.dev/hub-request: token-rotation。
Step 2 — Spoke 处理
- Spoke 在本地 API 上监听带
oam.dev/hub-request: token-rotation标签的 Secret; - Spoke 用自己持有的共享 AES-256-GCM 对称密钥解密载荷;
- Spoke 生成或轮换 cluster-gateway 访问令牌;
- Spoke 用载荷中的Hub 新 RSA 公钥加密新令牌——此时该令牌只有持有对应私钥的人才能读取;
- Spoke 将
{ encryptedToken: <RSA 加密后的令牌>, requestId: <uuid>, respondedAt: <时间戳> }写回同一个 Secret。
Step 3 — Hub 收集
- Hub 经 cluster-gateway 监听该请求 Secret,检测到响应字段出现;
- Hub 用本轮新的 RSA 私钥解密令牌;
- Hub 将新令牌存入
vela-system/spoke-credential-<spoke-name>; - Hub 删除 spoke 上的请求 Secret;
- Hub 记录轮换事件用于审计。
协议的核心安全属性是令牌投递的前向保密(forward secrecy):每轮轮换都会生成全新的 RSA 密钥对,某轮周期的私钥一旦泄露,无法解密其他任何轮次的令牌——单点泄露被隔离在单个轮次之内。
轮换的循环依赖:一个必须提前设计的失败模式
KEP-2.5 特别指出一个架构级陷阱:轮换请求本身要经过 cluster-gateway 才能到达 spoke,而 cluster-gateway 恰好需要当前有效的凭据才能被访问。于是,"修复过期凭据的通道"与"被过期凭据封锁的通道"是同一条通道,形成循环依赖。
该文档给出的判断是:
- 单次轮换失败无害:旧令牌仍然有效,下一次尝试即可成功;
- 持续失败才是灾难:一旦凭据真正过期,就没有带内(in-band)方式投递新凭据,恢复手段只能是对每个受影响的 spoke 做手动重新注册——一个轮换路径上的短暂故障,会被放大成与集群规模成正比的操作事故。
因此这不是可以"事后发现"的问题,而必须在设计期解决:轮换必须提前于过期足够远的时间启动,使重试预算能覆盖任何合理的故障时长;告警必须针对"轮换失败"而不是"凭据已过期"——等到凭据过期再告警,通道已经消失了。这句话是本文档最值得运维团队直接摘录的设计准则。
Secret 生命周期与审计
请求 Secret 在 Hub 读取并存储响应后,会立即从 spoke 删除。Hub 侧的审计轨迹通过结构化的控制器日志维护:
INFO token-rotation spoke=prod-eu requestId=abc123 status=initiated INFO token-rotation spoke=prod-eu requestId=abc123 status=response-received respondedAt=... INFO token-rotation spoke=prod-eu requestId=abc123 status=complete secret-deleted=true这种"短命 Secret + 结构化日志"的组合,让每次轮换的发起、响应到达、完成与清理都留下可检索的记录,供标准日志管道直接抓取。
引导凭据刷新:重新注册流程
如果长期共享对称密钥需要人工轮换,操作者触发重新注册(re-registration)流程:提供一份全新的 kubeconfig、重复 ECDH 密钥协商、并在两侧覆写对称密钥。此后,现有的令牌轮换将在下一个调度周期开始使用新的对称密钥继续工作。
密码学原语一览
KEP-2.5 对每个环节选用的算法给出了明确理由:
| 用途 | 算法 | 理由 |
|---|---|---|
| 引导密钥协商 | ECDH(P-256) | 标准算法,无需传输密钥 |
| 请求/响应信封 | AES-256-GCM | 快速、带认证的对称加密 |
| 令牌投递 | RSA-OAEP(2048 位) | 即使对称密钥日后被攻破,令牌也仅 Hub 可读 |
| 密钥材料存储 | Kubernetes Secret | 标准机制;可切换为 KMS 后端(见 Open Questions) |
三者各司其职:ECDH 解决"不传输秘密地建立共享密钥",AES-256-GCM 解决"批量请求的高效认证加密",RSA-OAEP 解决"投递环节的前向保密与责任隔离"。密钥材料存放在 Kubernetes Secret 中,与仓库现有 cluster-gateway 凭据存储方式一致(Secret 存放在vela-system命名空间,见pkg/multicluster/cluster_management.go与pkg/multicluster/utils.go)。
派发完整性:共享密钥的另一重职责
共享的 AES-256-GCM 对称密钥同时充当派发完整性(dispatch integrity)机制。Hub 派发的每个 Component CR 都携带一个 HMAC-SHA256 签名注解,该签名基于 Component 的关键字段计算;spoke 的组件控制器在处理前校验签名,拒绝任何无法认证为来自 Hub 的 Component。
注解格式:
metadata: annotations: oam.dev/dispatch-sig: <HMAC-SHA256(canonicalPayload, sharedSymmetricKey)>规范载荷(canonical payload)是 Hub 承诺字段的确定性序列化:
componentName + namespace + definitionName + definitionRevision + propertiesHash + resourceVersionspoke 使用自己持有的共享对称密钥,从相同字段重新计算 HMAC:
- 匹配→ Component 真实且未被篡改,继续处理;
- 不匹配→ spoke 拒绝该 Component、发出 warning 事件,并且不进行渲染。
两个结构性保证值得强调:
- 跨 spoke 重放被结构性阻止:共享对称密钥是每 spoke 独立的,为
spoke-a签名的 Component 在spoke-b上必然校验失败——无需任何额外的 nonce 或 audience 字段; - 无 fail-open 模式:Hub 在每次修改 Component 的 reconcile 上都会刷新签名(属性变更、Definition 升级、trait 注入),而 spoke 将缺失或无效签名视为硬性拒绝——派发完整性没有"失败即放行"的后门。
这一设计与 Hub 侧的 Definition 快照机制互相咬合:在 vNext 的 Hub 应用中,Component.spec.definitionSnapshot.inline中的 Definition 快照会以每 spoke 的 AES-256-GCM 对称密钥加密,并用每 spoke 的 Definition 签名密钥在 Component CR 上打component.oam.dev/snapshot-signature注解(详见 KEP-2.3:Hub Application-Controller 集成)。Hub 侧 "snapshot 加密 + 签名注解",Spoke 侧 "Definition 快照验签 + Component 派发验签",共同构成 vNext 双向信任模型的两端。
与 vNext 路线图及相邻 KEP 的关系
KEP-2.5 是 vNext Roadmap 总纲(design/vela-core/keps/README.md)中"安全与凭据模型"板块的核心子 KEP。该板块的总体方向包括:不保留长期静态令牌(全部短生命周期并自动轮换)、双向信任(spoke 控制自身身份,Hub 无法为 spoke 铸造令牌;Hub 为每个 spoke 颁发独立签名密钥,单点沦陷不影响其他 spoke)、三要素令牌刷新(spoke 集群写权限 + Hub 每 spoke 签名密钥 + 安全通道,三者同时沦陷任何两个都不足以攻破)等。需要说明的是,KEP-2.5 文档正文中的轮换协议由 Hub 驱动,与总纲中"spoke 主动驱动凭据刷新、Hub 纯被动"的表述存在方向性张力,这正对应文档自身的 Drafting 状态——方向尚未定稿,阅读时应以"探索性设计"对待。
凭据模型在架构链条中的位置可以这样理解:
- KEP-2.1(
core.oam.dev/v2alpha1API 类型与 CRD)定义Component、Dispatcher等基础资源; - KEP-2.2(Spoke 组件控制器与工作流引擎)定义 spoke 侧的渲染、工作流、健康评估与 ResourceTracker,spoke 收到 Component 后先验签再渲染;
- KEP-2.3(Hub 应用控制器集成)定义 Hub 的 Definition 解析、快照加密签名、trait 注入与 Component 派发;
- KEP-2.4(Dispatcher 实现)定义 local、cluster-gateway、OCM 三种投递后端——KEP-2.5 中"经 cluster-gateway 读写信令 Secret"正是 cluster-gateway dispatcher 路径的凭据层保障;
- KEP-2.5(本文)为以上所有跨集群交互提供凭据建立、轮换与完整性校验。
仓库中可继续深入的相关实现入口:多集群注册与凭据管理的核心代码在 pkg/multicluster/cluster_management.go(含JoinClusterByKubeConfig、DetachCluster、凭据类型CredentialTypeServiceAccountToken/CredentialTypeX509Certificate),cluster-gateway 通道初始化在 pkg/multicluster/utils.go,多集群开关配置在 cmd/core/app/config/multicluster.go。
小结:三个值得带走的设计判断
- 分层密码学隔离风险:ECDH 建钥、AES-256-GCM 加密请求、RSA-OAEP 投递令牌,任一层的泄露都无法直接穿透其他层,单轮 RSA 私钥泄露更不会波及其他轮次;
- 把"凭据过期"视为事故而非告警条件:轮换必须提前启动、告警必须盯住轮换失败,否则循环依赖会把通道故障放大为与集群规模成正比的手工事故;
- 每 spoke 密钥 + 无 fail-open 的验签:派发完整性用"一 spoke 一密钥"的 HMAC 把跨 spoke 重放消灭在结构上,并把缺失签名当作硬性拒绝——这是 Hub/Spoke 架构里信任边界的具体落点。
- 云原生
- DevOps
- 运维
- 微服务
【免费下载链接】kubevela
The Modern Application Platform.
相关推荐
Hydra-AI(Tambo)自托管部署指南:基于 Docker Compose 的 Web、API 与 PostgreSQL 一键私有化部署
Hydra AI(Tambo)自托管部署指南:基于 Docker Compose 的 Web、API 与 PostgreSQL 一键私有化部署 本指南面向希望将
云原生DevOps运维微服务Civitai Monorepo 认证架构解析:Hub-Spoke 模式下的薄会话令牌、本地校验与跨域桥接
Civitai Monorepo 认证架构解析:Hub Spoke 模式下的薄会话令牌、本地校验与跨域桥接 本文以 Civitai 仓库中的认证目标架构规范 a
后端前端AI 应用Kubebuilder 多版本 API 转换实现指南:Hub-Spoke 模型与 Conversion Webhook 实战
Kubebuilder 多版本 API 转换实现指南:Hub Spoke 模型与 Conversion Webhook 实战 本文围绕 Kubebuilder
开发者工具代码生成CLI云原生后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考