3、SBOM实战:在CI/CD中集成SBOM生成、SBOM版本管理

说实话,很多团队做供应链安全,第一步就卡在「我们连自己用了哪些组件都不清楚」。SBOM(软件物料清单)就是解决这个问题的。但光生成一份SBOM没用,你得把它嵌入到开发流程里,让它跟着版本走,每次构建都自动更新。

这一章,我就带你走一遍实战流程。从工具选型到CI/CD集成,再到版本管理策略,咱们一步到位。

3.1 选对工具:SBOM生成器怎么挑?

市面上SBOM生成工具不少,我挑几个主流的说说我的使用感受。

工具 支持格式 语言覆盖 我的评价
Syft CycloneDX, SPDX Java, Python, Go, JS, Rust 等 速度快,社区活跃,我主力用这个
Trivy CycloneDX, SPDX 同上,还能扫容器镜像 SBOM + 漏洞扫描二合一,省事
CycloneDX CLI CycloneDX 通过插件扩展 官方出品,格式最标准
Microsoft SBOM Tool SPDX .NET, JS, 其他 微软生态友好,但社区较小

我个人习惯用 Syft。为什么?因为它够轻量,生成一份Java项目的SBOM只需要几秒钟。而且它输出的是标准CycloneDX格式,后续对接漏洞库很方便。

我的建议:别纠结工具,选一个你团队能快速上手的。关键是「用起来」,而不是「选最好的」。我见过太多团队在工具选型上花了两周,结果一单都没生成过。

3.2 在CI/CD里集成SBOM生成

好了,工具选好了。下一步就是把它塞进你的CI/CD流水线。我以GitHub Actions为例,给你看看我是怎么做的。

核心思路很简单:每次构建成功后,自动生成一份SBOM,然后把它作为构建产物保存下来。

3.2.1 一个可用的GitHub Actions示例

name: Generate SBOM

on:
  push:
    branches: [main, release/*]
  pull_request:
    branches: [main]

jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate SBOM with Syft
        uses: anchore/sbom-action@v0
        with:
          path: ./
          format: cyclonedx-json
          output-file: sbom.cdx.json

      - name: Upload SBOM as artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom-${{ github.sha }}
          path: sbom.cdx.json

这段代码干了三件事:

  1. 检出代码
  2. 用Syft扫描当前目录,生成CycloneDX格式的SBOM
  3. 把SBOM文件上传为构建产物,文件名带上commit SHA

你看,就这么简单。但这里有个坑——我建议你只在主干分支和release分支上生成SBOM。每个PR都生成的话,你的artifact存储会爆炸。我之前在一个大项目里这么干过,一个月下来存储费用涨了三倍……后来改成只在合并到main时生成,问题就解决了。

注意:SBOM文件不要放在代码仓库里!每次提交都更新SBOM,会让你的git历史变得臃肿。正确的做法是作为构建产物,关联到具体的构建版本。

3.3 SBOM版本管理:别让SBOM变成一堆乱文件

SBOM生成出来了,但怎么管理?总不能每个版本都手动下载吧?

我见过最糟糕的做法:把SBOM文件扔到一个共享文件夹里,文件名写「sbom_final_v3_真的最终版.json」。嗯,你懂的,这迟早会乱套。

正确的做法是:让SBOM和你的软件版本一一对应。

3.3.1 版本关联策略

软件版本 SBOM标识 存储位置
v1.2.3 sbom-v1.2.3.cdx.json Release Artifacts
v1.2.4-rc.1 sbom-v1.2.4-rc.1.cdx.json Release Artifacts
main分支最新 sbom-{commit-sha}.cdx.json CI Artifacts(保留30天)

说白了,就是用版本号或commit SHA作为SBOM的文件名。这样你拿到一个软件版本,就能立刻找到它对应的SBOM。

3.3.2 集中存储与检索

如果项目多了,光靠CI的artifact存储就不够了。我建议搭建一个SBOM集中管理平台。你可以用现成的,比如:

  • Dependency-Track:开源,支持SBOM上传、漏洞分析、策略管理
  • GitHub Dependency Graph:如果你用GitHub,它能自动识别SBOM
  • 自建对象存储:比如MinIO,按版本目录存放

我个人推荐Dependency-Track。它有一个API,你可以在CI/CD里直接上传SBOM:

curl -X POST "https://dt.example.com/api/v1/bom" \
  -H "X-Api-Key: $DEPENDENCY_TRACK_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F "project=my-app" \
  -F "version=v1.2.3" \
  -F "bom=@sbom.cdx.json"

这样,每次构建完成后,SBOM自动上传到Dependency-Track。你可以在上面查看每个版本的组件清单、漏洞信息,甚至设置策略——比如「不允许使用GPL协议的组件」。

核心原则:SBOM不是生成一次就完事了。它应该像你的源代码一样,有版本、有历史、可追溯。每次软件发布,对应的SBOM必须归档,并且能快速检索到。

3.4 一张图看懂SBOM在CI/CD中的流转

说了这么多,我画张图帮你理清整个流程。

SBOM在CI/CD中的集成与流转 代码提交 CI流水线触发 构建 & 测试 Syft 生成 SBOM 是Release? 上传到Release Artifacts CI Artifacts短期存储 Dependency-Track 集中管理 漏洞扫描 策略检查

这张图展示了SBOM从生成到归档的完整路径。核心就两点:生成自动化和版本关联。做到这两点,你的SBOM管理就基本合格了。

3.5 避坑指南:我踩过的几个坑

最后,分享几个我实际项目中遇到的坑,你遇到了可以少走弯路。

  • SBOM文件太大? 一个大型Java项目,SBOM可能有几千行。别用默认的XML格式,换成JSON或CycloneDX的压缩格式,文件体积能减少70%。
  • 生成时间太长? Syft默认会扫描所有文件。如果你项目里有node_modules或vendor目录,记得加排除规则。我一般只扫描编译后的产物,比如JAR包或Docker镜像。
  • 版本号对不上? 我遇到过最头疼的问题:SBOM里记录的组件版本和实际运行的不一致。后来发现是构建缓存搞的鬼。解决办法:每次生成SBOM前,强制清理缓存,或者基于构建产物(比如Docker镜像)来生成。
曾经我犯过一个错:把SBOM生成放在了代码编译之前。结果SBOM里记录的依赖版本是pom.xml里的,但实际编译时因为依赖冲突,用了另一个版本。从那以后,我坚持基于构建产物生成SBOM,而不是基于源码。

好了,这一章的内容就这些。SBOM集成到CI/CD,说白了就是「自动化生成 + 版本关联 + 集中管理」。你把这三点做到位,供应链安全的第一步就站稳了。