3、SBOM生成工具实战:syft、cdxgen与tern
好,咱们进入实战环节。前面聊了那么多SBOM的理论和标准,今天直接上手干活。我个人习惯把这三种工具搭配着用:syft 搞容器镜像,cdxgen 搞应用代码,tern 深挖基础镜像的每一层。说白了,它们覆盖了从「打包好的镜像」到「源码依赖」再到「底层OS组件」的完整链路。
嗯,先看一张总图,理清这三个工具各自负责什么。
3.1 syft:容器镜像的「快速扫描仪」
syft 是我用得最多的工具。为什么?因为它快,而且准。你给它一个镜像名,它直接解包分析,几秒钟就把所有包列出来。
核心能力:支持 Docker / OCI 镜像、本地文件系统、甚至 SBOM 文件本身。输出格式支持 SPDX、CycloneDX、JSON、表格等。
安装很简单,一行命令搞定:
# macOS / Linux
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# 或者用 Docker
docker run -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:latest
实战一下。假设我们要分析一个 Node.js 应用的镜像:
# 生成 SPDX 格式的 SBOM
syft node:18-alpine -o spdx-json > node18-alpine.spdx.json
# 生成 CycloneDX 格式
syft node:18-alpine -o cyclonedx-json > node18-alpine.cdx.json
# 只看表格,快速浏览
syft node:18-alpine -o table
输出长什么样?我截取一段 SPDX 的片段给你看:
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"packages": [
{
"name": "libcrypto3",
"versionInfo": "3.0.8-r4",
"supplier": "Organization: Alpine Linux",
"licenseConcluded": "MIT",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:apk/alpine/libcrypto3@3.0.8-r4?arch=x86_64"
}
]
}
]
}
我的经验:syft 对 Alpine 镜像支持最好,因为 apk 包管理器信息完整。如果是 Ubuntu 镜像,它会解析 dpkg 数据库,速度稍慢但结果一样可靠。我曾经在审计一个 Java 镜像时,syft 帮我发现了三个被标记为高危的 Log4j 版本——嗯,当时团队都惊了。
3.2 cdxgen:应用源码的「依赖侦探」
cdxgen 和 syft 不同。syft 看的是「已经装好的包」,cdxgen 看的是「项目里声明了哪些依赖」。它直接读你的 package.json、pom.xml、requirements.txt 这些文件,然后生成 CycloneDX 格式的 SBOM。
我个人习惯在 CI/CD 的构建阶段跑 cdxgen,这样能提前发现依赖风险,不用等到镜像打包完。
# 安装
npm install -g @cyclonedx/cdxgen
# 或者用 Docker
docker run -v $(pwd):/app cyclonedx/cdxgen -r /app -o /app/bom.json
实战一下,分析一个 Python 项目:
# 进入项目目录
cd my-python-app
# 生成 SBOM,自动识别语言
cdxgen -r . -o bom.json
# 指定输出格式(默认 CycloneDX JSON)
cdxgen -r . -o bom.xml # XML 格式
cdxgen 支持的语言列表很全:
| 语言/生态 | 依赖文件 | 说明 |
|---|---|---|
| JavaScript / Node.js | package.json, package-lock.json | 支持 yarn, pnpm |
| Python | requirements.txt, Pipfile.lock, poetry.lock | 自动识别虚拟环境 |
| Java / JVM | pom.xml, build.gradle, ivy.xml | Maven / Gradle 都支持 |
| Go | go.mod, go.sum | 需要 Go 环境 |
| Rust | Cargo.toml, Cargo.lock | 直接解析 lock 文件 |
| .NET | *.csproj, packages.config | NuGet 生态 |
注意:cdxgen 需要网络去解析某些依赖的元数据。如果你在离线环境跑,记得提前把依赖缓存好。我曾经在客户内网部署时,忘了这茬,结果等了半小时超时——嗯,从那以后我都在 CI 脚本里加了 --offline 参数。
3.3 tern:基础镜像的「层解剖刀」
tern 是个很有意思的工具。它不直接生成 SBOM,而是帮你分析容器镜像的每一层,告诉你每一层里放了什么文件、来自哪个包。这对于审计「基础镜像是否合规」特别有用。
你想想看,有时候基础镜像里多了一个莫名其妙的二进制文件,用 syft 可能只看到包名,但 tern 能告诉你这个文件是在哪一层被加进来的。
# 安装(需要 Python 3.6+)
pip install tern
# 或者用 Docker
docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/output tern report -i node:18-alpine -o /output/report
实战分析:
# 生成完整报告
tern report -i ubuntu:22.04 -o ubuntu-report
# 只查看层信息
tern report -i ubuntu:22.04 -o ubuntu-report --layer-only
tern 的输出会包含每层的文件清单和对应的包管理器信息。比如 Ubuntu 镜像,它会告诉你:
- Layer 0:基础文件系统(来自 ubuntu:22.04 基础层)
- Layer 1:安装了 curl、ca-certificates(来自 apt-get install)
- Layer 2:添加了应用代码(来自 COPY 指令)
关键价值:tern 能帮你回答「这个文件到底是谁放进来的」。在合规审计中,如果发现某个敏感文件(比如 setuid 二进制),tern 可以直接定位到引入它的那一层和对应的包。
3.4 三剑客配合实战
在实际项目中,我通常这样搭配使用:
- 开发阶段:用 cdxgen 从源码生成应用层 SBOM,检查依赖漏洞。
- 构建阶段:用 syft 对构建好的镜像生成完整 SBOM,包含 OS 级依赖。
- 审计阶段:用 tern 对基础镜像做层分析,确认没有「幽灵文件」。
举个例子,一个典型的 CI 流水线脚本片段:
# 1. 源码依赖分析
cdxgen -r . -o bom-source.json
# 2. 构建镜像
docker build -t myapp:${CI_COMMIT_SHA} .
# 3. 镜像 SBOM
syft myapp:${CI_COMMIT_SHA} -o cyclonedx-json > bom-image.json
# 4. 基础镜像层审计
tern report -i myapp:${CI_COMMIT_SHA} -o layer-report
避坑指南:我曾经遇到过一个案例——syft 和 cdxgen 生成的 SBOM 里包版本不一致。后来发现是构建时用了 npm install --production,但 cdxgen 读的是完整的 devDependencies。所以记住:cdxgen 反映的是「项目声明」,syft 反映的是「镜像里实际装的」,两者有差异是正常的,关键是要理解差异的原因。
3.5 输出格式与工具选择建议
最后给个总结表,方便你选工具:
| 场景 | 推荐工具 | 输出格式 | 备注 |
|---|---|---|---|
| 已有容器镜像,快速出 SBOM | syft | SPDX / CycloneDX / JSON | 速度最快,支持最广 |
| 源码项目,开发阶段审计 | cdxgen | CycloneDX (JSON/XML) | 语言生态覆盖全 |
| 基础镜像层合规审计 | tern | 自定义报告 / JSON | 层级别文件溯源 |
| 需要同时看 OS + 应用依赖 | syft + cdxgen | 各自输出后合并 | 推荐用 CycloneDX 格式统一 |
嗯,工具就介绍到这里。记住一点:没有完美的工具,只有合适的组合。syft 快但看不到层历史,cdxgen 准但依赖源码环境,tern 细但输出不够标准化。把它们串起来用,才能覆盖从开发到部署的完整 SBOM 生成链路。