目录
[3.1 安装方式](#3.1 安装方式)
[3.2 基础命令](#3.2 基础命令)
[3.3 CI/CD 集成](#3.3 CI/CD 集成)
Trivy 是一个广受好评的开源安全扫描工具,以其全面性、易用性和高速的扫描能力而闻名。它由 Aqua Security 公司主导开发,旨在帮助开发者和安全团队在软件开发生命周期的早期发现并修复安全风险 。下面,我将从多个角度为你详细解析这款工具。
一、简介、来源与历史:从个人项目到业界标准
Trivy 的发音是"tri(像trigger)vy(像envy)" 。它的诞生源于其创造者 Teppei Fukuda 的个人需求。在运营 Kubernetes 集群时,他迫切需要一款工具来管理容器镜像中的漏洞,但市面上的解决方案要么设置复杂、要么速度慢、要么准确度低,而且都不够"左移"(即在开发和 CI 流程早期介入)。
2019年,Teppei 利用长假将 Trivy 作为个人兴趣项目开发出来并发布到 GitHub。由于其满足了市场的真实痛点,项目迅速获得关注。很快,Aqua Security 公司向他伸出了橄榄枝,邀请他带着 Trivy 加入,全职投入到该项目的开发中 。
在 Aqua 的推动下,Trivy 从一个仅扫描容器镜像的漏洞检测工具,进化为一个功能全面的安全扫描器。如今,它已成为云原生安全领域的明星项目,在 GitHub 上拥有大量 Star,并被 Harbor、GitLab 等众多知名平台集成 。
二、核心功能:多目标、多扫描器的统一解决方案
Trivy 的核心设计理念是 "All in one",即用一个工具解决多种安全扫描需求。它通过定义清晰的目标和扫描器来实现这一点 。
| 功能维度 | 支持范围 |
|---|---|
| 扫描目标 | 容器镜像 、文件系统 、Git 仓库 、Kubernetes 集群 、虚拟机镜像 、云环境(如 AWS) |
| 内置扫描器 | 漏洞 (CVE)、错误配置 (IaC)、敏感信息 (秘钥)、软件许可证、SBOM 生成与分析 |
- 漏洞扫描:这是 Trivy 的看家本领。它能检测 OS 包(如 Alpine, Debian, CentOS 等)和应用程序依赖(如 npm, pip, gem, cargo 等)中的已知漏洞(CVE)。其高准确性得益于对多种数据源的整合和精心设计的解析逻辑 。
- IaC 错误配置扫描:检测 Terraform、Dockerfile、Kubernetes 清单等基础设施即代码文件中的配置问题,确保环境符合安全最佳实践 。
- 敏感信息扫描:扫描文件系统或容器镜像中是否意外包含了秘钥、密码、API token 等敏感信息 。
- 软件组成分析:可以生成 SPDX、CycloneDX 等标准格式的软件物料清单,并能直接扫描已有的 SBOM 文件来查找漏洞,帮助理清软件供应链的组成 。
- 许可证合规性检查:识别项目依赖的软件包所使用的开源许可证,帮助团队规避潜在的合规风险 。
三、使用方式:极简设计与灵活集成
Trivy 的使用哲学是"开箱即用"。它是一个无状态的单二进制文件,不依赖任何数据库或运行时环境,下载后即可运行,这使其在 CI/CD 管道中非常受欢迎 。
3.1 安装方式
Trivy的安装方式及其多样,几乎覆盖所有主流平台:
bash
# macOS
brew install trivy
# Linux (以RHEL/CentOS为例)
sudo vim /etc/yum.repos.d/trivy.repo # 添加仓库
sudo yum -y install trivy
# Docker
docker run aquasec/trivy
# 或直接从GitHub Releases下载二进制文件 [citation:2][citation:7]
基于docker无需配置、不污染系统、跨平台通用、一键运行、卸载干净等等等优点,所以说如果有docker的话,还是最推荐使用docker进行部署(下面以部署发布于一个月前并且直至今日的最新版本为例):
bash
docker pull aquasec/trivy:latest
注意下载安装Trivy的过程中最好还是使用一点魔法会快很多:
如果因为网络抖动出现以下拒绝链接的问题也不是什么大事,保证网络稳定重新下载即可:

最终成功下载就是这样的:

3.2 基础命令
Trivy 的命令行结构清晰,遵循 trivy <目标类型> <目标> 的模式 。
bash
#扫描容器镜像
trivy image python:3.4-alpine
#扫描本地文件系统(可同时开启多个扫描器)
trivy fs --scanners vuln,secret,misconfig ./my-project
#扫描 Git 仓库
trivy repo https://github.com/spring-projects/spring-petclinic
#扫描 Kubernetes 集群
trivy k8s --report summary cluster [citation:2][citation:4]
3.3 CI/CD 集成
Trivy 非常适合在 CI 管道中使用。官方提供了 GitHub Action,可以轻松地将扫描集成到代码推送或镜像构建流程中 。
bash
# GitHub Actions 示例
name: Container Image Scan
on: push
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'your-image:latest' [citation:2]
四、适用方向:从开发者到安全团队
-
开发者:在本地编码或提交前,快速扫描自己的代码目录或依赖文件,及时发现漏洞和秘钥泄露。
-
DevOps / 平台工程师:将 Trivy 集成到 CI/CD 流水线中,在构建镜像后、推送到仓库前自动阻断包含高危漏洞的镜像 。
-
安全团队:通过 Trivy 持续扫描生产环境(如 Kubernetes 集群)的镜像和配置,或在私有镜像仓库中批量扫描所有镜像,建立全局的安全可见性。
-
云原生平台:作为底层扫描引擎,被 Harbor、GitLab 等平台集成,为其用户提供安全能力 。
五、优秀源码解析
Trivy 的核心优势在于其准确性 和无依赖 的特性。为了实现这一点,开发者在源码层面付出了巨大努力。下面我们以 RPM 包解析为例,看看它是如何做到不依赖系统命令的。
大多数扫描器可能会简单地调用系统命令 rpm -qa 来列出容器内安装的包。但这意味着 Trivy 运行时必须依赖外部的 rpm 命令,破坏了其"单文件、无依赖"的设计哲学。
Trivy 的解法是:用 Go 语言重新实现核心逻辑 。相关的代码于 pkg/dependency/parser/golang 或与系统包管理交互的部分。
Go
// 概念性代码,用于说明 Trivy 内部如何工作
package main
import (
"debug/elf" // 用于解析 ELF 文件
"fmt"
"io"
"regexp"
)
// scanRpmPackages 模拟 Trivy 扫描一个基于 RPM 的容器镜像层的函数
func scanRpmPackages(layerFilesystem map[string][]byte) []string {
var packages []string
// 1. 定位数据库:RPM 包的信息存储在特定路径的文件中,如 /var/lib/rpm/Packages
rpmDBPath := "/var/lib/rpm/Packages"
dbContent, exists := layerFilesystem[rpmDBPath]
if !exists {
fmt.Println("RPM 数据库不存在,可能不是 RPM 系统")
return packages
}
// 2. 解析二进制数据库 (这是最复杂的部分):
// RPM 的 Packages 文件是一个 Berkeley DB 格式的二进制文件,存储了所有包的元数据。
// Trivy 不能调用外部命令,所以它内部包含了一个用 Go 编写的 Berkeley DB 读取器。
// - 它会打开这个二进制文件。
// - 遍历数据库中的记录。
// - 从每条记录中提取包名 (Name)、版本 (Version)、发行版 (Release) 和架构 (Arch)。
// 假设我们有一个虚拟的解析器
dbReader := NewBerkeleyDBReader(dbContent)
for dbReader.Next() {
record := dbReader.Record()
// 提取关键信息
name := record.GetString("Name")
version := record.GetString("Version")
release := record.GetString("Release")
arch := record.GetString("Arch")
// 组合成标准格式,例如 "bash-4.4.19-7.el8.x86_64"
fullPackageName := fmt.Sprintf("%s-%s-%s.%s", name, version, release, arch)
packages = append(packages, fullPackageName)
}
// 3. 漏洞匹配:拿到包列表和版本后,Trivy 会与本地缓存的漏洞数据库(如 Red Hat OVAL 数据)进行匹配。
// 如果包的版本在漏洞数据库中某个 CVE 的受影响版本范围内,则报告该漏洞。
return packages
}
// NewBerkeleyDBReader 是一个占位符,表示 Trivy 复杂的解析逻辑
func NewBerkeleyDBReader(data []byte) *DummyDBReader {
return &DummyDBReader{data: data}
}
type DummyDBReader struct {
data []byte
// ... 内部状态
}
func (d *DummyDBReader) Next() bool {
// 实际逻辑:遍历数据库条目
return false
}
func (d *DummyDBReader) Record() map[string]string {
// 实际逻辑:将当前二进制条目解析为 key-value 对
return map[string]string{
"Name": "bash",
"Version": "4.4.19",
"Release": "7.el8",
"Arch": "x86_64",
}
}
func main() {
// 模拟扫描流程
fmt.Println("Trivy 正在解析 RPM 数据库...")
// packages := scanRpmPackages(layerFiles)
// fmt.Println("找到的包:", packages)
}
正如其创造者 Teppei 在博客中所言,直接调用系统命令是最简单的方法,但为了追求"易于安装和使用"的核心理念,Trivy 团队选择了一条更艰难的道路------用 Go 重写这些底层交互 。这种对细节的执着,正是 Trivy 能够在众多扫描器中脱颖而出,获得广泛认可的关键所在 。
参考文献:
1\] [https://developer.aliyun.com/article/1630623](https://developer.aliyun.com/article/1630623 "https://developer.aliyun.com/article/1630623") \[2\] [https://www.aquasec.com/blog/trivy-scanner/#demo-form](https://www.aquasec.com/blog/trivy-scanner/#demo-form "https://www.aquasec.com/blog/trivy-scanner/#demo-form") \[3\] [https://m.yisu.com/ask/99900794.html](https://m.yisu.com/ask/99900794.html "https://m.yisu.com/ask/99900794.html")