Skip to content

Software Provenance Workflow Using SBOMs as OCI Artifacts

Info

This article demonstrates a workflow for building an image while simultaneously creating a Software Bill of Materials (SBOM) as a related OCI artifact.

About SBOMs for container images

The recent Executive Order on Improving the Nation's Cybersecurity provides guidance for hardening the software supply chain, including "providing a purchaser a Software Bill of Materials (SBOM) for each product directly or by publishing it on a public website."

An SBOM is a comprehensive inventory of software components used in building a software image. The SBOM includes descriptions of all components, including libraries and modules, their relationships and hierarchies, and software supply chain origin information for each and every component of the image. The transparency provided by a detailed SBOM gives the user greater confidence in the security of the image and simplifies vulnerability assessment.

You can create an SBOM for an existing container image by using commonly-available third-party tools to scan the image and generate the SBOM. However, as developers work to reduce the size of images, in some cases there may not be enough data or context to produce a correct SBOM. A better approach is to create the SBOM as part of the original build process. Stacker provides this capability.

Workflow

The following sections describe a step-by-step workflow for creating a stacker YAML file, then building and publishing an alpine container image that includes the SBOM.

This example workflow implements the following scenario:

  • The stacker YAML file is named b.yaml
  • The workflow builds an alpine container image
  • The image is pushed to an OCI registry that is assumed to be running at localhost:8080

The first three steps in the workflow modify the stacker YAML file. To view the full stacker file used in this example workflow, see Reference: Full stacker file.

Step 1: stacker file: Enable SBOM creation

The build-time creation of an SBOM is enabled by including the bom attribute in the stacker YAML file (with generate: true), along with annotations. Both bom and annotations are required. This excerpt shows an example of the necessary sections and settings:

bom-test:
  from:
      type: docker
      url: docker://alpine:edge
  bom:
      generate: true
  annotations:
      org.opencontainers.image.authors: bom-test
      org.opencontainers.image.vendor: bom-test
      org.opencontainers.image.licenses: MIT

Step 2: stacker file: Discover all installed packages

To assist in creating the SBOM, stacker includes a tool for discovering the packages that will be present in the image. Include the following run command in your stacker YAML file:

  run: |
      /stacker/tools/static-stacker bom discover

Step 3: stacker file: Enable cleanup after the build (optional)

When the build is complete, you can remove unnecessary files to reduce the image size, as in this example for an alpine build:

  run: | 
    <...other commands...>
    rm -f /etc/alpine-release /etc/apk/arch /etc/apk/repositories /etc/apk/world /etc/issue /etc/os-release /etc/secfixes.d/alpine /lib/apk/db/installed /lib/apk/db/lock /lib/apk/db/scripts.tar /lib/apk/db/triggers

Step 4: Build the image

Run the stacker build command, using the modified stacker file.

$ stacker build -f b.yaml

Step 5: Publish the image to the OCI registry

Run the stacker publish command to publish the container image to the OCI registry.

$ ./stacker publish \
    --stacker-file b.yaml \
    --tag latest \
    --url docker://localhost:8080/ \
    --skip-tls

Step 6: Inspect the artifact tree

Info

Changes added in OCI Distribution Spec v1.1.0-rc3 and OCI Image Spec v1.1.0-rc4 (summarized here) allow arbitrary artifact types and references. These changes support software supply chain use cases such as SBOMs.

Run regctl artifact tree to display the artifact tree of the container image in the registry. The artifact tree for this image includes two SBOM-related artifact files.

$ ./regctl artifact tree localhost:8080/bom-test:latest

Ref: localhost:8080/bom-test:latest  
Digest: sha256:9172c5f692f2c65e4f773448503b21dba2de6454bd159905c4bf6d83176e4ea3
Referrers:  
   - sha256:9c0655368b10ca4b2ffe39e4dd261fb89df25a46ae92d6eb4e6e1792a451883e: application/spdx+json
   - sha256:9df25a46ae92d6eb4e6e1792a451883e9c0655368b10ca4b2ffe39e4dd261fb8: application/vnd.stackerbuild.inventory+json

The displayed artifact tree shows that the original image (localhost:8080/bom-test:latest) contains two direct referrers:

  • application/spdx+json is a comprehensive SPDX SBOM file in JSON format
  • application/vnd.stackerbuild.inventory+json is a summary inventory, listing the file name, file size, SHA digest, and mode of every file in the SBOM

The first two entries in the inventory file are shown here:

$ cat .stacker/artifacts/bom-test/inventory.json | jq . | less

{
  {
      "path": "/bin/arch",
      "size": 12,
      "checksum": "",
      "mode": "01000000777"
  },
  {
      "path": "/bin/ash",
      "size": 12,
      "checksum": "",
      "mode": "01000000777"
  }, 
  ...

Adding additional files to a package and SBOM

You can add to the image one or more files or directories that are not otherwise included in the build. This capability is useful if, for example, you are building a well-known distribution but you want to include some other content in the image. If the extra content is added after the static-stacker bom discover tool in the run section of the stacker file, you must explicitly provide the SBOM information in the bom section. Otherwise the SBOM creation will due to missing SBOM information, causing the build to fail.

Use the bom/packages attribute in the stacker YAML file to provide SBOM information for the additional files or directory paths. In the following example, the new content is added as a package. A name (pkg1), version, and license information is provided for the package. Under the paths attribute, provide a bracketed, comma-separated list of the files or directories to add. In this example, a single file named file1 is added.

bom:
  generate: true
  packages:
    - name: pkg1
      version: 1.0.0
      license: Apache-2.0
      paths: [/file1]

With the SBOM information provided, you can now safely use file1 in the run section, as shown here:

  run: |
      /stacker/tools/static-stacker bom discover
      touch /file1

Reference: Full stacker file

💡 To copy the file to the clipboard, click the copy icon that appears in the upper right corner of the expanded text box.

Click here to view the complete stacker file for this workflow example
bom-test:
  from:
    type: docker
    url: docker://alpine:edge
  bom:
    generate: true
    packages:
      - name: pkg1
        version: 1.0.0
        license: Apache-2.0
        paths: [/file1]
  annotations:
    org.opencontainers.image.authors: bom-test
    org.opencontainers.image.vendor: bom-test
    org.opencontainers.image.licenses: MIT
  run: |
    # discover installed pkgs
    /stacker/tools/static-stacker bom discover
    # run our cmds
    ls -al  /
    # some changes
    touch /file1
    # cleanup
    rm -f /etc/alpine-release /etc/apk/arch /etc/apk/repositories /etc/apk/world /etc/issue /etc/os-release /etc/secfixes.d/alpine /lib/apk/db/installed /lib/apk/db/lock /lib/apk/db/scripts.tar /lib/apk/db/triggers 

Last update: April 8, 2024