Trivy:从文件系统到镜像与 SBOM 的多目标安全扫描
Trivy 能扫描文件系统、容器镜像、SBOM,也能检查配置与 Secret。入口多的代价是“扫描通过”很容易被误读:目标可能不是预期镜像,漏洞库可能过期,Java 数据库可能未准备,某个 scanner 可能被关闭。可靠结论必须同时绑定目标摘要、scanner 集合、数据库状态、配置和工具版本。
本文使用 Trivy 0.74.0 作为实验坐标。正式 Runner 应固定 Release 资产校验值或镜像 digest,不能每次拉取 latest。
安装与版本证据
官方 Release 提供平台资产与 checksums。受控工具仓库先完成下载和摘要验证,再向 Runner 分发:
set -euo pipefail
TRIVY_VERSION=0.74.0
ASSET="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
BASE="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}"
work_dir="$(mktemp -d)"
cd "$work_dir"
curl -fLO "${BASE}/${ASSET}"
curl -fLO "${BASE}/trivy_${TRIVY_VERSION}_checksums.txt"
grep " ${ASSET}$" "trivy_${TRIVY_VERSION}_checksums.txt" | sha256sum -c -
tar -xzf "$ASSET" trivy
install -m 0755 trivy "$HOME/.local/bin/trivy"
trivy --version版本输出还会显示数据库信息。保存 CLI 版本、vulnerability DB、Java DB、策略 bundle 和配置摘要,才能解释同一目标为何在两次任务间变化。
目标身份先于漏洞数量
扫描镜像标签前先解析不可变 digest:
IMAGE_REF="registry.example.invalid/team/app:candidate"
docker buildx imagetools inspect "$IMAGE_REF"
trivy image --format json --output artifacts/trivy-image.json "$IMAGE_REF"报告必须保留 RepoDigest 或等价身份。标签可以移动,扫描 candidate 与发布 candidate 不一定是同一字节。文件系统扫描则记录 Git revision、工作目录和 lockfile;SBOM 扫描记录 SBOM 哈希、格式与生成工具。
trivy fs --scanners vuln,secret,misconfig \
--format json --output artifacts/trivy-fs.json .
trivy sbom --format json \
--output artifacts/trivy-sbom.json artifacts/app.cdx.json不同目标入口的可见范围不同。镜像扫描能看到操作系统包和镜像层中的应用依赖;工作区可能包含未进入镜像的开发依赖;SBOM 只包含生成器识别出的组件。三份报告不能只按 CVE 数量横向比较。
漏洞数据库必须显式准备
联网同步任务可以只下载数据库:
TRIVY_CACHE_DIR="$PWD/.cache/trivy" \
trivy image --download-db-only
TRIVY_CACHE_DIR="$PWD/.cache/trivy" \
trivy image --download-java-db-only离线扫描把已经验证的缓存作为只读制品传入,并禁止隐式更新:
TRIVY_CACHE_DIR="/secure/trivy-cache" \
trivy image --offline-scan --skip-db-update --skip-java-db-update \
--format json --output artifacts/trivy-offline.json \
"$IMAGE_REF"缓存目录包含 SQLite、元数据和策略内容,不应由低信任任务覆盖高信任任务。数据库过期、损坏或缺少 Java DB 应是 tool-failed,不能退化为零漏洞。
用固定退出码区分命中与故障
门禁可以给漏洞命中专用状态 23:
set +e
trivy image \
--severity HIGH,CRITICAL \
--exit-code 23 \
--format json \
--output artifacts/trivy-gate.json \
"$IMAGE_REF"
scanner_rc=$?
set -e
case "$scanner_rc" in
0) printf 'no threshold finding\n' ;;
23) printf 'vulnerability threshold reached\n' >&2; exit 23 ;;
*) printf 'trivy execution failed: %s\n' "$scanner_rc" >&2; exit "$scanner_rc" ;;
esac不要用 || true 确保报告上传。上传应由 finally/always 阶段执行,同时保留原状态。报告为空、JSON 无法解析和没有漏洞是三种不同结果。
严重度、修复状态与 VEX
严重度来自不同 advisory 来源,发行版可能覆盖 NVD 评分。先确认包坐标、已安装版本、发行版和数据源,再决定门禁。--ignore-unfixed 只适合某些门禁视图,完整报告仍应保留无修复项,否则“暂时没有补丁”会被误解为“不存在风险”。
VEX 表达组件在当前产品上下文中的可利用性状态。它不是全局忽略文件。接受 VEX 前核对 subject、组件标识、漏洞 ID、状态、理由、作者、签名和到期;镜像或 SBOM 变化后重新评估。宽泛 .trivyignore 也要有 owner 与期限,并用正反样例证明没有吞掉邻近结果。
配置与 Secret 扫描的额外边界
misconfiguration 扫描依赖策略 bundle 和 IaC 解析,规则版本变化会改变结果。Secret 扫描会读取源码与镜像层,报告本身可能包含路径和候选上下文。公开 Fork PR 不注入生产凭据,报告进入受限制品库。
多 scanner 并行带来 CPU、内存和网络开销。先在固定目标上分别测 vuln、secret、misconfig,再决定 PR 快速通道和周期全量任务。关闭慢 scanner 必须显式记录,不能只看总耗时下降。
多架构镜像还要明确扫描 manifest list 还是某个平台镜像。发布门禁若只看默认平台,另一个架构可能携带不同操作系统包。流水线应解析平台 digest,逐个平台保存报告,再生成聚合裁决;平台缺失和平台无漏洞同样不能合并成一个绿色状态。
这项区分必须进入报告元数据。
故障、升级与清理
同一镜像结果变化,比较 digest、Trivy、数据库、Java DB、策略和 ignore。CI 零结果而本机有结果,检查 scanner、缓存、目标平台和认证。Registry 429/5xx 与漏洞命中分开统计;提高 Registry 权限不能解决数据库问题。
升级前用固定镜像 digest、工作区快照和 SBOM 跑新旧版本,比较组件、漏洞、严重度、数据源、跳过项与耗时。回滚恢复 CLI、数据库快照、配置和策略,不能靠扩大 ignore 或关闭 scanner。
trivy clean --vuln-db --java-db 会删除共享缓存,在多任务 Runner 上只能由明确维护流程执行。删除前解析缓存绝对路径并确认不是工作区根或用户目录。迁移到其他扫描器时,先用相同 digest 和 SBOM 验证组件覆盖与门禁状态,再退出 Trivy。
