必须启用DOCKER_CONTENT_TRUST=1并配合私有Notary服务,否则Docker 27+默认策略下拉取未签名镜像会直接失败;单纯设环境变量仅触发客户端校验,无法解决私有仓库无Notary后端、自签名证书被拒、targets密钥未预分发三大问题。

必须启用 DOCKER_CONTENT_TRUST=1 并配合私有 Notary 服务,否则 Docker 27+ 默认策略下拉取未签名镜像会直接失败,不是“可选验证”,而是运行时拦截。
为什么 DOCKER_CONTENT_TRUST=1 在生产中不够用
单纯设置环境变量只触发客户端本地校验逻辑,但无法解决三个关键问题:私有仓库无 Notary 后端、自签名证书被拒绝、targets 密钥未预分发。Docker 客户端在拉取时若无法连接 DOCKER_CONTENT_TRUST_SERVER 或收到 TLS 错误,会静默降级为无签名拉取(尤其在旧版 daemon 或 CI 环境中),导致信任链实际断裂。
- 默认信任服务器
https://notary.docker.io仅支持 Docker Hub 官方镜像,私有 registry 必须部署自己的notary-server和notary-signer - 若私有 Notary 使用自签名证书,Docker daemon 不读取系统 CA 信任库,必须将证书显式放入
/etc/docker/certs.d/notary.example.com:4443/ca.crt - 首次
docker push会生成 targets 密钥并存于~/.docker/trust/private/,但该路径在 CI runner 或 Kubernetes Pod 中不可靠,需提前用notary key import注入已批准的密钥环
如何让 docker pull 真正拒绝未签名镜像
仅靠客户端配置无法保证强制性。生产环境必须在服务端和运行时双层拦截:
- 在私有 registry(如 Harbor)启用 Notary v2 插件,并配置策略:对
prod/命名空间下的所有镜像,pull请求必须携带有效的application/vnd.cncf.notary.signaturereferrer,否则返回 403 - Kubernetes 集群中部署
ImagePolicyWebhook(如 OPA/Gatekeeper),规则示例:deny[msg] { input.request.kind.kind == "Pod" image := input.request.object.spec.containers[_].image not regex.match("^(prod|core)/", image) not is_signed(image) msg := sprintf("unsigned image rejected: %s", [image]) } - 在
dockerd启动参数中添加--signature-verification=true(要求 dockerd ≥ 20.10),并确保/etc/docker/trust/下存在可信根证书和 targets 公钥
notary CLI 的常见误操作与修复
运维人员常以为 notary list 返回结果就代表“已验证”,其实它只查元数据是否存在,不执行完整 TUF 链校验。真实验证必须走 cosign verify 或 docker pull 实际触发。
- 错误:运行
notary -s https://notary.example.com list myreg/prod/app:v1显示条目,就认为签名有效 → 实际可能是过期的snapshot.json或被篡改的targets.json - 正确做法:在目标节点执行
docker pull --signature-verification=true myreg/prod/app:v1,观察是否报Error: signature verification failed或remote trust data does not exist - 调试技巧:加
-D参数看完整 HTTP 流程:docker -D pull myreg/prod/app:v1,确认是否真正请求了/v2/myreg/prod/app/_oci_artifacts/.../referrers
最易被忽略的是时间同步问题:Notary v2 的 timestamp.json 有效期默认仅 24 小时,若节点系统时间偏差超过 5 分钟,TUF 校验会直接失败且不提示具体原因,表现为“签名不存在”。生产集群必须统一 NTP 源并监控 ntpq -p 偏差值。


















