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

这条命令做了三件事:

  1. 扫描 nginx:latest 镜像。
  2. 把结果格式化为CycloneDX的JSON格式(-o cyclonedx-json)。
  3. 把输出重定向到文件 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 流程图 容器镜像 Syft 扫描引擎 解压镜像层 → 识别包管理器 → 提取包信息 解析依赖关系 & 生成组件清单 操作系统包 + 语言包 + 二进制文件 输出格式:CycloneDX JSON / SPDX JSON / 表格 通过 -o 参数指定 SBOM 文件(.json) 输入 本地镜像 / Docker Hub OCI 镜像 / 容器文件系统 输出 标准 SBOM 文件 可被下游工具消费

这张图把整个流程串起来了。从输入一个容器镜像开始,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文件看看吧,里面每一行都是你镜像的“成分表”。