Docker Content Trust 已经不再适合被当成“有点老、但先放着也无妨”的底层管线。Docker 开始把 Docker Official Images 从 DCT 路径上撤下来,Azure 也给自己的 DCT 支持写明了退出时间表之后,镜像签名这件事就从后台卫生项变成了有明确失败面的迁移工程。[1][2][3]
这篇给平台团队和安全团队,适用场景是:你们的环境里已经有一部分镜像签名习惯、旧验证逻辑或发布流程,现在需要迁到新的 OCI 签名路径,同时又不想把切换过程做成 CI 中断,或者做成 admission policy(准入策略)上的混乱现场。
图像说明:封面图是专门为这篇文章制作的分析示意图。它要帮助读者把真正的问题看清:先把 DCT 视作 tag 时代的旧信任习惯,再决定生产里的默认验证权威究竟落在工作负载身份,还是落在证书与策略。
这次迁移真正要完成的是契约切换
迁移里最容易踩的坑,是想找一个“新的 DCT”,然后把旧操作习惯原封不动带过去。
这样做通常会出现三件事:团队继续把镜像信任理解成 tag 层面的属性,继续把长期私钥塞在并不适合的地方,再把验证策略改造推迟到“新工具先装上再说”的阶段。债务看起来被迁移了,实际只是换了外壳。
Docker 自己的 DCT 文档已经把这条老路径的问题写得很清楚。DCT 的信任绑定在镜像引用里的 tag,不在不可变的 digest 上,而且同一个 tag 在不同时间点还或许出现“有签名内容”和“无签名内容”并存的理解错位。[1] 它还依赖 Notary server、delegation key、timestamp key,以及一个 Docker 明确警告过“丢失后无法恢复”的离线 root key(根密钥)。[1]
在 DCT 还是公共注册表默认信任通道的年代,这种设计还能运转;放到 2026 年,它已经和今天的实际工程环境有明显错位。现在的主流做法更偏向 digest 级验证、OCI 原生签名存储、透明日志(transparency log)、云 KMS 插件,以及基于工作负载身份(workload identity)的 CI 签名。
为什么 DCT 现在更像验证债务,而不再像验证能力
有三条事实足够说明问题。
第一,Docker 已经进入退场阶段。Docker 公开写明,上游 Notary v1 代码库已经不再积极维护,Docker Hub 上使用 DCT 的拉取占比已经低于 0.05%,而 Docker Official Images 从 2025 年 8 月 8 日 开始还会逐步碰到证书过期问题。[2] 如果你的拉取路径还依赖 DOCKER_CONTENT_TRUST=1,Docker 给出的现实建议已经接近“把它拿掉”。[2]
第二,Azure Container Registry 给出了完整时间表。微软说明,DCT 的弃用从 2025 年 3 月 31 日 开始,ACR 会在 2028 年 3 月 31 日 彻底移除 DCT。[3] 这已经是一个开始倒计时的迁移窗口。
第三,DCT 的运维形状和现代自动化并不匹配。Docker 文档里的 DCT 工作流仍然围绕本地 trust store(信任存储)、delegation key 导入,以及通过 Notary 初始化仓库来组织。[1] 这一套和今天的短时凭证签名、工作负载身份签名、云证书管理,已经是两种不同工程范式。
所以迁移目标不该理解成“把 docker trust sign 换成另一个命令”,真正要完成的是:把团队从 tag 时代的信任思路,迁到 digest 时代的验证契约。
分叉点在哪里:选身份优先的 Sigstore,还是证书优先的 Notation
大多数团队未必要给所有镜像找一条完全一致的通路,但每个环境仍然该有一条主路径。否则审核成本会不断上升。
可以先用一条很快的分流判断,把选择问题压实:
- 如果你们的发布权威本来就主要来自带 OIDC 的 CI 身份联邦,先从 Sigstore 这条路开始看;
- 如果发布权威本来就主要来自企业 PKI、trust store 与证书审查流程,先从 Notation 这条路开始看;
- 如果团队现在仍主要按“哪个工具更像
docker trust sign”来比较,就说明你们优化的还是旧操作手感,而并非新的验证契约。
什么时候该把 Sigstore 设成主路径
Sigstore 的默认路径是 keyless signing(无长期密钥签名)。它的文档解释得很直接:Fulcio 会给临时生成的签名密钥签发 短时证书,把这个密钥与 OpenID Connect(开放身份连接,OIDC)身份绑定起来,再由 Rekor 把签名事件写入透明日志。[4] 这样一来,团队关注的重心就从“长期私钥放在哪、谁有机会摸到它”,转成“是哪一个工作负载身份在什么 issuer(签发方)下面签了这个制品、事件有没有被记录”。
这条路更适合下面几种环境:
- CI 本身已经有比较稳定的 OIDC 身份,例如 GitHub、GitLab、Google 或 Microsoft;
- 发布系统不想长期保管私钥;
- 验证时希望直接按身份和 issuer 约束,例如
--certificate-identity与--certificate-oidc-issuer; - 团队可以把透明日志记录看成验证故事的一部分,而并非额外负担。[4][5]
Sigstore 还有一个实际优势:镜像之外的 blob、attestation、SBOM 等工件,也能放进同一套“身份 + 透明日志”的理解框架里。
什么时候该把 Notation 设成主路径
Notation 所处的位置不太一样。Notation 项目把自己定义成面向 OCI 注册表生态的标准签名工具,而微软在 ACR 的迁移文档里,也把 Notary Project 视作替代 DCT 的便携路径,用于注册表、CI 系统和 AKS 准入控制里的签名与验证。[3][6][7]
这条路更适合下面几种组织条件:
- 你们已经有企业 PKI、HSM 或云 KMS 驱动的证书工作流;
- 安全团队更习惯 trust store(信任存储)与证书策略,而并非 OIDC 身份策略;
- 你们希望更自然地接入 Key Vault、AWS Signer、Ratify 这类企业侧控制点;
- 团队的审计与治理语言本身就偏向 X.509 证书和信任链,而并非身份声明。[6][7]
微软的教程还强调了两个很关键的操作点:签名时应尽量使用 digest,不要把可变 tag 当成信任锚点;timestamping(时间戳)与 trust policy(信任策略)应该在设计一开始就进入验证路径,而并非后面再补。[7]
迁移过程中尽量不要做的两件事
1)不要一边保留 DCT 拉取路径,一边把验证改造无限后延
既然 Docker Official Image 在 DCT 路径上已经会受到证书过期影响,那么把 DOCKER_CONTENT_TRUST=1 继续散落在旧 CI 作业里,只是在延长一块已知脆弱面。[2] 更合理的动作,是先把 DCT 从关键拉取路径上拿掉,再有意识地把验证能力重建到新路径上。
2)不要把基于 tag 的信任习惯原样搬到新系统
DCT 绑定 tag,确实让人容易用人类可读标签来理解“哪个版本可信”。现代签名系统更适合把 tag 留给发布协作,把 digest 留给信任判断。微软的 Notation 教程明确建议用 digest 识别被签名镜像,因为 tag 可变、也或许被覆盖。[7] Cosign 的验证流程同样默认会检查签名 payload 里绑定的 digest。[5]
如果你的切换方案里还写着“验证 stable 这个 tag 就可以”,那这次迁移还没有真正完成。
一条可强制执行的四阶段切换路径
阶段一:把旧信任面全部盘出来
先找出 DCT 还活在哪些地方:
- CI 或 shell profile 里的
DOCKER_CONTENT_TRUST; - 发布脚本里的
docker trust命令; - 仍在依赖的 Notary server;
- runner 或 secrets store 里的 delegation key;
- 默认按 tag 理解信任的校验逻辑。
这一步更像依赖图盘点,不只是 grep 命令。风险最大的 DCT 依赖,往往埋在没人碰的旧模板里。
阶段二:先决定新的验证问题到底怎么问
每个环境最好先回答清楚,系统以后默认验证的是什么:
- Sigstore 路径:这个制品是并非由预期工作负载身份、在预期 issuer 下签出的,同时具备可验证的透明日志证据?[4][5]
- Notation 路径:这个制品是并非由我们允许的证书与 trust policy 所认可的主体签出,并且能在注册表与部署控制里被一致验证?[3][6][7]
整个组织可以同时支持两条路,但单个环境若没有默认答案,评审压力会很快失控。
阶段三:先双签,再双向核验,最后再上强制策略
切换窗口里,可以让新发布候选在新路径上完成签名,同时保留必要的旧行为。这里追求的长期状态并非双轨运行,更实际的目标,是先把验证证据和失败样式收集完整,再把 admission 规则切成阻断模式。
通过标准应当尽量工程化:
- unattended CI(无人值守 CI)可以稳定签名;
- verify 命令统一面向 digest 引用;
- 负责策略的人能解释清楚每个签名为什么通过;
- 紧急回滚不需要重新启用已经准备退场的 DCT 密钥材料。
阶段四:切 enforcement,再清理 DCT 资产
当 CI 与预发环境里的验证都已经稳定通过,就可以从 pull/build 环境移除 DCT,在平台允许的地方关闭注册表侧 DCT,再把不用的 delegation key 等材料退役。[2][3]
理想终态应该很朴素:
- 生产拉取路径不再依赖
DOCKER_CONTENT_TRUST; - 发布决策不再依赖可变 tag 本身;
- 签名身份或签名证书策略,由真正拥有部署信任策略的团队负责。
一个足以推翻“快速迁移”叙事的证伪条件
如果你的环境高度 air-gapped(隔离网络),或者注册表与准入层现在还不具备所需的签名 referrer 支持,或者安全流程暂时还没有办法批准 OIDC 身份策略与证书 trust store 策略中的任意一种,那么“快速切换”这件事在当前条件下就站不住。
这时需要先把验证权威模型定下来,再选 CLI。把架构缺口伪装成工具安装任务,后面通常会反复返工。
接下来一个季度值得盯住的四件事
- 残余 DCT 依赖,尤其是只会在热修复或回滚流程里冒出来的那种。[1][2]
- digest-first 验证纪律 是否已经进入 CI、注册表和准入控制。[5][7]
- trust policy 所有权 是否真正清楚:Sigstore 侧看 issuer 与身份,Notation 侧看证书与 trust store。[4][6][7]
- 云平台退出时间表,例如 ACR 的 2028 年 DCT 移除节点。[3]
结语
一场做得好的 DCT 迁移,关注点不该是哪个新签名 CLI 用起来更像旧工具,真正关键的是:生产里到底由什么契约来决定“这份镜像可信”。
如果你的组织本来就以身份为中心,Sigstore 往往更贴合现有流程;如果组织治理本来就以证书和策略为中心,Notation 通常更容易落地。两条路最后指向的是同一个升级动作:让信任判断回到 digest 级验证,并把签名身份或证书策略写成可审计、可解释的显式规则。
来源
- Docker Docs, Content trust in Docker(DCT 的 tag 语义、密钥角色、根密钥丢失不可恢复警告、
DOCKER_CONTENT_TRUST行为) - Docker Blog, Retiring Docker Content Trust(Notary v1 维护状态、Docker Hub 使用占比低于 0.05%、DOI 证书过期时间线、迁移方向)
- Microsoft Learn, Transition from Docker Content Trust to Notary Project - Azure Container Registry(ACR 弃用时间表、关闭 DCT 的方法、Notary Project 迁移入口)
- Sigstore Docs, Overview(无长期密钥签名、Fulcio 短时证书、Rekor 透明日志、OIDC 身份流程)
- Sigstore Docs, Verifying Signatures(身份约束验证参数、digest claim 校验、bundle 与日志验证行为)
- Notation README(OCI 标准签名、Notation 作为 Notary Project 规格实现、Azure Key Vault 与 AWS Signer 集成示例)
- Microsoft Learn, Sign Container Images with Notation and Azure Key Vault by Using a Self-Signed Certificate(优先按 digest 签名、Notation CLI 与插件流程、验证路径)