3、镜像安全基础:镜像来源验证、镜像签名与信任机制、Docker Content Trust (DCT) 实践
聊到容器安全,我第一个想说的就是镜像。你想想看,整个容器化应用的根基就是镜像。如果镜像本身就有问题,那后面做再多安全加固都是白搭。说白了,镜像安全就是容器安全的第一道防线。
3.1 镜像来源验证:别什么都往生产环境拉
我记得刚接触 Docker 那会儿,图省事,直接从 Docker Hub 拉了个第三方镜像就跑。结果呢?镜像里被人植入了挖矿脚本,CPU 直接飙到 100%。嗯,从那以后我再也不敢随便拉镜像了。
镜像来源验证,核心就两件事:
- 确认镜像从哪里来——是不是官方仓库?是不是受信组织?
- 确认镜像没有被篡改——拉下来的镜像和发布者上传的是不是同一个?
我个人习惯的做法是:
- 优先使用官方镜像(比如
nginx:alpine、python:3.11-slim) - 如果必须用第三方镜像,先看 Docker Hub 上的「官方镜像」标识或「Verified Publisher」标签
- 拉取后立即用
docker image inspect查看镜像的元数据,确认创建时间、层数是否合理
核心原则:永远不要在生产环境中使用 latest 标签。指定具体版本号,比如 nginx:1.25.3-alpine,这样你能清楚知道拉的是什么。
3.2 镜像签名与信任机制:给镜像上个「防伪码」
镜像签名,说白了就是给镜像加个数字签名。就像你买茅台要扫防伪码一样,镜像签名能让你确认这瓶「镜像」是真的,不是假酒。
为什么会需要这个?因为镜像在传输过程中可能被中间人攻击篡改。我在项目中遇到过,有人通过 ARP 欺骗劫持了镜像拉取请求,把恶意镜像塞给了我们的构建服务器。还好当时启用了签名验证,直接拉取失败,避免了灾难。
Docker 的信任机制基于 Notary 项目,它使用公钥基础设施(PKI)来管理签名。简单来说:
- 发布者用私钥对镜像签名
- 拉取者用公钥验证签名
- 签名信息存储在 Notary 服务器上
小提示:签名不是加密。镜像内容仍然是公开可见的,签名只是保证内容没有被篡改。如果你需要镜像内容保密,那得用镜像加密,那是另一个话题了。
3.3 Docker Content Trust (DCT) 实践:动手做一遍
DCT 是 Docker 内置的内容信任机制。开启后,Docker 客户端只拉取经过签名的镜像。没签名的?直接拒绝。
好,咱们直接上手操作。我假设你已经有一个 Docker 环境了。
3.3.1 开启 DCT
# 设置环境变量,开启 DCT
export DOCKER_CONTENT_TRUST=1
# 验证是否开启
docker trust inspect --pretty
开启后,你拉取镜像时会多一个步骤——Docker 会去 Notary 服务器检查签名。如果镜像没有签名,你会看到类似这样的错误:
Error: remote trust data does not exist for docker.io/library/nginx:latest
嗯,这里要注意:DCT 默认只对 Docker Hub 生效。如果你用私有仓库,需要额外配置 Notary 服务器地址。
3.3.2 生成签名密钥
第一次使用 DCT 时,需要生成密钥对:
# 生成根密钥和仓库密钥
docker trust key generate my-key
# 查看生成的密钥
ls -la ~/.docker/trust/
你会看到两个关键文件:
private/root_keys/——根密钥,极其重要,丢了就废了private/tuf_keys/——仓库密钥,用于签名具体镜像
警告:根密钥一定要备份!我曾经见过有人把根密钥存在 CI 服务器的临时目录里,结果服务器重启后密钥丢了,整个仓库的镜像都无法再签名。最后只能重建仓库,所有镜像重新打标签、重新签名,折腾了一整天。
3.3.3 签名并推送镜像
假设你有一个本地镜像 my-app:1.0,想签名后推送到 Docker Hub:
# 确保 DCT 已开启
export DOCKER_CONTENT_TRUST=1
# 给镜像打标签
docker tag my-app:1.0 your-dockerhub-username/my-app:1.0
# 推送镜像(会自动签名)
docker push your-dockerhub-username/my-app:1.0
推送过程中,Docker 会提示你输入密钥密码。第一次推送还会要求你设置 Notary 的 root key 密码。记住这个密码,后面验证要用。
3.3.4 验证签名镜像
在另一台机器上,开启 DCT 后拉取镜像:
export DOCKER_CONTENT_TRUST=1
docker pull your-dockerhub-username/my-app:1.0
如果签名有效,拉取成功。如果镜像被篡改或没有签名,拉取失败。
你也可以用 docker trust inspect 查看签名详情:
docker trust inspect --pretty your-dockerhub-username/my-app:1.0
输出会显示签名者、签名时间、使用的密钥等信息。
3.4 实际项目中的避坑指南
我在几个生产项目中落地 DCT 时,踩过不少坑。分享几个给你:
- CI/CD 集成问题:DCT 需要交互式输入密码,但 CI 环境是非交互的。解决方案是用
expect脚本或 Docker 的--password-stdin参数,把密码通过环境变量传入。 - 密钥管理:别把私钥硬编码在代码里。用密钥管理服务(比如 HashiCorp Vault、AWS KMS)来存储和分发密钥。
- 团队协作:每个开发者应该有独立的签名密钥,而不是共用一把。这样如果某个密钥泄露,只需要吊销那个人的密钥,不影响其他人。
- 回滚问题:如果镜像签名后发现有 bug,需要重新构建并签名新版本。旧版本的签名无法修改,只能发布新版本。
一句话总结:镜像签名不是可选项,是生产环境的必需品。DCT 配置起来不复杂,但能帮你挡住 90% 的镜像篡改攻击。别等到出事才想起来加签名,那时候就晚了。
好了,镜像安全基础就聊到这儿。下一章咱们会深入镜像扫描和漏洞管理,到时候再细聊怎么用 Trivy、Clair 这些工具做自动化扫描。记住,安全是持续的过程,不是一次性的配置。