P3 Apollo
Bundle 签名与验签:供应链安全
制品不是"一个压缩包传过去"就完了:bundle 自带逐文件 SM3 清单、不可变 digest、SM2 签名与签名者白名单信任锚。看完这 3 个故事,你就明白"验签通过"和"可信"为什么是两回事。
安全合规
平台运维
SM3
SM2
信任锚
共 3 个故事
能 / 不能速览
✅ 这个主题能做
- 上传文件构建签名 bundle:manifest + 逐文件 SM3 清单 + 不可变 digest + SM2 签名
- 全链路验签:files_ok / digest_ok / trusted / signature_ok 四项诊断
- 签名者白名单信任锚:指纹不在白名单(或已禁用)即拒绝应用(403 VERIFY_SIGNER_NOT_TRUSTED)
- 下载 zip 归档(manifest.json + digest.sig + files/),可离线读回再验
- 构建时自动把演示签名者占位指纹升级为真实 SM2 公钥指纹并注册白名单
⛔ 这个主题做不了
- verify 是诊断接口;真正应用时的拒绝逻辑在 Spoke 侧白名单预检(本版验签与白名单在控制面)
- 演示签名者私钥仅存服务器内存,重启后重新生成密钥,指纹会变化(需重新构建 / 更新白名单)
- SBOM / 漏洞扫描 / 策略门禁已实现(自研内置模拟,非真实 Trivy/Syft,OPA 未实现),但属于"安全合规"主题,bundle 签名层只管来源与完整性
- 签名者白名单要求密钥分发生命周期管理,当前只有手动 add / enable / disable
适用角色
- 应用开发工程师:把制品文件打包成 bundle,填 app / version / bundle_version 并上传文件。
- 安全合规工程师:验签、维护签名者白名单、评估供应链风险——本主题核心使用者。
- 平台运维 / SRE:构建与下载制品,配合安全团队完成信任锚演练。
能力速览(能做什么)
构建签名 bundle
POST /bundles multipart 上传文件,生成 manifest(文件清单 + 组件清单)+ 不可变 digest + SM2 签名。
全链路验签
POST /bundles/:id/verify:逐文件重算 SM3、重算 digest、白名单查指纹、SM2 验签,四项结果全绿才可信。
签名者白名单
POST /signers 维护信任锚(name + 公钥指纹 + enabled),指纹不在白名单或已禁用即拒绝应用。
不可变 digest
digest = 内容哈希,是 bundle 的唯一连接键(内容变则 digest 变);version 仅作展示语义。
下载与离线校验
GET /bundles/:id/download 返回 zip(manifest.json + digest.sig + files/),落盘后可读回再校验。
占位指纹自动升级
seed 的 demo-signer 占位指纹在首次构建 bundle 时自动替换为真实 SM3(公钥) 指纹,白名单自洽。
调整指南(怎么调整)
- 换签名者:构建时表单字段 signer 指定签名者(默认 demo-signer);组件 kind 按文件路径自动识别(config / eval_set / ontology_yaml 等)。
- 改信任锚:新供应商加入走 POST /signers(name + 公钥指纹);下线走 disable,同名 / 同指纹重复注册会 409 SIGNER_CONFLICT。
- 改校验粒度:验签四项逐项诊断,哪项红了就定位哪类问题(文件被改 / digest 对不上 / 白名单缺失 / 签名无效)。
- 改版本语义:bundle_version 建议按发布计划单调递增;bundle digest 变更即新制品,历史 bundle 仍可下载验签。
- 改分发方式:制品目录(temp/apollo_bundles/<digest>)可整体拷贝到离线环境,配合 zip 下载做二次分发。
做得好的场景
Bundle 签名最擅长"让制品来源与完整性可被证明":
- 供应链可视:bundle 从"一个包"变成"清单 + 签名 + 指纹",谁打的、含什么、有没有被改,全可查。
- 防篡改:逐文件 SM3 + digest 双重校验,中间人改一行配置都会被 files_ok / digest_ok 拦住。
- 防"任意有效签名":白名单信任锚让"验签通过"不等于"可信",从根上堵住 Sigstore 指出的漏洞。
- 国密合规:SM3 摘要 + SM2 签名,契合信创与国产化要求,离线可验。
限制与不足
以下是明确的边界,使用前先知道:
- 验签与应用分离:verify 只做诊断;"应用前拒绝"的白名单预检在 Spoke 侧,本版 Spoke 演示为拉取 / 上报。
- 密钥生命周期:演示签名者私钥仅存内存、重启重建;真实环境需自行管理密钥分发与轮换。
- SBOM / 漏洞扫描 / 策略门禁在"安全合规"主题:已实现的是自研内置模拟(非真实 Trivy/Syft,OPA 未实现),bundle 签名层不携带漏洞信息。
- 离线导入回传是骨架:三区双重校验 + 导入报告签名回传(G9)当前为机制骨架,完整流程属 P1 后续。
场景故事
故事 1
第一次构建签名 bundle:占位指纹自动升级
场景:制品构建
角色:应用开发
耗时:约 5 分钟
- 背景
- 阿强要把 demo-app 的制品打包成 bundle:web.bin + db.bin 两个文件。demo-signer 是 seed 里的占位签名者(占位指纹 demo-fingerprint-placeholder),第一次构建 bundle 时平台会自动把它升级为真实 SM2 密钥指纹并注册进白名单。
- 传统做法对比
- 以前制品就是一个压缩包传来传去,没人能证明"这个包到底是谁打的、有没有被改过",线上跑出问题只能回滚瞎猜;现在 bundle 自带文件清单(SM3)+ 不可变 digest + SM2 签名,来源与完整性一目了然。
- 角色
- 阿强(应用开发工程师,打包制品)+ 刘经理(安全合规,随后验收信任锚)。
- 操作步骤
-
- 进入"制品"页(/apollo/bundles),点击"构建 bundle"
- 填 app=demo-app、version=1.0.0、bundle_version=1
- 上传 web.bin、db.bin(表单字段名即 bundle 内路径)
- 提交,观察记录生成
- 系统响应
- 构建成功返回 bundle 记录:
POST /api/v1/bundles
{ "code": 0, "data": { "id": 1,
"name": "demo-app-1.0.0-b1",
"digest": "sm3:...", "signer_id": "demo-signer",
"signer_fingerprint": "..." } }
trusted_signers 里 demo-signer 的占位指纹被替换为真实 SM3(公钥) 指纹。
- 结果洞察
- bundle 不可变 digest 是唯一连接键(内容变则 digest 变);manifest 含逐文件 SM3 校验和与组件清单(组件名取文件基名)。签名者指纹自动注册白名单,保证后续验签信任锚自洽。
- 调整建议
- bundle_version 按发布计划填并保持单调递增;文件路径名会决定组件 kind(含 config / eval_set / ontology_yaml 的路径自动归类),跨产品分发时可复用。
- 动手试一试
- 登录:admin / admin1,端口 18082。页面路径:制品 → 构建 bundle。输入内容:app=demo-app、version=1.0.0、上传两个小文件。预期结果:返回 id 与 digest;到"签名者"列表看 demo-signer 指纹已从占位变为真实指纹。
- 限制提示
- 文件源不能为空(400 BUNDLE_INVALID);文件路径不允许重复 / 绝对路径 / 目录穿越;构建用服务器内存中的演示签名者私钥,重启后密钥重建,指纹变化需重新构建或更新白名单。
故事 2
验签:SM3 清单 + digest + SM2 全链路体检
场景:制品验签
角色:安全合规
耗时:约 3 分钟
- 背景
- bundle 构建完,刘经理要对它做一次完整"体检":逐文件 SM3 校验、digest 一致性、白名单信任、SM2 验签,确认这个包可以放心分发到各环境。
- 传统做法对比
- 以前校验制品只能"解压、跑起来看看",无法预防中间人篡改——改一行配置文件就神不知鬼不觉;现在一键验签,四项结果全绿才说明包从签名到现在没被动过。
- 角色
- 刘经理(安全合规工程师,主导验签与放行决策)。
- 操作步骤
-
- 在制品列表找到刚构建的 bundle,点击"验签"
- 等待校验链执行
- 查看 VerifyResult 四项结果
- 系统响应
- 验签返回结构示例:
POST /api/v1/bundles/1/verify
{ "code": 0, "data": { "files_ok": true,
"digest_ok": true, "signature_ok": true,
"trusted": true, "signer_name": "demo-signer" } }
- 结果洞察
- 校验链四步:①逐文件重算 SM3 对照清单(files_ok)②重算 digest 对照 manifest(digest_ok)③签名者指纹在白名单且 enabled(trusted)④用白名单公钥做 SM2 验签(signature_ok)。任何一步失败即拒绝应用。
- 调整建议
- 校验结果作为审计凭据留存;下载 zip(manifest.json + digest.sig + files/)后可离线读回再验,适合给内网 / 离线环境做二次确认。
- 动手试一试
- 操作:对刚构建的 bundle 点击"验签"。预期结果:files_ok / digest_ok / signature_ok / trusted 全部 true,signer_name=demo-signer。
- 限制提示
- verify 是诊断接口;真正部署时的"应用前拒绝"逻辑在 Spoke 侧白名单预检;trusted=false 时接口返回 403 VERIFY_SIGNER_NOT_TRUSTED(即使 SM2 验签通过也不放行)。
故事 3
签名者白名单信任锚:未知签名者的包被拒
场景:供应链演练
角色:安全合规 + 平台运维
耗时:约 6 分钟
- 背景
- 供应链安全演练:假设攻击者 / 外部供应商用"自己的密钥"签名了一个 bundle,SM2 验签技术上能通过,但它的指纹不在 trusted_signers 白名单里——平台必须拒绝。演示方法:把 demo-signer 禁用,再验签之前的 bundle,观察被拒;最后重新启用恢复。
- 传统做法对比
- 只验签不查身份的系统里,"任何持有有效密钥的人都能发布",等于把信任全交给密钥分发环节,这正是 Sigstore 指出的"任意有效签名"漏洞;白名单信任锚 = 除了验签,还要求签名者指纹在预置白名单(enabled=true),从根上杜绝。
- 角色
- 刘经理(安全合规,演练主导)+ 陈工(平台运维,操作白名单启用 / 禁用)。
- 操作步骤
-
- 先构建一个 bundle 并验签成功(demo-signer 在白名单)
- 到签名者页把 demo-signer 禁用(POST /signers/:id/disable)
- 再次对同一 bundle 验签
- 观察被拒,再启用(enable)恢复
- 系统响应
- 禁用后验签返回:
HTTP 403 VERIFY_SIGNER_NOT_TRUSTED
"签名者指纹 ... 不在可信白名单(enabled=true)中,拒绝应用"
VerifyResult: { "files_ok": true, "digest_ok": true,
"signature_ok": true, "trusted": false }
重新启用后验签恢复四项全绿。
- 结果洞察
- 信任锚语义:SM2 验签通过 ≠ 可信。白名单(signer_id + 公钥指纹)是信任的根,指纹不在白名单或已禁用,一律拒绝应用。这比"验签通过即放行"更贴近真实供应链安全,也是对 Sigstore 结论的落地。
- 调整建议
- 白名单模型已支持环境作用域(trusted_signers.environment_id,验签按环境级优先、回退全局);正式环境建议把签发流程与白名单变更纳入审批审计;同名 / 同指纹重复注册返回 409 SIGNER_CONFLICT。
- 动手试一试
- 操作:构建 bundle → 验签全绿 → 禁用 demo-signer → 再验签被 403 拒绝(trusted=false)→ 重新启用 → 验签恢复全绿。预期结果:完整走通"信任锚拒绝 / 恢复"闭环。
- 限制提示
- 白名单要求密钥分发生命周期管理(换钥要同步更新指纹);演示签名者私钥仅存内存,重启服务器会重新生成新密钥(指纹变化,需重新构建);当前仅 HTTP / 轮询,gRPC / mTLS 属 V2。
常见问题
bundle 和普通压缩包有什么区别?
普通压缩包只有内容;bundle 是"内容 + 清单 + 签名":manifest 记录逐文件 SM3 校验和与组件清单,digest 是内容哈希(唯一连接键),SM2 签名证明来源,白名单证明签名者可信任。任何一项对不上就拒绝。
为什么"验签通过"还不等于"可信"?
验签只证明"这个包确实是持密钥者签的",但持密钥者是不是可信的签发方是另一回事。白名单信任锚把信任收敛到"签名者指纹在 trusted_signers(enabled=true)",堵住"任意有效签名"漏洞。
demo-signer 的占位指纹是怎么回事?
seed 创建的 demo-signer 初始指纹是占位符(demo-fingerprint-placeholder)。首次构建 bundle 时,平台生成真实 SM2 密钥对,用 SM3(公钥) 计算指纹并替换占位值,保证"构建方"与"白名单"自洽。
换了密钥怎么办?
重新构建 bundle 会得到新指纹;若签名者记录已存在,需要更新 trusted_signers 的指纹与公钥(PUT /signers/:id),或删除重建。注意同名 / 同指纹冲突返回 409,白名单变更建议纳入审批。
验签失败可能是什么原因?
按四项对号入座:files_ok=false 文件被改(SM3 对不上);digest_ok=false 内容与 manifest 不一致;trusted=false 签名者不在白名单或已禁用(403);signature_ok=false SM2 验签失败(签名无效 / 公钥不匹配)。
主题小结
一句话:bundle 供应链安全 = 逐文件 SM3 清单 + 不可变 digest + SM2 签名 + 签名者白名单信任锚。核心认知:验签通过 ≠ 可信,白名单才是信任的根。边界:verify 是诊断接口、演示密钥仅存内存重启重建;SBOM / 漏洞扫描 / 策略门禁已实现(自研内置模拟)但属于
安全合规主题。