はじめに
Yocto ProjectではSBOM(Software Bill Of Materials)を出力する機能がある。SBOMはcreate-spdx.bbclassの機能によって出力される。
また、Nanbield(4.3)からこの機能がデフォルトで有効化されている。
今回はScarthgap(5.0)でこの機能を試してみた。
SBOMが何かは日本語の情報だと下記が詳しい。
create-spdx.bbclassesの機能で出力されるSBOMの形式はSPDXとなっている。 SPDXはYocto Projectと同じLinux Foundation参加のプロジェクトで策定されている。
SPDXについては下記が詳しい。
環境構築
まずは環境を構築する。
作業環境
$ mkdir -p ~/yocto/scarthgap $ cd ~/yocto/scarthgap
pokyの取得
$ git clone git://git.yoctoproject.org/poky.git -b scarthgap
環境変数の設定
$ source poky/oe-init-build-env
local.confを編集
local.confに下記を追加する。
MACHINE ?= "qemuarm64" INIT_MANAGER ?= "systemd" QB_OPT_APPEND:append = " -echr 0x14" SPDX_PRETTY = "1"
ビルド
今回はターゲットの機能は使わないのでcore-image-minimalをビルドする。
$ bitbake core-image-minimal -k
これでSBOMが出力されているはず。
Yocto ProjectのSBOMの機能
37 Creating a Software Bill of Materialsに機能の詳細が記述されている。
まず、SBOMは下記の場所に生成されているとのこと。
- SPDX output in JSON format as an IMAGE-MACHINE.spdx.json file in tmp/deploy/images/MACHINE/ inside the Build Directory.
今回の環境では「build/tmp/deploy/images/qemuarm64」になるので確認する。
$ cd tmp/deploy/images/qemuarm64 $ ls -lha *.spdx.json ls: '*.spdx.json' にアクセスできません: そのようなファイルやディレクトリはありません
見つからない。
わかりにくいが実はこのような修正が行われている。
- create-spdx: remove the top-level image SPDX file and the JSON index file from DEPLOYDIR to avoid confusion
DEPLOYDIRは https://docs.yoctoproject.org/singleindex.html#term-DEPLOYDIR によると、「DEPLOYDIR = "${WORKDIR}/deploy-${PN}"」となるらしいが、さらに下記のような記述がある。
Recipes inheriting the deploy class should copy files to be deployed into DEPLOYDIR, and the class will take care of copying them into DEPLOY_DIR_IMAGE afterwards.
DEPLOY_DIR_IMAGEは「${DEPLOY_DIR}/images/${MACHINE}/」となっており、DEPLOY_DIRは「${TMPDIR}/deploy.」となっているため、最終的には「build/tmp/deploy/images/qemuarm64」と一致する。
つまりは「build/tmp/deploy/images/qemuarm64」には「IMAGE-MACHINE.spdx.json」は作成されないということが正しい動作であるらしいということになる。
では実際のSBOMはどこにあるかというと、「core-image-minimal-qemuarm64.rootfs.spdx.tar.zst」となる。正確にはこれはシンボリックリンクなのでその指し先がSBOM本体。
tar.zstは下記のコマンドで伸長できる。
$ tar -I zstd -xf HOGE.tar.zst
次のようにして実際に伸張してみる。
$ cd ~/yocto/scarthgap/build/tmp/deploy/images/qemuarm64 $ mkdir sbom $ cd sbom $ tar -I zstd -v -xf ../core-image-minimal-qemuarm64.rootfs.spdx.tar.zst
SBOMの内容
SPDX_PRETTY
local.confでSPDX_PRETTY = "1"を設定している。これを設定しないと下記のような感じになる。
{"SPDXID": "SPDXRef-DOCUMENT", "creationInfo": {"comment": "This document was created by analyzing packages created during the build.", "created": "2025-01-25T00:35:11Z", "creators": ["Tool: OpenEmbedded Core create-spdx.bbclass", "Organization: OpenEmbedded ()", "Person: N/A ()"], "licenseListVersion": "3.14"}, "dataLicense": "CC0-1.0", "documentNamespace": "http://spdx.org/spdxdocs/base-files-dev-a752bc7c-388a-5a14-984a-72e888717fd3", "externalDocumentRefs": [{"checksum": {"algorithm": "SHA1", "checksumValue": "6dd33163fd47c2258769c8c955fe324cc3293273"}, "externalDocumentId": "DocumentRef-recipe-base-files", "spdxDocument": "http://spdx.org/spdxdocs/recipe-base-files-c146050a-959a-5836-966f-98e79d6e765f"}], "name": "base-files-dev", "packages": [{"SPDXID": "SPDXRef-Package-base-files-dev", "copyrightText": "NOASSERTION", "downloadLocation": "NOASSERTION", "licenseConcluded": "NOASSERTION", "licenseDeclared": "GPL-2.0-only", "licenseInfoFromFiles": ["NOASSERTION"], "name": "base-files-dev", "packageVerificationCode": {"packageVerificationCodeValue": "da39a3ee5e6b4b0d3255bfef95601890afd80709"}, "supplier": "Organization: OpenEmbedded ()", "versionInfo": "3.0.14"}], "relationships": [{"relatedSpdxElement": "DocumentRef-recipe-base-files:SPDXRef-Recipe-base-files", "relationshipType": "GENERATED_FROM", "spdxElementId": "SPDXRef-Package-base-files-dev"}, {"relatedSpdxElement": "SPDXRef-Package-base-files-dev", "relationshipType": "DESCRIBES", "spdxElementId": "SPDXRef-DOCUMENT"}], "spdxVersion": "SPDX-2.2"}
設定すると下記のように改行とインデントが挿入され、人間が読みやすく(Human-readable)なる。
{ "SPDXID": "SPDXRef-DOCUMENT", "creationInfo": { "comment": "This document was created by analyzing packages created during the build.", "created": "2025-01-25T00:48:46Z", "creators": [ "Tool: OpenEmbedded Core create-spdx.bbclass", "Organization: OpenEmbedded ()", "Person: N/A ()" ], "licenseListVersion": "3.14" }, ... (snip) ... "spdxVersion": "SPDX-2.2" }
出力されるデータの内容はこの設定によって変化することはない。
データの分類
core-image-minimal-qemuarm64.rootfs.spdx.tar.zstに含まれているSBOMについては下記のように分類することができる
| 分類 | ファイル名 | 概要 |
|---|---|---|
| イメージ | <イメージ名>.spdx.json | イメージ及び含まれているすべてのパッケージの情報 |
| インデックス | index.json | 出力されたすべてのspdxファイルの一覧 |
| レシピ | recipe-<レシピ名>.spdx.json | ソース及びビルド時依存関係の情報 |
| パッケージ | <パッケージ名>.spdx.json | パッケージ及び含まれているファイルの情報 |
| ランタイム | runtime-<パッケージ名>.spdx.json | 実行時依存関係の情報 |
イメージ
今回はcore-image-minimal-qemuarm64.rootfs-20250124123426.spdx.jsonが生成された。
{ "SPDXID": "SPDXRef-DOCUMENT", "creationInfo": { "comment": "This document was created by analyzing the source of the Yocto recipe during the build.", "created": "2025-01-24T12:42:53Z", "creators": [ "Tool: OpenEmbedded Core create-spdx.bbclass", "Organization: OpenEmbedded ()", "Person: N/A ()" ], "licenseListVersion": "3.14" }, "dataLicense": "CC0-1.0", "documentNamespace": "http://spdx.org/spdxdocs/core-image-minimal-qemuarm64.rootfs-20250124123426-65884203-16cb-5c76-b945-338caac15f74", "externalDocumentRefs": [ { "checksum": { "algorithm": "SHA1", "checksumValue": "441afea16d04b80a127fcaad0ce335c3608a97b7" }, "externalDocumentId": "DocumentRef-base-files", "spdxDocument": "http://spdx.org/spdxdocs/base-files-ee9424e3-1d7e-5739-b9cd-237a1a6f843f" }, ...(snip)... ], "packages": [ { "SPDXID": "SPDXRef-Image-core-image-minimal-qemuarm64.rootfs-20250124123426", "copyrightText": "NOASSERTION", "downloadLocation": "NOASSERTION", "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "licenseInfoFromFiles": [ "NOASSERTION" ], "name": "core-image-minimal", "supplier": "Organization: OpenEmbedded ()", "versionInfo": "1.0" } ], "relationships": [ { "relatedSpdxElement": "DocumentRef-base-files:SPDXRef-Package-base-files", "relationshipType": "CONTAINS", "spdxElementId": "SPDXRef-Image-core-image-minimal-qemuarm64.rootfs-20250124123426" }, ... (snip) ... ], "spdxVersion": "SPDX-2.2" }
大きく下記のブロックに分けることができる。
- creationInfo: SPDXファイル自体の情報
- externalDocumentRefs: 外部のSPDXファイルへの参照
- イメージに含まれるパッケージ
- packages: イメージの情報
- relationship: 各エレメントとの関係(CONTAINS,OTHER...)
インデックス
index.jsonが生成された。アーカイブに含まれているすべてのSPDXファイルの情報が記述されている。
{ "documents": [ { "documentNamespace": "http://spdx.org/spdxdocs/base-files-ee9424e3-1d7e-5739-b9cd-237a1a6f843f", "filename": "base-files.spdx.json", "sha1": "441afea16d04b80a127fcaad0ce335c3608a97b7" }, { "documentNamespace": "http://spdx.org/spdxdocs/base-passwd-78e3ab0a-4686-572d-a886-5105dd9b12de", "filename": "base-passwd.spdx.json", "sha1": "ff03da0613185fc706748144ea96acb018765fc8" }, { "documentNamespace": "http://spdx.org/spdxdocs/busybox-syslog-9c50eff5-a9f9-5765-b33a-312c76b55eb2", "filename": "busybox-syslog.spdx.json", "sha1": "c6c338cafce778f362a45b1c6af31e4f4bcb93a8" }, ... (snip) ...
レシピ
recipe-update-rc.d.spdx.jsonを例示する。
{ "SPDXID": "SPDXRef-DOCUMENT", "creationInfo": { "comment": "This document was created by analyzing recipe files during the build.", "created": "2025-01-24T12:34:48Z", "creators": [ "Tool: OpenEmbedded Core create-spdx.bbclass", "Organization: OpenEmbedded ()", "Person: N/A ()" ], "licenseListVersion": "3.14" }, "dataLicense": "CC0-1.0", "documentNamespace": "http://spdx.org/spdxdocs/recipe-update-rc.d-87cf57af-6fc6-500b-8aa5-8b53c9c11554", "name": "recipe-update-rc.d", "packages": [ { "SPDXID": "SPDXRef-Recipe-update-rc.d", "copyrightText": "NOASSERTION", "description": "update-rc.d is a utility that allows the management of symlinks to the initscripts in the /etc/rcN.d directory structure.", "downloadLocation": "NOASSERTION", "externalRefs": [ { "referenceCategory": "SECURITY", "referenceLocator": "cpe:2.3:*:*:update-rc.d:0.8:*:*:*:*:*:*:*", "referenceType": "http://spdx.org/rdf/references/cpe23Type" } ], "homepage": "http://github.com/philb/update-rc.d/", "licenseConcluded": "NOASSERTION", "licenseDeclared": "GPL-2.0-or-later", "licenseInfoFromFiles": [ "NOASSERTION" ], "name": "update-rc.d", "summary": "manage symlinks in /etc/rcN.d", "supplier": "Organization: OpenEmbedded ()", "versionInfo": "0.8+git" }, { "SPDXID": "SPDXRef-Download-update-rc.d-1", "copyrightText": "NOASSERTION", "downloadLocation": "git+https://git.yoctoproject.org/update-rc.d@b8f950105010270a768aa12245d6abf166346015", "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "licenseInfoFromFiles": [ "NOASSERTION" ], "name": "update-rc.d-source-1", "supplier": "NOASSERTION" } ], "relationships": [ { "relatedSpdxElement": "SPDXRef-Recipe-update-rc.d", "relationshipType": "DESCRIBES", "spdxElementId": "SPDXRef-DOCUMENT" }, { "relatedSpdxElement": "SPDXRef-Download-update-rc.d-1", "relationshipType": "DESCRIBES", "spdxElementId": "SPDXRef-DOCUMENT" }, { "relatedSpdxElement": "SPDXRef-Recipe-update-rc.d", "relationshipType": "BUILD_DEPENDENCY_OF", "spdxElementId": "SPDXRef-Download-update-rc.d-1" } ], "spdxVersion": "SPDX-2.2" }
大きく下記のブロックに分けることができる。
- creationInfo: SPDXファイル自体の情報
- externalDocumentRefs: 外部のSBOMへの参照
- ビルド時依存関係のあるレシピ
- packages: レシピの情報(ソースコードの場所やライセンス、セキュリティFixの情報など)
- relationship: 各エレメントとの関係(BUILD_DEPENDENCY_OF, DESCRIBES...)
CVE対応状況などはここに含まれる。下記はrecipes-openssl.spdx.jsonの例
"packages": [ { ... (snip) ... "name": "openssl", "sourceInfo": "CVEs fixed: CVE-2024-9143",//★これ "summary": "Secure Socket Layer", "supplier": "Organization: OpenEmbedded ()", "versionInfo": "3.2.3" },
パッケージ
update-rc.d.spdx.jsonを例示する。
{ "SPDXID": "SPDXRef-DOCUMENT", "creationInfo": { "comment": "This document was created by analyzing packages created during the build.", "created": "2025-01-24T12:34:48Z", "creators": [ "Tool: OpenEmbedded Core create-spdx.bbclass", "Organization: OpenEmbedded ()", "Person: N/A ()" ], "licenseListVersion": "3.14" }, "dataLicense": "CC0-1.0", "documentNamespace": "http://spdx.org/spdxdocs/update-rc.d-fde24237-98eb-54c2-b3e8-cc69971dd42c", "externalDocumentRefs": [ { "checksum": { "algorithm": "SHA1", "checksumValue": "9732cb1d663117e9acd729bbd9bd453141ef3c14" }, "externalDocumentId": "DocumentRef-recipe-update-rc.d", "spdxDocument": "http://spdx.org/spdxdocs/recipe-update-rc.d-87cf57af-6fc6-500b-8aa5-8b53c9c11554" } ], "files": [ { "SPDXID": "SPDXRef-PackagedFile-update-rc.d-1", "checksums": [ { "algorithm": "SHA1", "checksumValue": "8cabd995976443050be511510920ca5ecfc057c9" }, { "algorithm": "SHA256", "checksumValue": "5426fe8d447719957b51bdce842fb857816a6d5cd5053f7586ffbf66b48111d2" } ], "copyrightText": "NOASSERTION", "fileName": "usr/sbin/update-rc.d", "fileTypes": [ "BINARY" ], "licenseConcluded": "NOASSERTION", "licenseInfoInFiles": [ "NOASSERTION" ] } ], "name": "update-rc.d", "packages": [ { "SPDXID": "SPDXRef-Package-update-rc.d", "copyrightText": "NOASSERTION", "downloadLocation": "NOASSERTION", "hasFiles": [ "SPDXRef-PackagedFile-update-rc.d-1" ], "licenseConcluded": "NOASSERTION", "licenseDeclared": "GPL-2.0-or-later", "licenseInfoFromFiles": [ "NOASSERTION" ], "name": "update-rc.d", "packageVerificationCode": { "packageVerificationCodeValue": "35e8e3f99d23c8b20ea0ccfc4a21222fdc8eff34" }, "supplier": "Organization: OpenEmbedded ()", "versionInfo": "0.8+git" } ], "relationships": [ { "relatedSpdxElement": "DocumentRef-recipe-update-rc.d:SPDXRef-Recipe-update-rc.d", "relationshipType": "GENERATED_FROM", "spdxElementId": "SPDXRef-Package-update-rc.d" }, { "relatedSpdxElement": "SPDXRef-Package-update-rc.d", "relationshipType": "DESCRIBES", "spdxElementId": "SPDXRef-DOCUMENT" }, { "relatedSpdxElement": "SPDXRef-PackagedFile-update-rc.d-1", "relationshipType": "CONTAINS", "spdxElementId": "SPDXRef-Package-update-rc.d" } ], "spdxVersion": "SPDX-2.2" }
大きく下記のブロックに分けることができる。
- creationInfo: SPDXファイル自体の情報
- externalDocumentRefs: 外部のSPDXファイルへの参照
- 元になったレシピ
- files: パッケージに含まれるファイルの情報
- packages: パッケージの情報(バージョン、ライセンス、提供者など)
- relationship: 各エレメントとの関係(GENERATED_FROM,DESCRIBES,CONTAINS...)
依存関係はここに含まれない。
ランタイム
runtime-update-rc.d.spdx.jsonを例示する。
{ "SPDXID": "SPDXRef-DOCUMENT", "creationInfo": { "comment": "This document was created by analyzing package runtime dependencies.", "created": "2025-01-24T12:34:49Z", "creators": [ "Tool: OpenEmbedded Core create-spdx.bbclass", "Organization: OpenEmbedded ()", "Person: N/A ()" ], "licenseListVersion": "3.14" }, "dataLicense": "CC0-1.0", "documentNamespace": "http://spdx.org/spdxdocs/runtime-update-rc.d-91536303-d4bc-55bf-bad5-db561c40958c", "externalDocumentRefs": [ { "checksum": { "algorithm": "SHA1", "checksumValue": "d2d90dd2cf17edb835512a92866156918cc23e18" }, "externalDocumentId": "DocumentRef-package-update-rc.d", "spdxDocument": "http://spdx.org/spdxdocs/update-rc.d-fde24237-98eb-54c2-b3e8-cc69971dd42c" } ], "name": "runtime-update-rc.d", "relationships": [ { "relatedSpdxElement": "DocumentRef-package-update-rc.d:SPDXRef-DOCUMENT", "relationshipType": "AMENDS", "spdxElementId": "SPDXRef-DOCUMENT" } ], "spdxVersion": "SPDX-2.2" }
大きく下記のブロックに分けることができる。
- creationInfo: SPDXファイル自体の情報
- externalDocumentRefs: 外部のSPDXファイルへの参照
- 対象のパッケージ
- 依存関係のあるパッケージのランタイム
- relationship: 各エレメントとの関係(AMENDS、RUNTIME_DEPENDENCY_OF...)