Trivy 内嵌 Dockerfile:在镜像构建阶段完成容器漏洞扫描
2026/9/9 20:45:53 网站建设 项目流程

Trivy 内嵌 Dockerfile:在镜像构建阶段完成容器漏洞扫描

【免费下载链接】trivyFind vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more项目地址: https://gitcode.com/GitHub_Trending/tr/trivy

把 Trivy 扫描器直接嵌入 Dockerfile,是让"镜像构建即安全校验"成为现实的轻量实践:在RUN阶段针对当前构建出的文件系统运行trivy rootfs,一旦发现漏洞即可借助--exit-codedocker build直接失败,从源头阻止带病镜像进入仓库。本文基于 trivy 仓库的 embed-in-dockerfile.md 展开,结合仓库内安装脚本、官方镜像与 CLI 定义,给出可直接复制到 CI/CD 的完整方案。

适用场景:把安全扫描前置到镜像构建

容器安全最理想的拦截点,是漏洞进入镜像的"前一秒"。与其等镜像构建完成、推送到仓库后再通过trivy image事后扫描,不如在 Dockerfile 中直接嵌入扫描步骤,让构建过程同时充当质量门禁(gate)。原文档指出,这一思路尤其适合那些目前正在使用 Aqua Microscanner 的 Dockerfile——只需用同样内嵌在构建流程中的 Trivy 扫描命令替换即可完成迁移。

这种做法本质上利用了 Trivy 的 rootfs(未打包文件系统)扫描能力:docker build的每个RUN都发生在一个基于当前镜像层构建出的临时容器中,此时对/执行trivy rootfs,就等价于对"即将成为镜像内容"的文件系统做一次完整扫描。仓库文档中关于 rootfs 的定位也与此一致——在 unpacked-filesystem.md 中,它被用来直接分析docker export导出的文件系统;而在 Dockerfile 内扫描/,正是同一能力的构建期形态。

方案一:构建过程中安装 Trivy 并立即扫描

原文档给出了最直接的写法——在目标镜像的构建层内下载 Trivy 二进制,然后立刻对根文件系统发起扫描:

$ cat Dockerfile FROM alpine:3.7 RUN apk add curl \ && curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin \ && trivy rootfs --exit-code 1 --no-progress / $ docker build -t vulnerable-image .

逐段拆解这段命令:

  • apk add curl:该基础镜像需要 curl 才能执行下载,安装后被同一层RUN内后续命令复用,且不会残留在最终镜像的后续层中增加体积负担。
  • curl -sfL ... | sh -s -- -b /usr/local/bin:把仓库自带的 contrib/install.sh 管道给sh执行,-s -- -b之后的参数透传给脚本,-b /usr/local/bin表示把trivy可执行文件安装到该目录。该脚本由 godownloader 生成,会先探测 OS/ARCH(支持 Linux、macOS、FreeBSD、Windows 及 amd64、arm64、s390x 等架构),再从https://get.trivy.dev/trivy下载对应压缩包,并同时拉取trivy_<version>_checksums.txt做 SHA-256 校验后解压安装,因此不是"裸下载解压"。
  • trivy rootfs --exit-code 1 --no-progress /:这是整个方案的核心扫描命令,参数含义如下:
参数作用
rootfs扫描目标类型为"未打包的文件系统",位置参数/即扫描当前容器根目录下的全部已安装内容
--exit-code 1只要发现任何安全问题,就返回退出码 1。Docker 的RUN步骤会捕获非零退出码并让docker build失败,从而阻断镜像产出
--no-progress关闭进度条输出。在构建日志与 CI 中输出干净的扫描结果,避免进度条反复刷新干扰日志收集

关于--exit-code的语义,可在 pkg/flag/report_flags.go 中看到其定义:"specify exit code when any security issues are found"(发现安全问题时指定的退出码);--no-progress的开关定义则位于 pkg/flag/db_flags.go。两者均为布尔/整型开关,组合后即可实现"发现问题即终止构建"的硬门禁。需要留意的是,rootfs 扫描首次运行需要下载漏洞数据库(trivy-db)到缓存目录(默认$HOME/.cache/trivy,可由全局参数--cache-dir调整),在构建网络中应确保可以访问 Trivy 默认的数据库镜像源(mirror.gcr.io/aquasec/trivy-db:2)。

方案二:多阶段构建,避免 curl | sh 也保持镜像纯净

直接把 curl 下载放进目标镜像层虽然简单,却存在两个明显缺点:

  1. 下载源不可控性curl ... | sh属于典型的"从网络管道执行脚本",脚本内容若被中间人替换,影响的是构建期间执行的内容(供应链风险);
  2. 镜像被"污染":Trivy 二进制被装进了目标镜像的运行层,增加了最终交付镜像的体积,除非使用多阶段RUN分层技巧刻意清理。

原文档因此给出了更稳妥的多阶段构建(multistage build)方案:

[...] # Run vulnerability scan on build image FROM build AS vulnscan COPY --from=aquasec/trivy:latest /usr/local/bin/trivy /usr/local/bin/trivy RUN trivy rootfs --exit-code 1 --no-progress / [...]

这里COPY --from=aquasec/trivy:latest ...直接从一个临时阶段vulnscan(基于build阶段)把官方 Trivy 镜像中的二进制拷贝进来,完全绕过了curl | sh的管道执行风险;同时因为扫描只发生在独立的vulnscan阶段,最终产物镜像不包含 Trivy 可执行文件——扫描工具"用完即走",最终镜像内容不被改变。

这个写法与仓库中的官方镜像定义是呼应的:根目录 Dockerfile 显示官方 Trivy 镜像基于alpine:3.24.1,安装ca-certificatesgit,并把trivy放置在/usr/local/bin/trivy,同时以ENTRYPOINT ["trivy"]作为默认入口。也就是说,COPY --from=aquasec/trivy:latest /usr/local/bin/trivy /usr/local/bin/trivy拷贝的是一个与官方使用方式一致、静态可执行的单一文件,拷贝后即可在任意 Linux 基础镜像内直接运行。生产实践中建议把latest固定为具体的发布版本标签(例如镜像 tag 指向某个正式 Release),以保证同一 Dockerfile 在任意时间点构建出的行为可复现。

rootfs 扫描的关键参数与构建门禁调优

trivy rootfs的完整参数清单可在 trivy_rootfs.md 中查阅。除上述--exit-code--no-progress外,在构建门禁场景中以下参数尤其值得组合使用:

  • --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL(默认全部):与--exit-code搭配,可只让中高危漏洞阻断构建。例如--exit-code 1 --severity HIGH,CRITICAL表示"仅当存在高危及以上漏洞时才失败",低危漏洞只出现在报告中,避免门禁过严导致构建频繁中断。
  • --ignore-unfixed:只报告存在修复版本的漏洞。若构建门禁的目标是"可修复漏洞必须清零",此参数可屏蔽暂无上游修复的存量问题。
  • --scanners vuln,secret(rootfs 默认值即vuln,secret:rootfs 模式默认同时检测漏洞与硬编码密钥(secret);也可扩展为vuln,misconfig,secret,license,把 Dockerfile 配置检查、许可证问题一并纳入构建门禁。
  • --skip-files/--skip-dirs:按文件或目录(支持 glob)跳过扫描,用于排除测试数据、缓存目录等非交付内容,可显著缩短构建期扫描耗时并降低误报。
  • --ignorefile(默认.trivyignore:指向忽略文件,与--exit-code配合时,可把已知并已接受风险的漏洞 ID 排除在门禁之外。
  • --format(table/json/template/sarif/cyclonedx/spdx 等):默认表格输出适合直接查看;在 CI 中通常配合--format json或 SARIF 输出到文件(-o report.json),再交给上层流水线做告警聚合与归档。
  • -q, --quiet/--skip-version-check:前者抑制进度条与日志输出,后者关闭版本更新提示,两者都能让构建日志更加干净稳定。

从命令解析实现看,上述开关中--exit-code的默认值为 0(即"发现问题也不改变退出码"),其值在 pkg/flag/report_flags.go 中被注册为整型 flag;只有显式传入非零值(文档示例取 1)才会在存在漏洞时让扫描命令以失败退出。这是理解"为什么--exit-code 1能让docker build失败"的源码级依据。相应地,rootfs作为扫描目标类型在命令入口 pkg/commands/artifact/run.go 中被定义,与imagefsrepo等目标并列,进一步印证"在构建现场扫描当前文件系统"就是 Trivy 一等公民能力。

进阶:把构建期扫描扩展到更多交付物

"在 Dockerfile 里内嵌扫描"与"扫描未打包文件系统"是一体两面。仓库在 unpacked-filesystem.md 中给出了 rootfs 扫描的另一种典型用法——先导出镜像文件系统再扫描:

$ docker export $(docker create alpine:3.10.2) | tar -C /tmp/rootfs -xvf - $ trivy rootfs /tmp/rootfs

这一模式说明:trivy rootfs不仅能在构建现场对/扫描,也能对任意一个解包后的目录(例如 CI 中由docker export导出的临时目录)执行同等逻辑的漏洞检测。因此,无论你采用"内嵌 Dockerfile"还是"导出后扫描",底层都收敛到同一条 rootfs 扫描链路,门禁逻辑与参数调优完全通用。

小结

在 Dockerfile 中内嵌 Trivy,是把安全左移到镜像构建阶段的最低成本方案:直接curl | sh安装后扫描适合快速接入,多阶段COPY --from=aquasec/trivy:latest则兼顾了下载安全性与最终镜像的纯净度;配合--exit-code--no-progress--severity--ignorefile等参数,即可把"镜像含高危漏洞"从"可发布"变为"构建失败",让每一次docker build都同时成为一次可审计、可阻断的安全检查。

若要进一步深挖,可在仓库中继续阅读 contrib/install.sh(安装脚本的下载与校验逻辑)、Dockerfile(官方镜像的构成)、trivy_rootfs.md(rootfs 全部参数)以及 pkg/flag/report_flags.go 与 pkg/commands/artifact/run.go(--exit-code与 rootfs 目标的源码实现)。

【免费下载链接】trivyFind vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more项目地址: https://gitcode.com/GitHub_Trending/tr/trivy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询