Trivy 生产落地案例
生产落地案例
案例一:从「满屏红」到一周内可读完的报告
背景。一个约二十人的后端团队,服务全部容器化部署。接入扫描工具的第一天,主力服务的镜像扫出数百条漏洞,其中 CRITICAL 与 HIGH 加起来就超过一半。报告发到群里之后没人看——不是不想修,是根本不知道从哪条开始。扫描于是变成一个「跑归跑、没人管」的形式化步骤。
怎么做。他们分了三步。第一步,先让报告变得可读:加 --ignore-unfixed 与 --severity CRITICAL,HIGH,把无法修复和低级别的全部过滤掉,报告从数百条降到几十条。第二步,做归因分析,发现两个占比最大的来源:基础镜像用的是某个已 EOL 的发行版(贡献了一批永远不会有补丁的漏洞),以及多阶段构建写得不彻底,构建期的工具链被带进了运行镜像。第三步,改基础镜像为 alpine 与 distroless,并把多阶段构建的 COPY --from=builder 收到只拷产物;同时把 CI 门禁设成「CRITICAL 且 ignore-unfixed,exit-code 1」。
关键配置。核心就一条命令,放在构建之后、推送之前:
trivy image --severity CRITICAL --ignore-unfixed \
--exit-code 1 --format table --timeout 10m "$IMAGE"
效果。换基础镜像之后,运行镜像里来自 OS 包的漏洞条目大幅下降,剩下的主要是语言依赖里可以直接升级的条目。报告的阅读时间从「没人看」变成「几分钟能过一遍」,门禁也就真正跑起来了。需要说明的是,具体下降到多少取决于原来的镜像构成,这里不给数字。
坑与解决。第一个坑是忽略文件失控:初期为了绕过门禁,有人往 .trivyignore 里加了一堆 CVE,且没有写到期日,半年后这份文件成了实质上的永久白名单。解决方式是强制要求每条带 exp: 到期日与责任人,并把这个文件纳入代码评审,到期未处理的项直接在流水线里报错。第二个坑是离线内网的漏洞库更新失败:内网 runner 出不了 ghcr.io,早期靠手工拷 db 文件,经常忘记更新。后来改成在内网自建一份 trivy-db 的 OCI 镜像、用 --db-repository 指向它,并加了一个定时任务同步上游,问题才算关上。第三个坑是首次扫描慢导致 CI 超时,靠 job 级缓存复用 --cache-dir 解决。
案例二:把密钥扫描作为低成本的兜底
背景。同一个团队在排查一次配置事故时发现,某个服务仓库的历史提交里曾经提交过一个测试用的数据库连接串。虽然早就被后续提交覆盖,但它在 git 历史里仍然完好。这暴露出一个盲区:大家都在关注依赖漏洞,没人管写死的凭据。
怎么做。他们用 Trivy 的 secret 扫描器做兜底:在 MR 流水线里加一条 trivy fs --scanners secret,只对变更的目录跑,速度很快;同时在发布前的镜像扫描里显式加上 --scanners vuln,secret,因为镜像层里也可能残留构建时拷进去的凭据文件。关键配置如下:
# MR 阶段:扫变更目录里的密钥
trivy fs --scanners secret --severity HIGH,CRITICAL --exit-code 1 .
# 发布前:镜像里同时扫漏洞与密钥
trivy image --scanners vuln,secret --severity HIGH,CRITICAL \
--ignore-unfixed --exit-code 1 "$IMAGE"
效果。这条命令不需要漏洞库,因此在内网、在 pre-commit 里都能跑,接入成本几乎为零。它的定位不是替代专门的密钥扫描器,而是在已有的镜像流水线上顺手多一层网。
坑与解决。误报主要来自测试 fixture 和示例配置文件里的占位符,这类误报如果直接写进忽略文件,会连带把真实泄露也屏蔽掉。更稳的做法是把测试目录整体排除在扫描范围之外,或者用 --skip-dirs 之类的方式限定范围,而不是一条条忽略具体字符串。另外要强调的是,密钥扫描发现真实凭据之后,第一动作永远是吊销并轮换,而不是删掉那行代码——只要它曾经进过版本库或镜像层,就必须按已泄露处理。

