4、Syft入门:安装Syft,使用Syft生成第一个容器镜像的SBOM
好,咱们进入实战环节。
前面聊了那么多SBOM的理论和标准,说实话,光说不练假把式。这一章,我们就亲手把第一个SBOM搞出来。工具选谁?我推荐Syft。原因很简单:快,准,而且社区活跃。我在好几个客户的CI流水线里都用的它,没出过什么幺蛾子。
4.1 为什么是Syft?
市面上能生成SBOM的工具不少,比如Trivy、Dependency-Track、SPDX Tools。但Syft有个很实在的优势——它专为容器镜像设计。你想想看,我们做软件供应链安全,最头疼的就是镜像里那些乱七八糟的依赖:操作系统层的包管理器(apt、yum、apk)、编程语言的包管理器(pip、npm、gem、maven),还有各种二进制文件。Syft能一口气把这些全扫出来,直接输出CycloneDX或SPDX格式。
我个人的习惯是,把Syft作为本地开发阶段的快速验证工具。每次构建完镜像,顺手跑一下,看看有没有引入什么奇怪的依赖。嗯,这里要注意,Syft只负责“发现”和“列举”,它不做漏洞匹配。漏洞匹配是另一个工具(比如Grype)的事。但SBOM本身是基础,没有清单,你拿什么去匹配漏洞?
核心定位: Syft = 容器镜像的“物料清单扫描仪”。它把镜像拆开,把里面的每一个软件包、每一个库、每一个文件都登记在册。
4.2 安装Syft
安装方式很多,我挑最常用的几种说。你根据自己环境选一个就行。
4.2.1 macOS / Linux 一键脚本
这是最省事的办法。Syft官方提供了一个安装脚本,一行命令搞定。
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
跑完之后,验证一下:
syft version
如果看到版本号,比如 v1.0.0 之类的,就说明装好了。我曾经在客户的离线环境里折腾过,没法用这个脚本,只能手动下载二进制包。但大多数情况下,这个脚本是最快的。
4.2.2 Docker 方式运行
如果你不想污染宿主机环境,或者你本身就在容器化环境里工作,直接用Docker镜像跑Syft也很方便。
docker run -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:latest your-image:tag
这里有个关键点:-v /var/run/docker.sock:/var/run/docker.sock。Syft需要跟Docker守护进程通信,才能读取你本地的镜像层。不加这个挂载,它没法工作。我第一次用Docker方式跑的时候忘了加,结果报了个“无法访问镜像”的错误,折腾了好一会儿才反应过来。
4.2.3 其他方式
- Homebrew (macOS):
brew install syft - 直接下载二进制: 去GitHub Releases页面,下载对应操作系统和架构的压缩包,解压后放到PATH里。
我的建议: 本地开发用Homebrew或一键脚本,CI/CD流水线里用Docker方式。这样能保证环境隔离,不会因为宿主机上装了乱七八糟的东西影响扫描结果。
4.3 生成第一个SBOM
好,工具装好了。我们来生成第一个SBOM。目标镜像就用最经典的 nginx:latest。
4.3.1 基础命令
syft nginx:latest
就这么简单。Syft会自动拉取镜像(如果本地没有的话),然后开始扫描。你会看到终端里刷刷刷地输出一堆包名和版本号。默认情况下,它输出的是Syft自己的“表格式”视图,方便人眼阅读。
但我们要的是机器可读的SBOM,对吧?所以得指定输出格式。
4.3.2 输出为CycloneDX格式
syft nginx:latest -o cyclonedx-json > nginx-sbom.cyclonedx.json
这条命令做了三件事:
- 扫描
nginx:latest镜像。 - 把结果格式化为CycloneDX的JSON格式(
-o cyclonedx-json)。 - 把输出重定向到文件
nginx-sbom.cyclonedx.json。
打开这个JSON文件,你会看到里面包含了镜像的元数据、组件列表、依赖关系等等。这就是标准的SBOM。
4.3.3 输出为SPDX格式
如果你更习惯用SPDX,也没问题:
syft nginx:latest -o spdx-json > nginx-sbom.spdx.json
我个人更倾向于CycloneDX,因为它的结构更紧凑,而且对容器场景的支持更好。但SPDX在开源社区里也很流行,看团队统一标准吧。
注意: 生成的SBOM文件不要直接提交到公开的Git仓库里。它包含了镜像里所有软件包的精确版本信息,攻击者拿到手就能精准定位你的漏洞面。我见过有团队不小心把SBOM传到GitHub上,结果被安全扫描工具扫出来一堆CVE。SBOM应该像密码一样妥善保管。
4.4 理解输出结果
我们来看看生成的SBOM里到底有什么。以CycloneDX JSON为例,核心结构是这样的:
| 字段 | 说明 | 示例值 |
|---|---|---|
bomFormat |
SBOM格式标准 | CycloneDX |
specVersion |
标准版本号 | 1.5 |
metadata |
镜像元数据(创建时间、工具信息等) | 包含 timestamp、tools 等 |
components |
组件列表(核心!) | 数组,每个元素是一个软件包 |
dependencies |
组件之间的依赖关系 | 数组,描述谁依赖谁 |
其中 components 数组里的每个元素,又包含:
- type: 组件类型,比如
library、operating-system、file。 - name: 包名,比如
libc6、openssl。 - version: 版本号,比如
2.31-13+deb11u6。 - purl: 包URL,这是用来唯一标识一个软件包的标准格式。有了它,漏洞数据库就能精确匹配。
你想想看,如果没有这个清单,你怎么知道你的nginx镜像里用了哪个版本的OpenSSL?出了Heartbleed那样的漏洞,你怎么快速定位哪些镜像受影响?SBOM就是干这个用的。
4.5 知识体系:Syft工作流程
为了让你更直观地理解Syft是怎么工作的,我画了一张流程图。
这张图把整个流程串起来了。从输入一个容器镜像开始,Syft解压、扫描、解析,最后输出一份标准格式的SBOM。说白了,它就是个“翻译官”,把镜像里那些二进制文件翻译成结构化的文本清单。
4.6 避坑指南
最后,分享几个我踩过的坑:
- 镜像拉取失败: 如果你在CI环境里跑Syft,确保Docker daemon在运行,而且有网络权限去拉镜像。我曾经在K8s的Pod里直接跑Syft,结果发现Pod里没有Docker socket,折腾了半天改用
syft dir:./扫描本地目录才搞定。 - 输出文件太大: 一个大型的Java应用镜像,SBOM文件可能达到几十MB。别慌,这是正常的。但如果你要把它存到数据库或者传到API里,记得考虑压缩或者分片。
- 版本锁定: 别用
latest标签。在CI/CD流水线里,一定要锁定Syft的具体版本号,比如anchore/syft:v0.100.0。否则哪天Syft升级了,输出格式变了,你的下游解析脚本就全挂了。我吃过这个亏,那次升级导致整个SBOM解析管道瘫痪了两小时。
快速验证: 如果你只是想看看Syft能不能正常工作,可以扫描一个很小的镜像,比如 alpine:latest。它只有几MB,扫描结果也简单,适合用来测试环境配置。
好了,这一章的内容就到这儿。你现在应该已经装好了Syft,并且成功生成了第一个容器镜像的SBOM。打开那个JSON文件看看吧,里面每一行都是你镜像的“成分表”。