1. 项目概述为什么选择Trivy作为你的安全扫描器在软件开发和运维的日常里镜像安全扫描已经从一个“加分项”变成了“必选项”。无论是个人项目还是企业级流水线没人愿意把带有已知高危漏洞的镜像部署到生产环境。市面上扫描工具不少但真正用起来你会发现很多要么太重、要么太贵、要么结果看不懂。我折腾过好几个最后长期留在工具箱里的是Trivy。今天这篇就从一个实际使用者的角度跟你聊聊怎么把它装起来、用起来以及我踩过的那些坑。Trivy这个名字来源于“Tri”和“Vy”寓意着它能从三个维度漏洞、配置、密钥进行扫描目标就是做到全面且简单。它由Aqua Security开源和维护这几年发展势头很猛社区活跃更新也快。最打动我的几点是开箱即用几乎不需要额外配置扫描速度快对CI/CD流水线友好结果清晰易懂直接告诉你问题在哪、严重程度如何、怎么修复。它支持的东西也很多从容器镜像、文件系统到Git仓库甚至Kubernetes的清单文件都能扫。对于刚接触安全扫描的开发者或者想给现有流程快速加上一道安全闸门的团队Trivy是个非常不错的起点。2. 安装前的环境准备与方案选型在真正动手安装之前花几分钟搞清楚你的使用场景和系统环境能省下后面很多调试的时间。Trivy提供了多种安装方式没有绝对的好坏只有最适合你当前情况的。2.1 明确你的核心使用场景首先问自己几个问题扫描对象是什么主要是Docker镜像还是服务器上的文件目录或者是Git仓库里的代码运行环境在哪里是在你个人的开发笔记本上偶尔用用还是要集成到Jenkins、GitLab CI、GitHub Actions这样的自动化流水线里对时效性的要求如何是需要每次构建都实时扫描还是可以定期比如每天跑一次批量扫描如果你的回答偏向于“个人开发机”、“临时扫描几个镜像”那么直接通过包管理器或者下载二进制文件安装是最简单的。如果你的回答是“集成到CI/CD”、“在Kubernetes集群里跑作业”那么你可能需要考虑将Trivy打包进Docker镜像或者直接使用其官方提供的容器镜像来运行。对于大规模、集中化的扫描需求Aqua Security还提供了企业版的Trivy带有中央管理界面和策略引擎但那属于商业产品范畴我们这里主要聚焦开源版本。2.2 系统环境检查与依赖确认Trivy本身是Go语言编写的静态二进制文件理论上的依赖极少。但为了让它能正常工作尤其是扫描容器镜像时你需要确保以下几点操作系统兼容性主流的Linux发行版Ubuntu, CentOS, RHEL, Alpine等、macOS以及Windows通过WSL2体验更佳都支持。我个人的主力环境是Ubuntu 22.04和macOS后续的演示也会基于这两个系统。包管理器可用性如果你打算用apt、yum、brew这样的包管理器安装需要确保网络通畅并且有相应的软件源配置。Docker环境可选但重要如果你要扫描本地Docker镜像那么本地必须安装并运行着Docker Daemon。Trivy需要调用Docker API来拉取和解析镜像。检查命令很简单docker --version和docker ps看服务是否在跑。网络连接Trivy在首次运行时需要从GitHub等源下载漏洞数据库Vulnerability Database。这个数据库文件比较大几百MB所以需要稳定的网络环境。后续的更新会基于增量进行。注意在一些严格的内网环境或离线环境中安装需要提前下载好漏洞数据库文件并通过特定参数指定其路径。这个我们会在后面的“常见问题”部分详细说。2.3 安装方案对比与选择这里我把几种主流安装方式的优缺点列个表你可以快速决策安装方式优点缺点适用场景包管理器安装(apt,yum,brew)安装最方便一键完成便于后续升级管理。版本可能不是最新依赖系统软件源。个人开发机、测试服务器追求安装便利性。下载二进制文件版本可控总能拿到最新版纯静态文件几乎无环境依赖。需要手动下载、放置路径、设置权限升级需重复操作。所有环境尤其是需要特定版本或包管理器不可用的环境。Docker容器运行环境完全隔离不污染宿主机版本切换极其方便。运行命令稍长需要docker run扫描本地镜像需要挂载Docker Socket有一定安全考量。CI/CD流水线集成、Kubernetes Job、快速试用、避免环境冲突。从源码编译适合深度定制或开发贡献者。过程繁琐需要Go语言环境。极少数需要修改Trivy本身功能的场景。对于绝大多数用户我推荐前两种个人长期使用选包管理器临时使用或自动化脚本选二进制文件。Docker方式在CI中非常优雅。接下来我们就分别看看这几种方式的具体操作。3. 多种安装方式详解与实操步骤我会以Ubuntu/macOS为例Windows用户如果使用WSL2操作与Ubuntu类似如果使用原生PowerShell请注意路径和命令的差异。3.1 方案一使用系统包管理器安装最便捷这种方式的核心理念是让系统的包管理工具来帮你处理下载、安装和后续更新。Ubuntu/Debian 系统Trivy的官方仓库已经加入了Aqua Security的APT源。操作步骤如下添加APT存储库密钥这是为了验证软件包的来源真实性。sudo apt-get install wget apt-transport-https gnupg lsb-release wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -这里有个小坑在最新的Debian/Ubuntu版本中apt-key add的方式已被标记为deprecated更推荐将密钥文件放入/etc/apt/trusted.gpg.d/。但Trivy官方脚本目前仍用此方法且能正常工作。如果你系统版本很新且对此有强迫症可以手动下载密钥文件并放置。添加APT软件源echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list这条命令会根据你的系统代号如Ubuntu 22.04是jammy生成正确的源地址。更新软件包列表并安装sudo apt-get update sudo apt-get install trivy安装完成后运行trivy --version验证一下。如果看到版本号输出恭喜你安装成功了。macOS 系统如果你安装了Homebrew那简直不能更简单。brew install aquasecurity/trivy/trivy等待安装完成同样用trivy --version验证。CentOS/RHEL/Fedora 系统使用YUM或DNF仓库。sudo vim /etc/yum.repos.d/trivy.repo在文件中写入以下内容以CentOS 7为例[trivy] nameTrivy repository baseurlhttps://aquasecurity.github.io/trivy-repo/rpm/releases/$releasever/$basearch/ gpgcheck0 enabled1然后安装sudo yum -y update sudo yum -y install trivy注意这里为了简便设置了gpgcheck0在生产环境中建议配置正确的GPG密钥进行校验。3.2 方案二直接下载二进制文件最灵活这是我最常使用的方式特别是在服务器上或需要集成到脚本中时。你可以从Trivy的GitHub Releases页面下载对应平台的最新版本。确定系统架构打开终端执行uname -m。常见输出x86_64(AMD64),aarch64(ARM64),armv7l(ARMv7)等。下载并解压以Linux AMD64架构下载最新版v0.50.1为例。# 下载 wget https://github.com/aquasecurity/trivy/releases/download/v0.50.1/trivy_0.50.1_Linux-64bit.tar.gz # 解压 tar -xzf trivy_0.50.1_Linux-64bit.tar.gz解压后你会得到一个名为trivy的二进制文件。移动到系统路径并赋权sudo mv trivy /usr/local/bin/ sudo chmod x /usr/local/bin/trivy移动到的/usr/local/bin目录通常已经在系统的PATH环境变量中这样你就可以在任意位置直接输入trivy命令了。验证安装trivy --version。对于macOS或Windows流程完全一样只是下载的压缩包后缀不同macOS是.tar.gzWindows是.zip。Windows用户解压后可以将trivy.exe所在目录添加到系统的PATH环境变量中。3.3 方案三使用Docker容器运行最干净如果你不想在主机上安装任何东西或者需要在CI环境中使用Docker方式是最佳选择。前提是你的宿主机已经安装了Docker Engine。运行一个简单的扫描命令如下docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/.cache/trivy:/root/.cache/trivy aquasec/trivy:latest image [你的镜像名]我来拆解一下这个命令docker run --rm: 运行一个容器并在退出后自动删除它避免留下无用容器。-v /var/run/docker.sock:/var/run/docker.sock: 这是关键它将宿主机的Docker守护进程套接字挂载到容器内使得容器内的Trivy能够与宿主机的Docker通信从而拉取和扫描本地镜像。这里涉及安全考虑在不可信的CI环境中需谨慎使用。-v $HOME/.cache/trivy:/root/.cache/trivy: 将漏洞数据库的缓存目录挂载到宿主机。这样下次运行时就不需要重新下载整个数据库可以极大加快扫描启动速度。aquasec/trivy:latest: 使用的Trivy官方容器镜像。你可以指定具体版本如aquasec/trivy:0.50.1以获得确定性的行为。image [你的镜像名]: 这是传递给容器内Trivy的命令参数表示扫描一个镜像。你可以把这个命令封装成一个Shell函数或别名用起来会更方便alias trivydocker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/.cache/trivy:/root/.cache/trivy aquasec/trivy:latest之后就可以像本地命令一样使用trivy image nginx:latest了。4. 首次运行配置与漏洞数据库更新无论用哪种方式安装好第一次运行Trivy时它都不会立刻开始扫描而是会去做一件非常重要的事情下载漏洞数据库。4.1 理解漏洞数据库Trivy本身是一个扫描引擎它依赖一个外部的、持续更新的漏洞数据库来识别软件包中的安全问题。这个数据库集成了多个权威来源如NVD美国国家漏洞数据库、Alpine SecDB、RedHat OVAL等。首次下载可能需要几分钟取决于你的网速因为文件体积有几百MB。执行一个简单的命令来触发数据库下载和更新trivy --download-db-only或者直接运行一次扫描它会自动检查并下载数据库trivy image --clear-cache alpine:latest我建议显式地运行--download-db-only这样你能更清楚地看到下载进度和结果。4.2 数据库缓存位置与离线使用数据库默认会下载到用户主目录下的缓存文件夹中Linux/macOS:~/.cache/trivy/db/Windows:%USERPROFILE%\.cache\trivy\db\离线环境部署指南 如果你需要在无法连接互联网的生产环境或内网中使用Trivy可以按照以下步骤操作在一台有网的环境准备数据库# 下载数据库和元数据 trivy --download-db-only # 缓存目录现在包含了所需的文件 ls -la ~/.cache/trivy/db/打包缓存文件将整个~/.cache/trivy目录或者至少是db子目录打包。tar -czf trivy-offline-cache.tar.gz -C ~/.cache trivy传输到离线环境通过U盘、内部文件服务器等方式将压缩包传到目标服务器。在离线环境解压并指定缓存路径mkdir -p ~/.cache tar -xzf trivy-offline-cache.tar.gz -C ~/.cache # 运行扫描时Trivy会自动使用~/.cache/trivy下的缓存 trivy image --offline-scan nginx:local关键是使用--offline-scan参数它会告诉Trivy不要尝试联网更新数据库。4.3 配置自动更新与扫描策略对于集成到CI/CD的场景你肯定不希望每次扫描都等待数据库更新。有两种策略在扫描前主动更新在CI流水线中先执行一个更新数据库的步骤。虽然这可能会增加几十秒到一分钟的时间但能保证每次扫描都使用最新的漏洞信息。你可以设置一个较短的缓存时间如12小时避免过度频繁更新。# 例如在GitHub Actions的步骤中 - name: Update Trivy DB run: trivy --download-db-only --cache-dir .trivycache - name: Run Trivy scan run: trivy image --cache-dir .trivycache --exit-code 1 --severity HIGH,CRITICAL myapp:latest这里通过--cache-dir指定了一个项目内的缓存目录可以在工作流的不同步骤间共享。使用外部服务维护共享缓存在团队或公司层面可以在一台有网的内网服务器上定期如每6小时运行数据库更新然后将缓存目录通过NFS或对象存储共享给所有CI Runner。Runner在扫描时通过--cache-dir参数指向这个共享目录。这能极大减少网络流量和等待时间。5. 基础扫描命令实战与结果解读安装配置好数据库也齐了现在我们来真刀真枪地扫描点东西。Trivy的子命令很清晰最常用的就是trivy image。5.1 扫描一个远程公共镜像让我们从最简单的开始扫描Docker Hub上的官方Nginx镜像trivy image nginx:latest第一次扫描某个镜像时Trivy会先尝试从本地查找如果没有则会从Docker Hub拉取。你会看到类似下面的输出2024-XX-XXTXX:XX:XX.XXXZ INFO Need to update DB 2024-XX-XXTXX:XX:XX.XXXZ INFO Downloading DB... ... nginx:latest (debian 11.7) Total: 56 (UNKNOWN: 0, LOW: 32, MEDIUM: 18, HIGH: 5, CRITICAL: 1) ------------------------------------------------------------------------------------------------------------------------ | LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE | ------------------------------------------------------------------------------------------------------------------------ | libc-bin | CVE-2024-XXXXX | HIGH | 2.31-13deb11u5 | 2.31-13deb11u6| GNU C Library: ... | | libssl1.1 | CVE-2023-XXXXX | CRITICAL | 1.1.1n-0deb11u4 | 1.1.1n-0deb11u5| OpenSSL: ... | | ... (更多行) | | | | | | ------------------------------------------------------------------------------------------------------------------------这个表格是扫描结果的核心。每一行代表一个检测到的漏洞列的含义如下LIBRARY: 存在漏洞的软件包名称。VULNERABILITY ID: 漏洞的唯一标识符通常是CVE编号。你可以用这个编号去安全公告网站查询详细情况。SEVERITY: 严重等级从低到高为LOW,MEDIUM,HIGH,CRITICAL。这是根据CVSS评分等因素综合判定的。INSTALLED VERSION: 当前镜像中安装的该软件包版本。FIXED VERSION: 已修复该漏洞的软件包版本。这是最关键的信息它直接告诉你应该将软件包升级到哪个版本才能解决这个问题。如果显示为“None”则表示目前还没有官方修复版本。TITLE: 漏洞的简要描述。5.2 扫描本地镜像或镜像归档文件更多时候我们是扫描自己构建的、已经存在于本地Docker环境中的镜像。# 假设你本地有一个名为 myapp标签为 v1.0 的镜像 trivy image myapp:v1.0 # 你也可以扫描一个保存为tar文件的镜像 docker save myapp:v1.0 -o myapp-v1.0.tar trivy image --input myapp-v1.0.tar5.3 使用过滤器聚焦关键问题默认输出可能会很长尤其是对于像ubuntu:latest这样的大型基础镜像。我们可以通过参数过滤只关注高风险问题。按严重等级过滤--severity HIGH,CRITICALtrivy image --severity HIGH,CRITICAL nginx:latest这样输出就只包含高危和严重漏洞便于快速决策。按漏洞类型忽略--ignore-unfixedtrivy image --ignore-unfixed nginx:latest这个参数非常实用。它只显示那些已有修复版本的漏洞。对于那些还没有官方补丁的漏洞即使严重等级很高开发者目前也无能为力看到也只是徒增焦虑。在CI中我通常结合这两个参数使用--severity HIGH,CRITICAL --ignore-unfixed只阻断那些既高危又有修复方案的问题。指定退出码--exit-code 1trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest这是CI集成的灵魂参数。当扫描发现符合条件此处是HIGH和CRITICAL级别的漏洞时Trivy会以退出码1结束。这样你的CI流水线如Jenkins、GitLab CI就能捕获到这个失败状态从而自动阻断构建或部署流程。5.4 生成结构化报告命令行表格输出适合人工查看但自动化流程更需要结构化的数据如JSON、SARIF格式以便后续处理。JSON格式报告trivy image --format json --output result.json nginx:latest生成的result.json文件包含了所有扫描结果的机器可读信息你可以用jq等工具进行解析或者导入到其他安全平台。SARIF格式报告SARIF是一种通用的静态分析结果交换格式可以被GitHub Advanced Security、Azure DevOps等平台原生集成。trivy image --format sarif --output result.sarif nginx:latest在GitHub的仓库中上传SARIF文件可以自动在“Security”标签页下生成安全警报。HTML格式报告用于生成可视化的网页报告更易于在团队内部分享和展示。trivy image --format template --template /usr/local/share/trivy/templates/html.tpl --output report.html nginx:latest注意后面跟的是Trivy内置模板的路径。使用包管理器安装的Trivy通常会自动安装这些模板。如果找不到可以去Trivy的GitHub仓库下载contrib目录下的模板文件。6. 进阶扫描模式与应用场景除了扫描容器镜像Trivy还能胜任其他安全扫描任务这些功能同样强大。6.1 扫描文件系统与Rootfs如果你有一个解压后的容器根文件系统目录或者想扫描一台服务器的整个文件系统谨慎使用可以用filesystem命令。# 扫描一个目录 trivy filesystem --severity HIGH,CRITICAL /path/to/rootfs # 扫描当前目录 trivy fs .这会在指定目录中寻找所有已知的软件包管理器文件如package-lock.json,Gemfile.lock,go.mod,pom.xml等和已安装的二进制文件并检查其依赖的漏洞。注意扫描整个根目录/可能会非常慢并且需要root权限通常不建议这么做。6.2 扫描代码仓库与配置清单Trivy还能作为“基础设施即代码”IaC和“密钥”的扫描工具。扫描Kubernetes清单文件检查你的YAML文件中是否有不安全的配置。trivy config kubernetes.yaml它可以检测出诸如“以特权模式运行容器”、“挂载了主机路径”、“未设置内存限制”等数百种Kubernetes安全最佳实践违规项。扫描Terraform代码trivy config --policy /path/to/policies terraform/你需要指定一个Rego策略文件目录。Trivy内置了一些策略你也可以自定义。扫描仓库中的敏感信息如密码、API密钥、令牌等。trivy repo https://github.com/your-org/your-repo或者扫描本地仓库目录trivy repo --severity HIGH,CRITICAL /path/to/your/git/repo这个功能在代码提交前或CI中运行非常有用可以有效防止密钥被意外提交到版本库。6.3 集成到CI/CD流水线这是Trivy价值最大化的地方。以下是一个GitHub Actions工作流的示例片段它在每次推送到主分支时构建Docker镜像并对其进行安全扫描name: Build, Scan and Push on: push: branches: [ main ] jobs: build-and-scan: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkoutv3 - name: Set up Docker Buildx uses: docker/setup-buildx-actionv2 - name: Login to Container Registry uses: docker/login-actionv2 with: registry: ${{ secrets.REGISTRY_URL }} username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build Docker image run: | docker build -t ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} . docker tag ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} ${{ secrets.REGISTRY_URL }}/myapp:latest - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-actionmaster with: image-ref: ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} format: sarif output: trivy-results.sarif severity: HIGH,CRITICAL ignore-unfixed: true exit-code: 1 # 发现漏洞则失败 - name: Upload SARIF results to GitHub Security uses: github/codeql-action/upload-sarifv2 if: always() # 即使扫描失败也上传结果 with: sarif_file: trivy-results.sarif - name: Push Docker image if: success() # 只有扫描通过才推送镜像 run: | docker push ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} docker push ${{ secrets.REGISTRY_URL }}/myapp:latest这个工作流的关键步骤是“Run Trivy vulnerability scanner”它使用了官方的trivy-action配置了只关注已修复的高危和严重漏洞severity: HIGH,CRITICAL,ignore-unfixed: true并且一旦发现就使步骤失败exit-code: 1。同时无论成功与否都将SARIF格式的结果上传到GitHub Security便于在仓库的安全面板中集中查看。7. 常见问题排查与性能调优在实际使用中你肯定会遇到一些问题。下面是我总结的一些高频问题和解决办法。7.1 网络问题与数据库下载失败这是最常见的问题尤其是在国内网络环境下。症状执行扫描时卡在Downloading DB...很久最后报超时或连接错误。原因Trivy默认从GitHub Releases下载数据库网络可能不稳定。解决方案使用镜像源Trivy支持通过环境变量TRIVY_DB_REPOSITORY指定自定义的数据库镜像源。一些国内的镜像站如华为云镜像提供了Trivy DB的同步。export TRIVY_DB_REPOSITORYhttps://mirror.example.com/aquasecurity/trivy-db trivy --download-db-only手动下载并离线部署如前文“离线使用”部分所述在有网环境下载好数据库然后拷贝到目标机器。增加超时和重试可以通过--timeout参数增加单个操作的超时时间但治标不治本。7.2 扫描速度慢或内存占用高扫描大型镜像如包含完整操作系统的ubuntu:latest或镜像层数非常多时可能会比较慢并消耗较多内存。优化策略使用轻量级基础镜像这是根本解决方法。将ubuntu换成alpine镜像大小和软件包数量会急剧减少扫描速度自然飞快。合理使用缓存确保~/.cache/trivy目录有足够的空间并且在不同扫描任务间共享此缓存。在CI中可以将此目录作为缓存层持久化。调整并发度Trivy默认使用多线程扫描。如果机器资源紧张可以通过--parallel参数减少并发数默认是5。trivy image --parallel 2 myapp:large只扫描特定类型如果你只关心操作系统包漏洞可以跳过语言特定包如npm, pip的扫描反之亦然。但通常不建议会遗漏风险。7.3 误报与漏洞忽略策略没有任何一个扫描工具是100%准确的Trivy也可能出现误报将无害的版本报告为有漏洞。此外有些漏洞在你的特定上下文中可能风险极低但修复成本很高比如需要升级一个底层核心库可能引发兼容性问题。这时你需要一个漏洞忽略策略。Trivy支持通过一个名为.trivyignore的文件来忽略特定的漏洞。创建忽略文件在项目根目录或当前工作目录创建.trivyignore文件。编写忽略规则每行一条规则。你可以根据CVE ID、软件包名、严重等级等来忽略。# 忽略特定的CVE ID直到某个过期日期 CVE-2018-14600 until2024-12-31 CVE-2019-1543 # 忽略某个软件包的所有漏洞慎用 libssl1.1 # 忽略某个软件包的特定版本的所有漏洞 libc-bin 2.31-13deb11u5 # 忽略低危漏洞 severity:LOW运行扫描时自动应用Trivy会自动读取当前目录下的.trivyignore文件。你也可以通过--ignorefile参数指定其他位置的忽略文件。重要心得忽略漏洞是一个需要谨慎记录和评审的过程。最好在.trivyignore文件中为每一条忽略规则添加注释说明忽略的原因、评审人和预计重新评估的日期。这可以作为团队的安全审计依据。7.4 与其他工具集成时的冲突有时Trivy可能和你系统上已有的其他扫描工具如Clair, Grype或包管理器冲突尤其是当它们都尝试管理~/.cache目录下的内容时。解决方案为Trivy指定独立的缓存目录。这可以通过环境变量TRIVY_CACHE_DIR或命令行参数--cache-dir实现。export TRIVY_CACHE_DIR/path/to/trivy-specific-cache trivy image --cache-dir /path/to/trivy-specific-cache nginx:latest将缓存隔离后就能避免工具间的相互干扰。安装和上手Trivy的过程其实是一个逐步建立安全左移意识的过程。从最初的手动扫描到集成进CI流水线自动阻断再到制定团队的漏洞忽略策略每一步都让软件交付变得更可靠一点。工具本身不难难的是坚持使用并将其变成开发流程中自然而然的一环。我自己的经验是先把扫描跑起来哪怕只看看报告你就会对镜像里的世界有全新的认识。然后从阻断最高危的漏洞开始慢慢完善流程。安全没有终点但好的工具能让你起点更高走得更稳。
Trivy安全扫描器从入门到精通:安装、配置与CI/CD集成实战
1. 项目概述为什么选择Trivy作为你的安全扫描器在软件开发和运维的日常里镜像安全扫描已经从一个“加分项”变成了“必选项”。无论是个人项目还是企业级流水线没人愿意把带有已知高危漏洞的镜像部署到生产环境。市面上扫描工具不少但真正用起来你会发现很多要么太重、要么太贵、要么结果看不懂。我折腾过好几个最后长期留在工具箱里的是Trivy。今天这篇就从一个实际使用者的角度跟你聊聊怎么把它装起来、用起来以及我踩过的那些坑。Trivy这个名字来源于“Tri”和“Vy”寓意着它能从三个维度漏洞、配置、密钥进行扫描目标就是做到全面且简单。它由Aqua Security开源和维护这几年发展势头很猛社区活跃更新也快。最打动我的几点是开箱即用几乎不需要额外配置扫描速度快对CI/CD流水线友好结果清晰易懂直接告诉你问题在哪、严重程度如何、怎么修复。它支持的东西也很多从容器镜像、文件系统到Git仓库甚至Kubernetes的清单文件都能扫。对于刚接触安全扫描的开发者或者想给现有流程快速加上一道安全闸门的团队Trivy是个非常不错的起点。2. 安装前的环境准备与方案选型在真正动手安装之前花几分钟搞清楚你的使用场景和系统环境能省下后面很多调试的时间。Trivy提供了多种安装方式没有绝对的好坏只有最适合你当前情况的。2.1 明确你的核心使用场景首先问自己几个问题扫描对象是什么主要是Docker镜像还是服务器上的文件目录或者是Git仓库里的代码运行环境在哪里是在你个人的开发笔记本上偶尔用用还是要集成到Jenkins、GitLab CI、GitHub Actions这样的自动化流水线里对时效性的要求如何是需要每次构建都实时扫描还是可以定期比如每天跑一次批量扫描如果你的回答偏向于“个人开发机”、“临时扫描几个镜像”那么直接通过包管理器或者下载二进制文件安装是最简单的。如果你的回答是“集成到CI/CD”、“在Kubernetes集群里跑作业”那么你可能需要考虑将Trivy打包进Docker镜像或者直接使用其官方提供的容器镜像来运行。对于大规模、集中化的扫描需求Aqua Security还提供了企业版的Trivy带有中央管理界面和策略引擎但那属于商业产品范畴我们这里主要聚焦开源版本。2.2 系统环境检查与依赖确认Trivy本身是Go语言编写的静态二进制文件理论上的依赖极少。但为了让它能正常工作尤其是扫描容器镜像时你需要确保以下几点操作系统兼容性主流的Linux发行版Ubuntu, CentOS, RHEL, Alpine等、macOS以及Windows通过WSL2体验更佳都支持。我个人的主力环境是Ubuntu 22.04和macOS后续的演示也会基于这两个系统。包管理器可用性如果你打算用apt、yum、brew这样的包管理器安装需要确保网络通畅并且有相应的软件源配置。Docker环境可选但重要如果你要扫描本地Docker镜像那么本地必须安装并运行着Docker Daemon。Trivy需要调用Docker API来拉取和解析镜像。检查命令很简单docker --version和docker ps看服务是否在跑。网络连接Trivy在首次运行时需要从GitHub等源下载漏洞数据库Vulnerability Database。这个数据库文件比较大几百MB所以需要稳定的网络环境。后续的更新会基于增量进行。注意在一些严格的内网环境或离线环境中安装需要提前下载好漏洞数据库文件并通过特定参数指定其路径。这个我们会在后面的“常见问题”部分详细说。2.3 安装方案对比与选择这里我把几种主流安装方式的优缺点列个表你可以快速决策安装方式优点缺点适用场景包管理器安装(apt,yum,brew)安装最方便一键完成便于后续升级管理。版本可能不是最新依赖系统软件源。个人开发机、测试服务器追求安装便利性。下载二进制文件版本可控总能拿到最新版纯静态文件几乎无环境依赖。需要手动下载、放置路径、设置权限升级需重复操作。所有环境尤其是需要特定版本或包管理器不可用的环境。Docker容器运行环境完全隔离不污染宿主机版本切换极其方便。运行命令稍长需要docker run扫描本地镜像需要挂载Docker Socket有一定安全考量。CI/CD流水线集成、Kubernetes Job、快速试用、避免环境冲突。从源码编译适合深度定制或开发贡献者。过程繁琐需要Go语言环境。极少数需要修改Trivy本身功能的场景。对于绝大多数用户我推荐前两种个人长期使用选包管理器临时使用或自动化脚本选二进制文件。Docker方式在CI中非常优雅。接下来我们就分别看看这几种方式的具体操作。3. 多种安装方式详解与实操步骤我会以Ubuntu/macOS为例Windows用户如果使用WSL2操作与Ubuntu类似如果使用原生PowerShell请注意路径和命令的差异。3.1 方案一使用系统包管理器安装最便捷这种方式的核心理念是让系统的包管理工具来帮你处理下载、安装和后续更新。Ubuntu/Debian 系统Trivy的官方仓库已经加入了Aqua Security的APT源。操作步骤如下添加APT存储库密钥这是为了验证软件包的来源真实性。sudo apt-get install wget apt-transport-https gnupg lsb-release wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -这里有个小坑在最新的Debian/Ubuntu版本中apt-key add的方式已被标记为deprecated更推荐将密钥文件放入/etc/apt/trusted.gpg.d/。但Trivy官方脚本目前仍用此方法且能正常工作。如果你系统版本很新且对此有强迫症可以手动下载密钥文件并放置。添加APT软件源echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list这条命令会根据你的系统代号如Ubuntu 22.04是jammy生成正确的源地址。更新软件包列表并安装sudo apt-get update sudo apt-get install trivy安装完成后运行trivy --version验证一下。如果看到版本号输出恭喜你安装成功了。macOS 系统如果你安装了Homebrew那简直不能更简单。brew install aquasecurity/trivy/trivy等待安装完成同样用trivy --version验证。CentOS/RHEL/Fedora 系统使用YUM或DNF仓库。sudo vim /etc/yum.repos.d/trivy.repo在文件中写入以下内容以CentOS 7为例[trivy] nameTrivy repository baseurlhttps://aquasecurity.github.io/trivy-repo/rpm/releases/$releasever/$basearch/ gpgcheck0 enabled1然后安装sudo yum -y update sudo yum -y install trivy注意这里为了简便设置了gpgcheck0在生产环境中建议配置正确的GPG密钥进行校验。3.2 方案二直接下载二进制文件最灵活这是我最常使用的方式特别是在服务器上或需要集成到脚本中时。你可以从Trivy的GitHub Releases页面下载对应平台的最新版本。确定系统架构打开终端执行uname -m。常见输出x86_64(AMD64),aarch64(ARM64),armv7l(ARMv7)等。下载并解压以Linux AMD64架构下载最新版v0.50.1为例。# 下载 wget https://github.com/aquasecurity/trivy/releases/download/v0.50.1/trivy_0.50.1_Linux-64bit.tar.gz # 解压 tar -xzf trivy_0.50.1_Linux-64bit.tar.gz解压后你会得到一个名为trivy的二进制文件。移动到系统路径并赋权sudo mv trivy /usr/local/bin/ sudo chmod x /usr/local/bin/trivy移动到的/usr/local/bin目录通常已经在系统的PATH环境变量中这样你就可以在任意位置直接输入trivy命令了。验证安装trivy --version。对于macOS或Windows流程完全一样只是下载的压缩包后缀不同macOS是.tar.gzWindows是.zip。Windows用户解压后可以将trivy.exe所在目录添加到系统的PATH环境变量中。3.3 方案三使用Docker容器运行最干净如果你不想在主机上安装任何东西或者需要在CI环境中使用Docker方式是最佳选择。前提是你的宿主机已经安装了Docker Engine。运行一个简单的扫描命令如下docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/.cache/trivy:/root/.cache/trivy aquasec/trivy:latest image [你的镜像名]我来拆解一下这个命令docker run --rm: 运行一个容器并在退出后自动删除它避免留下无用容器。-v /var/run/docker.sock:/var/run/docker.sock: 这是关键它将宿主机的Docker守护进程套接字挂载到容器内使得容器内的Trivy能够与宿主机的Docker通信从而拉取和扫描本地镜像。这里涉及安全考虑在不可信的CI环境中需谨慎使用。-v $HOME/.cache/trivy:/root/.cache/trivy: 将漏洞数据库的缓存目录挂载到宿主机。这样下次运行时就不需要重新下载整个数据库可以极大加快扫描启动速度。aquasec/trivy:latest: 使用的Trivy官方容器镜像。你可以指定具体版本如aquasec/trivy:0.50.1以获得确定性的行为。image [你的镜像名]: 这是传递给容器内Trivy的命令参数表示扫描一个镜像。你可以把这个命令封装成一个Shell函数或别名用起来会更方便alias trivydocker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/.cache/trivy:/root/.cache/trivy aquasec/trivy:latest之后就可以像本地命令一样使用trivy image nginx:latest了。4. 首次运行配置与漏洞数据库更新无论用哪种方式安装好第一次运行Trivy时它都不会立刻开始扫描而是会去做一件非常重要的事情下载漏洞数据库。4.1 理解漏洞数据库Trivy本身是一个扫描引擎它依赖一个外部的、持续更新的漏洞数据库来识别软件包中的安全问题。这个数据库集成了多个权威来源如NVD美国国家漏洞数据库、Alpine SecDB、RedHat OVAL等。首次下载可能需要几分钟取决于你的网速因为文件体积有几百MB。执行一个简单的命令来触发数据库下载和更新trivy --download-db-only或者直接运行一次扫描它会自动检查并下载数据库trivy image --clear-cache alpine:latest我建议显式地运行--download-db-only这样你能更清楚地看到下载进度和结果。4.2 数据库缓存位置与离线使用数据库默认会下载到用户主目录下的缓存文件夹中Linux/macOS:~/.cache/trivy/db/Windows:%USERPROFILE%\.cache\trivy\db\离线环境部署指南 如果你需要在无法连接互联网的生产环境或内网中使用Trivy可以按照以下步骤操作在一台有网的环境准备数据库# 下载数据库和元数据 trivy --download-db-only # 缓存目录现在包含了所需的文件 ls -la ~/.cache/trivy/db/打包缓存文件将整个~/.cache/trivy目录或者至少是db子目录打包。tar -czf trivy-offline-cache.tar.gz -C ~/.cache trivy传输到离线环境通过U盘、内部文件服务器等方式将压缩包传到目标服务器。在离线环境解压并指定缓存路径mkdir -p ~/.cache tar -xzf trivy-offline-cache.tar.gz -C ~/.cache # 运行扫描时Trivy会自动使用~/.cache/trivy下的缓存 trivy image --offline-scan nginx:local关键是使用--offline-scan参数它会告诉Trivy不要尝试联网更新数据库。4.3 配置自动更新与扫描策略对于集成到CI/CD的场景你肯定不希望每次扫描都等待数据库更新。有两种策略在扫描前主动更新在CI流水线中先执行一个更新数据库的步骤。虽然这可能会增加几十秒到一分钟的时间但能保证每次扫描都使用最新的漏洞信息。你可以设置一个较短的缓存时间如12小时避免过度频繁更新。# 例如在GitHub Actions的步骤中 - name: Update Trivy DB run: trivy --download-db-only --cache-dir .trivycache - name: Run Trivy scan run: trivy image --cache-dir .trivycache --exit-code 1 --severity HIGH,CRITICAL myapp:latest这里通过--cache-dir指定了一个项目内的缓存目录可以在工作流的不同步骤间共享。使用外部服务维护共享缓存在团队或公司层面可以在一台有网的内网服务器上定期如每6小时运行数据库更新然后将缓存目录通过NFS或对象存储共享给所有CI Runner。Runner在扫描时通过--cache-dir参数指向这个共享目录。这能极大减少网络流量和等待时间。5. 基础扫描命令实战与结果解读安装配置好数据库也齐了现在我们来真刀真枪地扫描点东西。Trivy的子命令很清晰最常用的就是trivy image。5.1 扫描一个远程公共镜像让我们从最简单的开始扫描Docker Hub上的官方Nginx镜像trivy image nginx:latest第一次扫描某个镜像时Trivy会先尝试从本地查找如果没有则会从Docker Hub拉取。你会看到类似下面的输出2024-XX-XXTXX:XX:XX.XXXZ INFO Need to update DB 2024-XX-XXTXX:XX:XX.XXXZ INFO Downloading DB... ... nginx:latest (debian 11.7) Total: 56 (UNKNOWN: 0, LOW: 32, MEDIUM: 18, HIGH: 5, CRITICAL: 1) ------------------------------------------------------------------------------------------------------------------------ | LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE | ------------------------------------------------------------------------------------------------------------------------ | libc-bin | CVE-2024-XXXXX | HIGH | 2.31-13deb11u5 | 2.31-13deb11u6| GNU C Library: ... | | libssl1.1 | CVE-2023-XXXXX | CRITICAL | 1.1.1n-0deb11u4 | 1.1.1n-0deb11u5| OpenSSL: ... | | ... (更多行) | | | | | | ------------------------------------------------------------------------------------------------------------------------这个表格是扫描结果的核心。每一行代表一个检测到的漏洞列的含义如下LIBRARY: 存在漏洞的软件包名称。VULNERABILITY ID: 漏洞的唯一标识符通常是CVE编号。你可以用这个编号去安全公告网站查询详细情况。SEVERITY: 严重等级从低到高为LOW,MEDIUM,HIGH,CRITICAL。这是根据CVSS评分等因素综合判定的。INSTALLED VERSION: 当前镜像中安装的该软件包版本。FIXED VERSION: 已修复该漏洞的软件包版本。这是最关键的信息它直接告诉你应该将软件包升级到哪个版本才能解决这个问题。如果显示为“None”则表示目前还没有官方修复版本。TITLE: 漏洞的简要描述。5.2 扫描本地镜像或镜像归档文件更多时候我们是扫描自己构建的、已经存在于本地Docker环境中的镜像。# 假设你本地有一个名为 myapp标签为 v1.0 的镜像 trivy image myapp:v1.0 # 你也可以扫描一个保存为tar文件的镜像 docker save myapp:v1.0 -o myapp-v1.0.tar trivy image --input myapp-v1.0.tar5.3 使用过滤器聚焦关键问题默认输出可能会很长尤其是对于像ubuntu:latest这样的大型基础镜像。我们可以通过参数过滤只关注高风险问题。按严重等级过滤--severity HIGH,CRITICALtrivy image --severity HIGH,CRITICAL nginx:latest这样输出就只包含高危和严重漏洞便于快速决策。按漏洞类型忽略--ignore-unfixedtrivy image --ignore-unfixed nginx:latest这个参数非常实用。它只显示那些已有修复版本的漏洞。对于那些还没有官方补丁的漏洞即使严重等级很高开发者目前也无能为力看到也只是徒增焦虑。在CI中我通常结合这两个参数使用--severity HIGH,CRITICAL --ignore-unfixed只阻断那些既高危又有修复方案的问题。指定退出码--exit-code 1trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest这是CI集成的灵魂参数。当扫描发现符合条件此处是HIGH和CRITICAL级别的漏洞时Trivy会以退出码1结束。这样你的CI流水线如Jenkins、GitLab CI就能捕获到这个失败状态从而自动阻断构建或部署流程。5.4 生成结构化报告命令行表格输出适合人工查看但自动化流程更需要结构化的数据如JSON、SARIF格式以便后续处理。JSON格式报告trivy image --format json --output result.json nginx:latest生成的result.json文件包含了所有扫描结果的机器可读信息你可以用jq等工具进行解析或者导入到其他安全平台。SARIF格式报告SARIF是一种通用的静态分析结果交换格式可以被GitHub Advanced Security、Azure DevOps等平台原生集成。trivy image --format sarif --output result.sarif nginx:latest在GitHub的仓库中上传SARIF文件可以自动在“Security”标签页下生成安全警报。HTML格式报告用于生成可视化的网页报告更易于在团队内部分享和展示。trivy image --format template --template /usr/local/share/trivy/templates/html.tpl --output report.html nginx:latest注意后面跟的是Trivy内置模板的路径。使用包管理器安装的Trivy通常会自动安装这些模板。如果找不到可以去Trivy的GitHub仓库下载contrib目录下的模板文件。6. 进阶扫描模式与应用场景除了扫描容器镜像Trivy还能胜任其他安全扫描任务这些功能同样强大。6.1 扫描文件系统与Rootfs如果你有一个解压后的容器根文件系统目录或者想扫描一台服务器的整个文件系统谨慎使用可以用filesystem命令。# 扫描一个目录 trivy filesystem --severity HIGH,CRITICAL /path/to/rootfs # 扫描当前目录 trivy fs .这会在指定目录中寻找所有已知的软件包管理器文件如package-lock.json,Gemfile.lock,go.mod,pom.xml等和已安装的二进制文件并检查其依赖的漏洞。注意扫描整个根目录/可能会非常慢并且需要root权限通常不建议这么做。6.2 扫描代码仓库与配置清单Trivy还能作为“基础设施即代码”IaC和“密钥”的扫描工具。扫描Kubernetes清单文件检查你的YAML文件中是否有不安全的配置。trivy config kubernetes.yaml它可以检测出诸如“以特权模式运行容器”、“挂载了主机路径”、“未设置内存限制”等数百种Kubernetes安全最佳实践违规项。扫描Terraform代码trivy config --policy /path/to/policies terraform/你需要指定一个Rego策略文件目录。Trivy内置了一些策略你也可以自定义。扫描仓库中的敏感信息如密码、API密钥、令牌等。trivy repo https://github.com/your-org/your-repo或者扫描本地仓库目录trivy repo --severity HIGH,CRITICAL /path/to/your/git/repo这个功能在代码提交前或CI中运行非常有用可以有效防止密钥被意外提交到版本库。6.3 集成到CI/CD流水线这是Trivy价值最大化的地方。以下是一个GitHub Actions工作流的示例片段它在每次推送到主分支时构建Docker镜像并对其进行安全扫描name: Build, Scan and Push on: push: branches: [ main ] jobs: build-and-scan: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkoutv3 - name: Set up Docker Buildx uses: docker/setup-buildx-actionv2 - name: Login to Container Registry uses: docker/login-actionv2 with: registry: ${{ secrets.REGISTRY_URL }} username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build Docker image run: | docker build -t ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} . docker tag ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} ${{ secrets.REGISTRY_URL }}/myapp:latest - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-actionmaster with: image-ref: ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} format: sarif output: trivy-results.sarif severity: HIGH,CRITICAL ignore-unfixed: true exit-code: 1 # 发现漏洞则失败 - name: Upload SARIF results to GitHub Security uses: github/codeql-action/upload-sarifv2 if: always() # 即使扫描失败也上传结果 with: sarif_file: trivy-results.sarif - name: Push Docker image if: success() # 只有扫描通过才推送镜像 run: | docker push ${{ secrets.REGISTRY_URL }}/myapp:${{ github.sha }} docker push ${{ secrets.REGISTRY_URL }}/myapp:latest这个工作流的关键步骤是“Run Trivy vulnerability scanner”它使用了官方的trivy-action配置了只关注已修复的高危和严重漏洞severity: HIGH,CRITICAL,ignore-unfixed: true并且一旦发现就使步骤失败exit-code: 1。同时无论成功与否都将SARIF格式的结果上传到GitHub Security便于在仓库的安全面板中集中查看。7. 常见问题排查与性能调优在实际使用中你肯定会遇到一些问题。下面是我总结的一些高频问题和解决办法。7.1 网络问题与数据库下载失败这是最常见的问题尤其是在国内网络环境下。症状执行扫描时卡在Downloading DB...很久最后报超时或连接错误。原因Trivy默认从GitHub Releases下载数据库网络可能不稳定。解决方案使用镜像源Trivy支持通过环境变量TRIVY_DB_REPOSITORY指定自定义的数据库镜像源。一些国内的镜像站如华为云镜像提供了Trivy DB的同步。export TRIVY_DB_REPOSITORYhttps://mirror.example.com/aquasecurity/trivy-db trivy --download-db-only手动下载并离线部署如前文“离线使用”部分所述在有网环境下载好数据库然后拷贝到目标机器。增加超时和重试可以通过--timeout参数增加单个操作的超时时间但治标不治本。7.2 扫描速度慢或内存占用高扫描大型镜像如包含完整操作系统的ubuntu:latest或镜像层数非常多时可能会比较慢并消耗较多内存。优化策略使用轻量级基础镜像这是根本解决方法。将ubuntu换成alpine镜像大小和软件包数量会急剧减少扫描速度自然飞快。合理使用缓存确保~/.cache/trivy目录有足够的空间并且在不同扫描任务间共享此缓存。在CI中可以将此目录作为缓存层持久化。调整并发度Trivy默认使用多线程扫描。如果机器资源紧张可以通过--parallel参数减少并发数默认是5。trivy image --parallel 2 myapp:large只扫描特定类型如果你只关心操作系统包漏洞可以跳过语言特定包如npm, pip的扫描反之亦然。但通常不建议会遗漏风险。7.3 误报与漏洞忽略策略没有任何一个扫描工具是100%准确的Trivy也可能出现误报将无害的版本报告为有漏洞。此外有些漏洞在你的特定上下文中可能风险极低但修复成本很高比如需要升级一个底层核心库可能引发兼容性问题。这时你需要一个漏洞忽略策略。Trivy支持通过一个名为.trivyignore的文件来忽略特定的漏洞。创建忽略文件在项目根目录或当前工作目录创建.trivyignore文件。编写忽略规则每行一条规则。你可以根据CVE ID、软件包名、严重等级等来忽略。# 忽略特定的CVE ID直到某个过期日期 CVE-2018-14600 until2024-12-31 CVE-2019-1543 # 忽略某个软件包的所有漏洞慎用 libssl1.1 # 忽略某个软件包的特定版本的所有漏洞 libc-bin 2.31-13deb11u5 # 忽略低危漏洞 severity:LOW运行扫描时自动应用Trivy会自动读取当前目录下的.trivyignore文件。你也可以通过--ignorefile参数指定其他位置的忽略文件。重要心得忽略漏洞是一个需要谨慎记录和评审的过程。最好在.trivyignore文件中为每一条忽略规则添加注释说明忽略的原因、评审人和预计重新评估的日期。这可以作为团队的安全审计依据。7.4 与其他工具集成时的冲突有时Trivy可能和你系统上已有的其他扫描工具如Clair, Grype或包管理器冲突尤其是当它们都尝试管理~/.cache目录下的内容时。解决方案为Trivy指定独立的缓存目录。这可以通过环境变量TRIVY_CACHE_DIR或命令行参数--cache-dir实现。export TRIVY_CACHE_DIR/path/to/trivy-specific-cache trivy image --cache-dir /path/to/trivy-specific-cache nginx:latest将缓存隔离后就能避免工具间的相互干扰。安装和上手Trivy的过程其实是一个逐步建立安全左移意识的过程。从最初的手动扫描到集成进CI流水线自动阻断再到制定团队的漏洞忽略策略每一步都让软件交付变得更可靠一点。工具本身不难难的是坚持使用并将其变成开发流程中自然而然的一环。我自己的经验是先把扫描跑起来哪怕只看看报告你就会对镜像里的世界有全新的认识。然后从阻断最高危的漏洞开始慢慢完善流程。安全没有终点但好的工具能让你起点更高走得更稳。