SBOM 证明
软件物料清单(SBOM)声明描述了镜像包含的软件制品,以及用于创建镜像的制品。SBOM 中用于描述软件制品的元数据可能包括:
- 构件的名称
- 版本
- 许可证类型
- 作者
- 唯一包标识符
在构建过程中对镜像内容进行索引,而不是扫描最终镜像,有其优势。当扫描作为构建的一部分进行时,您可以检测到用于构建镜像的软件,这些软件可能不会出现在最终镜像中。
由 BuildKit 生成的软件物料清单(SBOM)遵循 SPDX 标准。SBOM 以 JSON 编码的 SPDX 文档形式附加到最终镜像中,使用由 in-toto SPDX predicate定义的格式。
创建SBOM证明
要创建SBOM证明,请将--attest type=sbom选项传递给docker buildx build命令:
$ docker buildx build --tag <namespace>/<image>:<version> \
--attest type=sbom --push .
或者,您可以使用简写 --sbom=true 选项,而不是 --attest type=sbom。
有关如何使用 GitHub Actions 添加 SBOM 凭证的示例,请参阅 使用 GitHub Actions 添加凭证。
验证SBOM证明
在将镜像推送到注册表之前,始终验证为您的镜像生成的SBOM。
为了验证,您可以使用 local 导出器构建镜像。
使用 local 导出器构建会将构建结果保存到本地文件系统,而不是创建镜像。
证明会被写入根目录下的一个 JSON 文件中。
$ docker buildx build \
--sbom=true \
--output type=local,dest=out .
SBOM 文件出现在输出的根目录中,命名为 sbom.spdx.json:
$ ls -1 ./out | grep sbom
sbom.spdx.json
参数
默认情况下,BuildKit 仅扫描镜像的最终阶段。生成的 SBOM 不包含在早期阶段安装的构建时依赖项,或存在于构建上下文中的依赖项。这可能会导致您忽视这些依赖项中的漏洞,从而影响最终构建工件的安全性。
例如,您可以使用
多阶段构建,
在最终阶段使用 FROM scratch 语句来实现更小的镜像大小。
FROM alpine AS build
# build the software ...
FROM scratch
COPY --from=build /path/to/bin /bin
ENTRYPOINT [ "/bin" ]使用此 Dockerfile 示例构建的镜像进行扫描时,不会揭示在 build 阶段中使用的构建时依赖项。
要从 Dockerfile 中包含构建时依赖项,可以设置构建参数 BUILDKIT_SBOM_SCAN_CONTEXT 和 BUILDKIT_SBOM_SCAN_STAGE。这会将扫描范围扩展到包括构建上下文和额外阶段。
您可以将参数设置为全局参数(在声明 Dockerfile 语法指令后,第一个 FROM 命令之前)或在每个阶段单独设置。如果全局设置,该值将传播到 Dockerfile 中的每个阶段。
BUILDKIT_SBOM_SCAN_CONTEXT 和 BUILDKIT_SBOM_SCAN_STAGE 构建参数
是特殊值。你不能使用这些参数进行变量替换,也不能在Dockerfile中使用环境变量来设置它们。设置这些值的唯一方法是在Dockerfile中使用显式的 ARG 命令。
扫描构建上下文
要扫描构建上下文,请将 BUILDKIT_SBOM_SCAN_CONTEXT 设置为 true。
# syntax=docker/dockerfile:1
ARG BUILDKIT_SBOM_SCAN_CONTEXT=true
FROM alpine AS build
# ...您可以使用 --build-arg CLI 选项来覆盖 Dockerfile 中指定的值。
$ docker buildx build --tag <image>:<version> \
--attest type=sbom \
--build-arg BUILDKIT_SBOM_SCAN_CONTEXT=false .
请注意,仅将该选项作为命令行参数传递,而未在 Dockerfile 中使用 ARG 声明,将不会产生任何效果。您必须在 Dockerfile 中指定 ARG,从而可以使用 --build-arg 覆盖上下文扫描行为。
扫描阶段
要扫描最终阶段之外的内容,请将BUILDKIT_SBOM_SCAN_STAGE
参数设置为 true,可以在全局或在您要扫描的特定阶段中设置。下表展示了此参数的不同可能设置。
| 值 | 描述 |
|---|---|
BUILDKIT_SBOM_SCAN_STAGE=true | 启用当前阶段的扫描 |
BUILDKIT_SBOM_SCAN_STAGE=false | 禁用当前阶段的扫描 |
BUILDKIT_SBOM_SCAN_STAGE=base,bin | 启用对名为 base 和 bin 的阶段进行扫描 |
仅构建的阶段将被扫描。不是目标阶段依赖项的阶段不会被构建或扫描。
以下 Dockerfile 示例使用多阶段构建,通过 Hugo 构建静态网站。
# syntax=docker/dockerfile:1
FROM alpine as hugo
ARG BUILDKIT_SBOM_SCAN_STAGE=true
WORKDIR /src
COPY <<config.yml ./
title: My Hugo website
config.yml
RUN apk add --upgrade hugo && hugo
FROM scratch
COPY --from=hugo /src/public /在 hugo 阶段设置 ARG BUILDKIT_SBOM_SCAN_STAGE=true 可确保最终的 SBOM 包含使用 Alpine Linux 和 Hugo 创建网站的信息。
使用 local 导出器构建此镜像会创建两个 JSON 文件:
$ docker buildx build \
--sbom=true \
--output type=local,dest=out .
$ ls -1 out | grep sbom
sbom-hugo.spdx.json
sbom.spdx.json
检查SBOMs
要探索通过image导出器导出的已创建的SBOM,您可以使用
imagetools inspect。
使用 --format 选项,您可以为输出指定一个模板。所有与 SBOM 相关的数据都位于 .SBOM 属性下。例如,要获取 SPDX 格式的 SBOM 原始内容:
$ docker buildx imagetools inspect <namespace>/<image>:<version> \
--format "{{ json .SBOM.SPDX }}"
{
"SPDXID": "SPDXRef-DOCUMENT",
...
}
提示
如果镜像是多平台的,您可以使用
--format '{{ json (index .SBOM "linux/amd64").SPDX }}'检查特定平台的 SBOM 索引。
你还可以使用 Go 模板的全部功能构建更复杂的表达式。例如,你可以列出所有已安装的软件包及其版本标识符:
$ docker buildx imagetools inspect <namespace>/<image>:<version> \
--format "{{ range .SBOM.SPDX.packages }}{{ .name }}@{{ .versionInfo }}{{ println }}{{ end }}"
adduser@3.118ubuntu2
apt@2.0.9
base-files@11ubuntu5.6
base-passwd@3.5.47
...
SBOM生成器
BuildKit 使用扫描器插件生成 SBOM。默认情况下,它使用的是 BuildKit Syft 扫描器 插件。该插件基于 Anchore 的 Syft, 这是一个用于生成 SBOM 的开源工具。
您可以使用 generator 选项选择不同的插件,指定一个实现
BuildKit SBOM 扫描器协议 的镜像。
$ docker buildx build --attest type=sbom,generator=<image> .
提示
Docker Scout SBOM 生成器现已可用。参见 Docker Scout SBOMs。
SBOM 证明示例
以下 JSON 示例展示了 SBOM 证明可能的外观。
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://spdx.dev/Document",
"subject": [
{
"name": "pkg:docker/<registry>/<image>@<tag/digest>?platform=<platform>",
"digest": {
"sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862"
}
}
],
"predicate": {
"SPDXID": "SPDXRef-DOCUMENT",
"creationInfo": {
"created": "2022-12-16T15:27:25.517047753Z",
"creators": ["Organization: Anchore, Inc", "Tool: syft-v0.60.3"],
"licenseListVersion": "3.18"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/dir/run/src/core/sbom-cba61a72-fa95-4b60-b63f-03169eac25ca",
"name": "/run/src/core/sbom",
"packages": [
{
"SPDXID": "SPDXRef-b074348b8f56ea64",
"downloadLocation": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceLocator": "cpe:2.3:a:org:repo:\\(devel\\):*:*:*:*:*:*:*",
"referenceType": "cpe23Type"
},
{
"referenceCategory": "PACKAGE_MANAGER",
"referenceLocator": "pkg:golang/github.com/org/repo@(devel)",
"referenceType": "purl"
}
],
"filesAnalyzed": false,
"licenseConcluded": "NONE",
"licenseDeclared": "NONE",
"name": "github.com/org/repo",
"sourceInfo": "acquired package info from go module information: bin/server",
"versionInfo": "(devel)"
},
{
"SPDXID": "SPDXRef-1b96f57f8fed62d8",
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "0c13f1f3c1636491f716c2027c301f21f9dbed7c4a2185461ba94e3e58443408"
}
],
"downloadLocation": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceLocator": "cpe:2.3:a:go-chi:chi\\/v5:v5.0.0:*:*:*:*:*:*:*",
"referenceType": "cpe23Type"
},
{
"referenceCategory": "SECURITY",
"referenceLocator": "cpe:2.3:a:go_chi:chi\\/v5:v5.0.0:*:*:*:*:*:*:*",
"referenceType": "cpe23Type"
},
{
"referenceCategory": "SECURITY",
"referenceLocator": "cpe:2.3:a:go:chi\\/v5:v5.0.0:*:*:*:*:*:*:*",
"referenceType": "cpe23Type"
},
{
"referenceCategory": "PACKAGE_MANAGER",
"referenceLocator": "pkg:golang/github.com/go-chi/chi/v5@v5.0.0",
"referenceType": "purl"
}
],
"filesAnalyzed": false,
"licenseConcluded": "NONE",
"licenseDeclared": "NONE",
"name": "github.com/go-chi/chi/v5",
"sourceInfo": "acquired package info from go module information: bin/server",
"versionInfo": "v5.0.0"
}
],
"relationships": [
{
"relatedSpdxElement": "SPDXRef-1b96f57f8fed62d8",
"relationshipType": "CONTAINS",
"spdxElementId": "SPDXRef-043f7360d3c66bc31ba45388f16423aa58693289126421b71d884145f8837fe1"
},
{
"relatedSpdxElement": "SPDXRef-b074348b8f56ea64",
"relationshipType": "CONTAINS",
"spdxElementId": "SPDXRef-043f7360d3c66bc31ba45388f16423aa58693289126421b71d884145f8837fe1"
}
],
"spdxVersion": "SPDX-2.2"
}
}