必须部署独立notary server/signer并启用docker_content_trust=1,否则私有镜像拉取将因缺少tuf元数据或自签名证书不被信任而失败;签名需嵌入ci流程,验证须在客户端和kubernetes准入层双重强制。

在 Docker 中使用 Notary 实现镜像签名与强制验证,核心是通过 TUF(The Update Framework)协议保障镜像从构建到拉取全过程的完整性与来源可信性。虽然 Docker 官方已将 Notary 项目移交至 CNCF,并推荐用 Notary v2(即 notaryproject.dev)替代旧版,但当前主流 Docker CLI 和 Registry(如 Docker Hub、Harbor)仍主要支持 Notary v1(即 docker/notary)。以下以生产可用的 Notary v1 为基础,说明如何落地签名与强制验证。
部署并配置 Notary 服务端(Notary Server)
Notary 依赖一个独立的签名服务(notary-server)和后端存储(如 MySQL + S3/MinIO)。它不内置于 Docker Engine,必须单独部署:
- 使用官方
docker/notary镜像启动 server 和 signer 容器,确保两者共享密钥存储(如挂载同一 volume 或使用统一数据库) - 配置
config.json指向你的 Docker Registry(例如https://my-registry.example.com),并启用 TLS(Notary 强制要求 HTTPS) - 为每个命名空间(如
myorg/nginx)生成根密钥和 snapshot 密钥,首次推送需手动初始化:notary -s https://notary.example.com init myorg/nginx
对镜像打标签并签名(Sign image tags)
签名操作针对的是镜像 tag,而非镜像层本身。流程如下:
使用 Python 脚本下载 Docker 镜像并打包为 tar 文件。用户说“下载镜像”“拉取镜像”“pull xxx”“帮我下载 nginx 镜像”等时触发。支持 SOCKS5 代理直连(推荐)和国内镜像站加速两种模式。
- 先将镜像推送到已接入 Notary 的 Registry:
docker push my-registry.example.com/myorg/nginx:1.24 - 使用 notary CLI 关联该 tag 并签名:
notary -s https://notary.example.com -d ~/.notary add myorg/nginx 1.24 - 签名后,Notary 会生成 TUF 元数据(root.json、targets.json 等)并存入其后端;这些元数据描述了哪些 tag 是可信的、由谁签署、是否过期
- 可添加多个签名者(通过不同 GPG key),实现多签策略,例如“开发+安全+运维三方共同批准才允许上线”
客户端强制验证签名(Enforce verification on pull)
Docker CLI 本身不自动校验签名,需显式启用或配合脚本/CI 工具。关键方式有:
- 设置环境变量启用全局验证:
export DOCKER_CONTENT_TRUST=1,此后所有docker pull和docker build(含 FROM)都将拒绝未签名镜像 - 临时启用单次验证:
DOCKER_CONTENT_TRUST=1 docker pull my-registry.example.com/myorg/nginx:1.24 - 验证失败时提示类似
no signature found或signature expired,拉取中断,容器无法启动 - 注意:Docker Hub 的官方镜像(如
library/ubuntu)默认已签名,开启 DCT 后可直接拉取;私有 registry 必须自行部署 Notary 并完成签名,否则所有 pull 均失败
集成进 CI/CD 与运维实践要点
签名不应是发布后补救动作,而应嵌入交付流水线:
- 在 CI 构建成功、镜像推送到 registry 后,立即调用
notary add签名;失败则整个流水线失败 - 定期轮换 signing key(尤其是 compromise 场景),使用
notary key rotate更新 targets 密钥,旧镜像仍可验证(TUF 支持密钥继承) - 监控 Notary 元数据更新状态,例如用 Prometheus 抓取 notary-server metrics,告警 targets.json 长期未更新
- 避免将 root key 存于 CI 环境;建议用离线 HSM 或 Vault 托管 root 密钥,仅在初始化或重大变更时人工介入










