BuildKit SBOM 生成与接入实践:从镜像扫描到 SPDX 证据链
BuildKit SBOM 生成与接入实践从镜像扫描到 SPDX 证据链【免费下载链接】buildkitconcurrent, cache-efficient, and Dockerfile-agnostic builder toolkit项目地址: https://gitcode.com/GitHub_Trending/bu/buildkitSBOMSoftware Bill of Materials软件物料清单用于记录构成最终镜像的软件包及其所属文件是供应链安全与漏洞扫描的基础数据。本文基于 BuildKit 仓库中 docs/attestations/sbom.md 展开讲解如何用attest:sbom选项在构建时自动生成 SBOM、如何通过 Dockerfile 构建参数扩展扫描范围构建上下文与中间阶段、以及 SBOM 以 in-toto attestation SPDX JSON 形式挂载到镜像索引的具体结构与字段含义。读完本文你将掌握从buildctl命令行触发扫描、自定义生成器镜像、解读输出证据到定位 80 MiB 大小限制的完整实战能力。什么是 BuildKit 的 SBOMBuildKit 在构建镜像时自动生成 SBOM记录最终镜像由哪些软件包组成、这些软件包分别拥有哪些文件。除了文件与包清单SBOM 通常还携带每个组件的元数据例如软件许可证、作者以及可用于漏洞扫描的唯一包标识符CPE、PURL 等。在 BuildKit 中所有生成的 SBOM 都被包装在 in-toto attestation 中predicate 采用 SPDX JSON 格式https://spdx.dev/Document。SBOM 的生成工作由遵循 SBOM 生成器协议 的生成器镜像generator image完成当最终导出格式为容器镜像时SBOM 通过 attestation 存储 挂载到镜像索引中。当前受支持的 attestation 类型除 SBOM 外还有 SLSA Provenance二者统一由 docs/attestations/README.md 管理。快速开始构建带 SBOM 的镜像使用内置默认扫描器使用内置默认扫描器即 docker/buildkit-syft-scanner基于 syft 实现时只需要在buildctl build中追加attest:sbom选项值为空字符串即可buildctl build \ --frontenddockerfile.v0 \ --local context. \ --local dockerfile. \ --opt attest:sbom指定自定义 SBOM 生成器镜像如果希望使用自己的扫描器例如内部安全团队维护的扫描镜像通过generator参数指定镜像引用buildctl build \ --frontenddockerfile.v0 \ --local context. \ --local dockerfile. \ --opt attest:sbomgeneratorregistry/image生成器镜像必须遵循 SBOM 生成器协议BuildKit 会把目标文件系统以只读挂载方式传给该镜像由镜像把扫描结果写入指定目录。目前协议只支持 SPDX JSON 格式的 SBOM。生成器镜像的协议约束从源码 frontend/attestations/sbom/sbom.go 可以看到协议在实现层面对生成器镜像的约束CreateSBOMScanner函数扫描器镜像必须带有Entrypoint或Cmd否则会报错scanner %s does not have cmdBuildKit 会向生成器注入以下环境变量BUILDKIT_SCAN_DESTINATION必填扫描结果输出目录镜像内固定为/run/out/扫描器应将 SBOM 写入$BUILDKIT_SCAN_DESTINATION/scan.spdx.jsonBUILDKIT_SCAN_SOURCE必填主扫描目标即最终构建结果根文件系统镜像内固定为/run/src/core/sbom输出文件名为$(basename $BUILDKIT_SCAN_SOURCE).spdx.jsonBUILDKIT_SCAN_SOURCE_EXTRAS可选附加扫描目标构建上下文或其他阶段的根文件系统目录镜像内固定为/run/src/extras/未设置或为空时扫描器不应扫描 extras扫描器不允许在可选参数未设置时报错也不允许为协议指定之外的文件系统产出 SBOM。源码中常量CoreSBOMName sbom、ExtraSBOMPrefix sbom-表明主扫描的挂载点为/run/src/core/sbom每个附加目标以sbom-目标名的形式挂载到/run/src/extras/下最终产物通过llb.AddMount(outDir, llb.Scratch())收集。另外sbom_test.go 中的TestScannerTmpMount验证了扫描器临时目录的挂载方式Linux 平台使用 tmpfsMountType_TMPFSWindows 平台使用 bind 挂载MountType_BIND并设置SkipOutput即扫描器的中间产物不会进入最终镜像。Dockerfile 配置扩展扫描范围默认情况下只有最终构建结果会被扫描。由于多阶段构建中的中间阶段和构建上下文可能安装了构建期依赖例如编译工具链、go mod download的缓存等仅扫描最终阶段会让这些依赖被遗漏进而漏报可能影响最终产物的漏洞。BuildKit 为此提供了两个特殊的构建参数BUILDKIT_SBOM_SCAN_CONTEXT额外扫描构建上下文BUILDKIT_SBOM_SCAN_STAGE额外扫描其他构建阶段。这两个参数是特殊值不会参与变量替换也不能作为 Dockerfile 内的环境变量使用——它们存在的唯一目的就是改变扫描器的行为。参数取值两个参数均可作为全局 meta 参数在第一个FROM之前声明或按阶段单独声明。若在全局声明其值会传播给 Dockerfile 中的每个阶段。可取以下值值含义示例true启用上下文/阶段扫描BUILDKIT_SBOM_SCAN_STAGEtruefalse禁用上下文/阶段扫描BUILDKIT_SBOM_SCAN_STAGEfalsestage-name[,stage-name]仅扫描逗号分隔列表中列出的阶段BUILDKIT_SBOM_SCAN_STAGEx,y表示只扫描名为x和y的阶段需要注意即使通过构建参数启用了扫描从未被实际构建的阶段也永远不会被扫描例如测试用例dockerfile_sbom_test.go中base2阶段因RUN命令故意失败而未产出扫描结果。示例扫描中间构建阶段与构建上下文FROM alpine:latest as build # 为中间构建阶段启用扫描 ARG BUILDKIT_SBOM_SCAN_STAGEtrue WORKDIR /src COPY . . RUN ... # 构建某些软件 FROM scratch as final # 仅当构建完整执行到结束时才扫描构建上下文 ARG BUILDKIT_SBOM_SCAN_CONTEXTtrue COPY --frombuild /path/to/software /path/to/software命令行覆盖也可以在命令行直接覆盖这些ARG无需修改 Dockerfilebuildctl build \ --frontenddockerfile.v0 \ --local context. \ --local dockerfile. \ --opt build-arg:BUILDKIT_SBOM_SCAN_STAGEvalue \ --opt build-arg:BUILDKIT_SBOM_SCAN_CONTEXTvalue \ --opt attest:sbom注意命令行覆盖只会作用于 Dockerfile 中已经显式声明的ARG定义。也就是说如果某阶段没有声明BUILDKIT_SBOM_SCAN系列参数命令行传值不会强制对该阶段启用扫描。集成测试 frontend/dockerfile/dockerfile_sbom_test.go 分别验证了全局/阶段 ARG 开启扫描后产出 1 份 core 3 份 extra attestation与命令行传false关闭后只剩 1 份 core attestation两种行为。底层解析逻辑在 frontend/dockerfile/dockerfile2llb/convert.go 中两个参数被声明为特殊参数并排除在普通变量替换之外sbomScanContext BUILDKIT_SBOM_SCAN_CONTEXT sbomScanStage BUILDKIT_SBOM_SCAN_STAGEdispatch阶段会遍历每个阶段依次检查全局参数d.opt.globalArgs与阶段内声明的构建参数d.buildArgs并通过isEnabledForStage(d.stageName, v)解析true/false/ 阶段名列表三种取值任一来源命中即为该阶段开启scanContext或scanStage。这印证了文档中全局传播、按阶段生效、仅覆盖已声明 ARG的行为描述。输出如何查看与解读 SBOM构建完成后可以通过docker buildx imagetools探索 registry 中的镜像按 attestation 存储 描述的格式查看挂载的 SBOM 证据镜像索引中platform为unknown/unknown的 manifest 即 attestation manifest其in-toto.io/predicate-type注解为https://spdx.dev/Document。以下是一个基于alpine:latest的简单镜像生成的 SBOM 示例具体内容取决于生成器此处展示典型结构{ _type: https://in-toto.io/Statement/v1, predicateType: https://spdx.dev/Document, subject: [ { name: pkg:docker/registry/imagetag/digest?platformplatform, digest: { sha256: e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862 } } ], predicate: { SPDXID: SPDXRef-DOCUMENT, name: /run/src/core, spdxVersion: SPDX-2.2, creationInfo: { created: 2022-11-09T10:12:01.338817553Z, creators: [ Organization: Anchore, Inc, Tool: syft-[not provided] ], licenseListVersion: 3.18 }, dataLicense: CC0-1.0, documentNamespace: https://anchore.com/syft/dir/run/src/core-4006bb64-24b1-4a22-a18f-94efc6b90edb, files: [ { SPDXID: SPDXRef-1ac501c94e2f9f81, comment: layerID: sha256:9b18e9b68314027565b90ff6189d65942c0f7986da80df008b8431276885218e, fileName: /bin/busybox, licenseConcluded: NOASSERTION }, ... ], packages: [ { SPDXID: SPDXRef-980737451f148c56, description: Size optimized toolbox of many common UNIX utilities, downloadLocation: https://busybox.net/, externalRefs: [ { referenceCategory: SECURITY, referenceLocator: cpe:2.3:a:busybox:busybox:1.35.0-r17:*:*:*:*:*:*:*, referenceType: cpe23Type }, { referenceCategory: PACKAGE_MANAGER, referenceLocator: pkg:alpine/busybox1.35.0-r17?archaarch64upstreambusyboxdistroalpine-3.16.2, referenceType: purl } ], filesAnalyzed: false, hasFiles: [ SPDXRef-1ac501c94e2f9f81, ... ], licenseConcluded: GPL-2.0-only, licenseDeclared: GPL-2.0-only, name: busybox, originator: Person: Sören Tempel soerenalpinesoeren-tempel.net, sourceInfo: acquired package info from APK DB: lib/apk/db/installed, versionInfo: 1.35.0-r17 }, ... ], relationships: [ { relatedSpdxElement: SPDXRef-1ac501c94e2f9f81, relationshipType: CONTAINS, spdxElementId: SPDXRef-980737451f148c56 }, ... ] } }输出字段解读具体输出取决于生成器但通常遵循以下规律files键镜像中所有文件的列表packages键从镜像中发现的所有软件包列表relationships键把文件与包关联起来并描述它们之间的关系例如示例中的CONTAINS关系表示包busybox包含文件/bin/busyboxfiles与packages中的条目若带有comment字段则该字段包含引入该条目的镜像层的sha256digest前提是该层存在于最终镜像中——通过这个字段可以把 SBOM 中的每个包/文件精确回溯到具体镜像层方便定位哪个 RUN 命令引入了哪个依赖。从 in-toto 层面看外层subject的name形如pkg:docker/registry/imagetag/digest?platformplatformdigest指向被证明的目标镜像 manifest与 attestation-storage.md 中attestation 的 subject 应设置为与目标 manifest 相同 digest的约定一致。80 MiB 大小限制生成后的 SBOM attestation 文件在挂载到导出镜像前会被限制为80 MiB。SPDX JSON 文档包含详细的文件、包和关系元数据大型镜像的 SBOM 可能逼近甚至超过该上限。这一限制的实现位于 exporter/attestation/make.go 的常量maxAttestationBytes int64 80 20即 80 × 1024 × 1024 字节用于防止导出器对前端提供的 attestation 文件进行无界读取。如果你的镜像包体量很大需要留意扫描产物是否触及该上限。在 OCI 镜像索引中的存储形态虽然 SBOM 的生成入口是attest:sbom但其落盘方式是理解整条证据链的关键。按 attestation-storage.md 的说明SBOM 会以 OCI artifact 的形式存储可设置镜像导出选项oci-artifactfalse回退到传统格式根镜像索引的manifests中在所有可运行 manifest 之后追加 attestation manifest其platform固定为unknown/unknown防止容器运行时误拉取或误运行attestation manifest 的artifactType为application/vnd.docker.attestation.manifest.v1jsonsubject指向目标镜像 manifestlayers中每个层是一份 attestation blobmediaType为application/vnd.in-totojson并带in-toto.io/predicate-type注解以便遍历时按 predicate 类型快速筛选镜像索引的 manifest descriptor 上带有vnd.docker.reference.typeattestation-manifest与vnd.docker.reference.digest注解用于把 attestation manifest 与其所证明的镜像 manifest 关联起来。这一存储格式对docker buildx imagetools、漏洞扫描器等消费方是公开契约SBOM 可以只按需拉取利用 predicate-type 注解跳过无关 attestation而无需拉取镜像全部内容。小结BuildKit 的 SBOM 能力可以概括为一条闭环链路attest:sbom选项指定生成器 → 生成器按协议扫描最终 rootfs 与可选的上下文/阶段 → 产物以 SPDX JSON 写入并在导出时被包装为 in-toto attestation → 以 OCI artifact 形式挂载到镜像索引 → 消费方通过 predicate-type 注解按需读取。配合BUILDKIT_SBOM_SCAN_CONTEXT/BUILDKIT_SBOM_SCAN_STAGE构建参数可以决定扫描边界避免漏掉构建期依赖引入的漏洞。相关实现与测试可直接在仓库内查看SBOM 扫描协议、attestation 存储格式、扫描器调用实现、Dockerfile 参数解析、端到端集成测试 与 大小限制实现。【免费下载链接】buildkitconcurrent, cache-efficient, and Dockerfile-agnostic builder toolkit项目地址: https://gitcode.com/GitHub_Trending/bu/buildkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →