SBOM 證明
SBOM 證明有助於透過驗證映像檔內含的軟體組件以及用於建立該映像檔的組件,來確保軟體供應鏈透明度。包含在 SBOM 中用於描述軟體組件的中繼資料可能包括:
- 組件名稱
- 版本
- 授權類型
- 作者
- 唯一套件識別碼
在建置期間編制映像檔內容索引,比起掃描最終映像檔具有更多優勢。當掃描作為建置的一部分進行時,您可以偵測到用於建置映像檔的軟體,這些軟體可能不會顯示在最終映像檔中。
Docker 透過使用 BuildKit 和證明的符合 SLSA 標準的建置流程,支援 SBOM 的產生與證明。由 BuildKit 產生的 SBOM 遵循 SPDX 標準,並使用 in-toto SPDX 述詞 (predicate) 所定義的格式,以 JSON 編碼的 SPDX 文件形式附加到最終映像檔上。在本頁中,您將學習如何使用 Docker 工具建立、管理及驗證 SBOM 證明。
建立 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 證明
在將映像檔推送到登錄檔 (registry) 之前,請務必驗證為您的映像檔產生的 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 不會包含在早期階段安裝或存在於建置環境中的建置時依賴項。這可能會導致您忽略這些依賴項中的漏洞,進而影響最終建置產出的安全性。
例如,您可能會使用多階段建置 (multi-stage builds),並在最後階段使用 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 指令。
掃描建置環境 (Build context)
若要掃描建置環境,請將 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 .
請注意,僅將選項作為 CLI 參數傳遞,而未在 Dockerfile 中使用 ARG 宣告它,將不會有任何效果。您必須在 Dockerfile 中指定 ARG,這樣才能使用 --build-arg 覆寫環境掃描行為。
掃描階段 (Stages)
若要掃描而不僅僅是最終階段,請將 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
檢查 SBOM
若要探索透過 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 使用掃描器外掛程式 (scanner plugin) 來產生 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"
}
}