推荐做法是将漏洞扫描作为构建后、推送前的独立ci验证步骤,dockerfile仅负责安全构建:固定基础镜像版本、多阶段构建、非root运行、清理缓存等;ci中用trivy扫描并阻断高危漏洞,结合报告驱动dockerfile修复闭环。

直接在 Dockerfile 构建流程中嵌入漏洞扫描,不是推荐做法。Dockerfile 本身是声明式构建描述,不适合执行扫描逻辑或中断构建。真正可行且生产级的做法,是把扫描作为构建后、推送前的独立验证步骤,与 Dockerfile 协同配合——Dockerfile 负责“怎么安全地构建”,CI 流水线负责“构建完是否真安全”。
Dockerfile 做好基础加固,为扫描铺路
扫描能否有效、能否快速修复,高度依赖 Dockerfile 的编写质量。它不执行扫描,但决定了扫描结果是否可控、可修复。
- 固定基础镜像版本,例如
FROM python:3.10-slim-bookworm,避免使用:latest或模糊标签,确保每次构建可复现、漏洞可追溯 - 多阶段构建分离构建环境与运行环境,减少最终镜像中的冗余工具和包(如编译器、dev headers),天然缩小攻击面
- 显式安装运行时依赖,禁用隐式全局安装;Node.js 项目用
npm ci --omit=dev,Python 用pip install --no-deps -r requirements.txt - 非 root 用户运行,添加
USER 1001并确保工作目录权限适配,降低漏洞利用后的提权风险 - 删除缓存和临时文件,例如
RUN apt-get clean && rm -rf /var/lib/apt/lists/*,避免残留已知漏洞的旧包元数据
CI 阶段调用 Trivy 扫描构建产物
构建完成生成镜像后,立即用 Trivy 对本地镜像做静态扫描。这是拦截高危漏洞的第一道闸门。
- 在 CI 脚本(如 GitHub Actions 的
.yml或 Jenkins Pipeline)中,docker build后追加一行:trivy image --severity CRITICAL,HIGH --exit-code 1 --skip-update your-app:latest -
--exit-code 1表示发现指定级别漏洞即返回非零退出码,CI 自动失败并阻断后续推送或部署 -
--skip-update加速扫描(适合高频构建),搭配定期后台更新 Trivy DB(如每小时 cron 更新) - 可选导出报告:
trivy image --format json -o trivy-report.json your-app:latest,供后续归档或平台解析
结合扫描结果驱动修复闭环
扫描不是终点,而是修复起点。关键在于让漏洞信息能反向指导 Dockerfile 修改。
- 查看 Trivy 输出,定位 CVE 对应的包名与当前版本(例如
openssl 3.0.11-1~deb12u3)及建议修复版本 - 若漏洞来自基础镜像,直接升级基础镜像标签(如从
python:3.10-slim-bookworm升至python:3.11-slim-bookworm) - 若漏洞来自应用依赖,更新
requirements.txt或package.json中对应包版本,并重新构建验证 - 避免无差别
apt upgrade或pip install --upgrade,防止引入兼容性问题;优先选择最小版本升级路径
进阶:用 Dockerfile 构建扫描器自身,实现私有化集成
当需要离线环境或定制策略时,可将扫描器(如 Clair 或 Trivy)打包成镜像,再由 CI 调用——这仍是“构建后扫描”,但扫描器本身由 Dockerfile 定义。
- 参考
gh_mirrors/do/dockerfiles/clair/Dockerfile:采用多阶段构建,先编译源码,再制作精简运行镜像 - 构建完成后,在 CI 中运行:
docker run --rm -v $(pwd):/workspace gh-mirrors/clair:latest --report-dir /workspace/reports your-app:latest - 这种方式便于统一扫描器版本、策略配置和 CVE 数据库,适合大型团队或合规要求高的场景











