登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  业界新闻

OCI 镜像签名进入构建链后如何划分验证责任

来源:17golang原创

时间:2026-09-15 05:08:43 202浏览 收藏

把 OCI 镜像签名塞进构建流水线,最容易犯的错是把 cosign sign 当成“安全已经完成”。更稳的划分是:构建系统负责产出可追溯的镜像,受保护的发布步骤负责签名,仓库负责保存按 digest 关联的证据,部署准入负责按策略放行,运行时再守住最后一道边界。签名证明“这个内容由某个可信身份签过”,并不自动证明内容没有漏洞、构建过程没有越权,也不代表任何环境都能兼容。

要点速览
  • 签名对象应是 OCI manifest digest;tag 只能作为查找入口,不能作为稳定身份。
  • 签名、attestation、漏洞扫描和部署策略是不同证据,分别由不同环节消费。
  • 先以观察或告警模式接入,再逐步收紧准入;旧仓库、多架构和离线环境要单独设计。

官方资料:https://docs.sigstore.dev/cosign/

先固定签名对象,再谈谁来签

OCI Image Specification 把镜像组织成 manifest、config、layers 和 descriptor,并用 digest 做内容寻址。一个发布链如果只在 tag 上签名,tag 被重新指向后,读者很难判断签名对应的是哪份内容。工程上应让构建器先推送镜像、记录最终 manifest digest,再让受保护的发布身份对这个 digest 签名。

这里有三个不同问题:构建器是否使用了允许的源代码和依赖,签名身份是否确实代表发布主体,最终拉取的 digest 是否就是被签过的对象。前两个问题可以由构建记录和签名证书回答,最后一个问题要由消费方在验证时重新比对 digest。

OCI 镜像构建链中源代码、构建器、manifest digest、Cosign 签名和 attestation 的责任关系示意图
图1:OCI 构建链的签名责任边界示意;签名绑定 manifest digest,构建器与发布身份各自承担不同责任。

一个简化的发布片段可以这样表达责任边界:

# 先固定最终 digest,再由受保护的发布身份签名
IMAGE="registry.example.com/team/service@sha256:..."
cosign sign --yes "$IMAGE"

# attestation 描述构建事实,不能替代镜像签名
cosign attest --yes --predicate sbom.spdx.json --type spdxjson "$IMAGE"

上面的命令是文章中的配置示意,不表示本机已经执行。真正的门禁还要限制谁能触发发布、哪些流水线能拿到短期身份,以及签名失败时是否阻止晋级。

验证责任要分成仓库、准入和运行时三层

签名写入 registry 后,仓库只是在保存镜像和关联证据,它不应替业务决定“谁可以在生产运行”。晋级门禁适合检查签名身份、构建来源、SBOM 或漏洞阈值,并把合格的 digest 推进到下一环境;Kubernetes admission 则根据命名空间和镜像匹配规则决定请求能否创建。

Sigstore Policy Controller 的思路很适合说明这种分工:策略先匹配镜像,再要求至少一个 authority 验证通过;多个匹配策略通常要同时满足。换句话说,签名验证回答“证据是否有效”,策略回答“这份证据是否满足本环境的要求”。不要把这两个问题揉成一个布尔开关。

更靠近节点的 runtime hook 可以弥补 admission 的空档,例如静态 Pod、直接的 kubelet 路径、Webhook 网络故障或已经预拉取的镜像。它仍然不能替代集群策略,也不能拯救已经被完全攻破的节点。职责越靠后,覆盖面越强,但对运行时可用性和故障恢复的要求也越高。

OCI registry、签名证据、晋级门禁、Policy Controller、runtime hook 与部署工作负载的分层验证关系示意图
图2:OCI 镜像发布后的分层验证示意;仓库保存证据,准入策略做决策,运行时负责最后一道执行边界。
环节主要回答不应独自承担
构建器输入、依赖和产物如何关联生产环境最终放行
签名发布步骤哪个受信身份认可了哪个 digest证明镜像没有漏洞
Registry镜像与签名/attestation 是否可取按业务环境做授权决策
准入策略当前命名空间是否接受该证据替代构建来源记录
Runtime hook容器真正启动前是否再次满足策略修复被攻破的节点

兼容性决定了门禁收紧的速度

新链路通常不是“签名开启或关闭”二选一。先盘点 registry 是否支持 OCI referrer、旧客户端是否能拉取带关联对象的镜像、多架构镜像究竟签 index 还是签选中的 platform manifest,再决定验证点。对离线集群,还要明确证书链、透明日志或 bundle 能否在本地验证,不能把公网可达当成默认前提。

推荐采用三段式迁移:第一阶段记录缺失签名、身份不匹配和 digest 漂移,但不阻断;第二阶段只对新环境或高风险命名空间拒绝;第三阶段才把生产基线设为强制,并保留明确的回滚镜像和策略版本。这样失败时能判断是证据不存在、策略不匹配,还是仓库/网络不可用。

# 策略示意:注释说明决策,不是可直接套用的集群清单
no-match-policy: warn
# 灰度完成后再切换为 deny,并先准备可验证的回滚 digest+promotion-image: registry.example.com/team/service@sha256:...

落地前用五个问题检查责任是否清楚

  • 签名绑定的是最终 manifest digest,还是仍在依赖可变 tag?
  • 构建身份、签名身份和部署审批身份是否分离,泄露一个凭据会不会越过全部环节?
  • 签名与 attestation 的保存位置、保留周期和离线验证方式是否明确?
  • Policy Controller、其他 admission webhook 和 runtime hook 失败时,谁负责告警、重试和回滚?
  • 多架构、旧 registry、预拉取镜像和静态 Pod 是否有单独的验证路径?

常见问题

镜像签名能代替漏洞扫描吗?

不能。签名确认内容与身份的关联,扫描关注已知风险;两者应作为不同门禁条件组合。

为什么不能只在 CI 构建完成时验证一次?

因为镜像可能在仓库、晋级或运行前被换成另一个 digest。消费端至少要在实际使用的 digest 上重新验证。

应该签 tag 还是签 digest?

以 digest 作为签名对象,tag 只用于人类查找和发布编排;部署清单也应尽量固定 digest。

Policy Controller 和 runtime hook 必须同时部署吗?

不一定。前者适合集群准入策略,后者更靠近容器启动边界;是否叠加取决于旁路风险、运行时支持和故障恢复能力。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>