From 8a42e12ace082cf94238baf366813d32bde5de69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20M=C3=A4nnchen?= Date: Wed, 12 Mar 2025 15:22:32 +0100 Subject: [PATCH] Setup Build SBoM & Build Provenance --- .github/actions/build-container/action.yml | 32 +++++++++- .github/actions/build-release/action.yml | 71 ++++++++++++++++++++++ .github/workflows/release-containers.yaml | 2 + .github/workflows/release-nightly.yaml | 4 ++ .github/workflows/release.yaml | 2 + containers/elixir-alpine.dockerfile | 1 + containers/elixir-slim.dockerfile | 1 + containers/elixir.dockerfile | 1 + containers/erlang-alpine.dockerfile | 1 + containers/erlang-slim.dockerfile | 1 + containers/erlang.dockerfile | 1 + containers/node-alpine.dockerfile | 1 + containers/node-slim.dockerfile | 1 + containers/node.dockerfile | 1 + containers/scratch.dockerfile | 1 + 15 files changed, 120 insertions(+), 1 deletion(-) diff --git a/.github/actions/build-container/action.yml b/.github/actions/build-container/action.yml index ac4620021..d49a3929e 100644 --- a/.github/actions/build-container/action.yml +++ b/.github/actions/build-container/action.yml @@ -15,7 +15,7 @@ runs: uses: robinraju/release-downloader@v1 with: releaseId: "${{ inputs.release-id }}" - fileName: "gleam-${{ inputs.version }}-{x86_64-unknown-linux-musl,aarch64-unknown-linux-musl}.tar.gz" + fileName: "gleam-${{ inputs.version }}-{x86_64-unknown-linux-musl,aarch64-unknown-linux-musl}.*" - name: "Unpack release files into correct location" shell: bash @@ -33,6 +33,11 @@ runs: # Move files into place mv gleam "gleam-$SHORT" + # The SBoM is added to the images so that the Docker Scout Scanner is + # able to find the info about the gleam binary since it was not + # installed by the operating system package manager. + mv "gleam-$VERSION-$LONG.tar.gz.sbom.spdx.json" "gleam-$SHORT.sbom.spdx.json" + # Delete Unused Files rm -rf "gleam-$VERSION-$LONG*" done @@ -74,6 +79,31 @@ runs: platforms: linux/amd64,linux/arm64 file: containers/${{ matrix.base-image }}.dockerfile push: true + + # Enabling `provenance` will cause the action to create SLSA build + # provenance and push it alongside the tagged image. In practical terms, + # we're adding info to the tag that attests to where, when, and how the + # asset and image was built. + # + # For more info on Docker Attestations, see: + # https://docs.docker.com/build/ci/github-actions/attestations/ + provenance: true + + # Enabling `sbom` will trigger an SBoM Scan using Docker Scout: + # https://docs.docker.com/scout/how-tos/view-create-sboms/ + # The scan will detect any operating system packages as well as the Gleam + # Build SBoM added into the Docker Container. + # + # Why is this helpful? + # * If you build services on top of these container images, you can track + # all dependencies that ship with Gleam, plus the rest of your stack in + # the image. + # * This makes it easier to do image-level vulnerability scans and + # compliance checks. + # + # For more info on Docker SBoMs, see: + # https://docs.docker.com/build/metadata/attestations/sbom/ + sbom: true tags: ${{ steps.versions.outputs.container-tag }} labels: | org.opencontainers.image.title=gleam diff --git a/.github/actions/build-release/action.yml b/.github/actions/build-release/action.yml index 37a37bda0..2febfcef1 100644 --- a/.github/actions/build-release/action.yml +++ b/.github/actions/build-release/action.yml @@ -28,6 +28,9 @@ outputs: ${{ steps.build.outputs.archive }} ${{ steps.build.outputs.archive }}.sha256 ${{ steps.build.outputs.archive }}.sha512 + ${{ steps.build.outputs.archive }}.sigstore + ${{ steps.build.outputs.archive }}.sbom.spdx.json + ${{ steps.build.outputs.archive }}.sbom.cyclonedx.json runs: using: "composite" @@ -39,6 +42,12 @@ runs: target: ${{ inputs.target }} cache-key: v1-${{ inputs.target }} + - name: Install Cargo SBoM + shell: bash + # The `cargo-sbom` version is specified in the next line. Change it to + # keep it up-to-date. + run: cargo install cargo-sbom@~0.9.1 + - name: Build WASM release binary if: ${{ inputs.target != 'wasm32-unknown-unknown' }} uses: clechasseur/rs-cargo@v3 @@ -124,6 +133,29 @@ runs: TARGET: "${{ inputs.target }}" ARCHIVE: "${{ steps.build.outputs.archive }}" + # By using `cargo-sbom``, we create two formats of Build SBoMs + # (SPDX and CycloneDX) for the gleam build. + # We store those files alongside the build artifacts on the GitHub Release + # page and also use them to create Container SBoMs for Docker images. + # + # Why is this helpful? + # * It gives us and our users complete visibility into which dependencies + # and which versions are present in the build / container image. + # * The SBoM can be fed into vulnerability scanners so that anyone can check + # if any dependencies have known security issues. + - name: Generate Build SBoM + shell: bash + run: | + cargo-sbom \ + --output-format spdx_json_2_3 \ + > "$ARCHIVE.sbom.spdx.json" + + cargo-sbom \ + --output-format cyclone_dx_json_1_4 \ + > "$ARCHIVE.sbom.cyclonedx.json" + env: + ARCHIVE: "${{ steps.build.outputs.archive }}" + - name: Hash Build Archive shell: bash run: | @@ -132,6 +164,42 @@ runs: env: ARCHIVE: "${{ steps.build.outputs.archive }}" + # We provide SLSA Provenance for the distribution build. This attests to + # where, when, and how the asset or image was built. + # + # Why is this helpful? + # * It provides a record of the exact Git commit (git sha) and GitHub + # Actions workflow used to produce a release. + # * Users or automated systems can verify that the artifact you’re + # downloading was indeed built from the official Gleam repo, on a + # particular date, using the correct pipeline and not tampered with later. + # * The attestation is published to a transparency log for extra + # verification: https://github.com/gleam-lang/gleam/attestations/ + # + # For more information, see: + # * https://github.com/actions/attest + # * https://github.com/actions/attest-sbom + - name: Attest Distribution Assets with SBoM + id: attest-sbom + uses: actions/attest-sbom@v2 + with: + subject-path: | + ${{ steps.build.outputs.archive }} + ${{ steps.build.outputs.archive }}.sbom.spdx.json + ${{ steps.build.outputs.archive }}.sbom.cyclonedx.json + sbom-path: "${{ steps.build.outputs.archive }}.sbom.spdx.json" + + # The provenanve information is stored alongside the built artifact with + # the `.sigstore` file extension. + - name: "Copy SBoM provenance" + id: sbom-provenance + shell: bash + run: | + cp "$ATTESTATION" "$ARCHIVE.sigstore" + env: + ARCHIVE: "${{ steps.build.outputs.archive }}" + ATTESTATION: "${{ steps.attest-sbom.outputs.bundle-path }}" + - name: Upload artifact uses: actions/upload-artifact@v4 with: @@ -140,4 +208,7 @@ runs: ${{ steps.build.outputs.archive }} ${{ steps.build.outputs.archive }}.sha256 ${{ steps.build.outputs.archive }}.sha512 + ${{ steps.build.outputs.archive }}.sigstore + ${{ steps.build.outputs.archive }}.sbom.spdx.json + ${{ steps.build.outputs.archive }}.sbom.cyclonedx.json overwrite: true diff --git a/.github/workflows/release-containers.yaml b/.github/workflows/release-containers.yaml index 6ce798c38..0fdd790df 100644 --- a/.github/workflows/release-containers.yaml +++ b/.github/workflows/release-containers.yaml @@ -6,6 +6,8 @@ on: permissions: packages: write + id-token: write + attestations: write jobs: publish-container-images: diff --git a/.github/workflows/release-nightly.yaml b/.github/workflows/release-nightly.yaml index 7de205dce..51fc70333 100644 --- a/.github/workflows/release-nightly.yaml +++ b/.github/workflows/release-nightly.yaml @@ -12,6 +12,8 @@ env: permissions: contents: write packages: write + id-token: write + attestations: write jobs: # Check if the actions already ran in the last 24 hours @@ -48,6 +50,8 @@ jobs: *.tar.gz *.sha256 *.sha512 + *.sigstore + *.sbom.*.json build-release: name: build-release diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 22b2502e5..c2a84f2ac 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,6 +10,8 @@ env: permissions: contents: write + id-token: write + attestations: write jobs: build-release: diff --git a/containers/elixir-alpine.dockerfile b/containers/elixir-alpine.dockerfile index fa33b1564..95b220574 100644 --- a/containers/elixir-alpine.dockerfile +++ b/containers/elixir-alpine.dockerfile @@ -2,5 +2,6 @@ FROM elixir:alpine ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/elixir-slim.dockerfile b/containers/elixir-slim.dockerfile index 5defb8cf3..fcc12ef78 100644 --- a/containers/elixir-slim.dockerfile +++ b/containers/elixir-slim.dockerfile @@ -2,5 +2,6 @@ FROM elixir:slim ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/elixir.dockerfile b/containers/elixir.dockerfile index 70e7b71d6..85bf68c2d 100644 --- a/containers/elixir.dockerfile +++ b/containers/elixir.dockerfile @@ -2,5 +2,6 @@ FROM elixir:latest ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/erlang-alpine.dockerfile b/containers/erlang-alpine.dockerfile index 0110448d1..04fc5af2d 100644 --- a/containers/erlang-alpine.dockerfile +++ b/containers/erlang-alpine.dockerfile @@ -2,5 +2,6 @@ FROM erlang:alpine ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/erlang-slim.dockerfile b/containers/erlang-slim.dockerfile index b5b88acd1..9509f5753 100644 --- a/containers/erlang-slim.dockerfile +++ b/containers/erlang-slim.dockerfile @@ -2,5 +2,6 @@ FROM erlang:slim ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/erlang.dockerfile b/containers/erlang.dockerfile index af394fca4..ea2c46525 100644 --- a/containers/erlang.dockerfile +++ b/containers/erlang.dockerfile @@ -2,5 +2,6 @@ FROM erlang:latest ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/node-alpine.dockerfile b/containers/node-alpine.dockerfile index 9d88f0e94..b14f47c90 100644 --- a/containers/node-alpine.dockerfile +++ b/containers/node-alpine.dockerfile @@ -2,5 +2,6 @@ FROM node:alpine ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/node-slim.dockerfile b/containers/node-slim.dockerfile index 36f3d58f0..a0452807d 100644 --- a/containers/node-slim.dockerfile +++ b/containers/node-slim.dockerfile @@ -2,5 +2,6 @@ FROM node:slim ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/node.dockerfile b/containers/node.dockerfile index a86f0e47e..3e7eb5b8a 100644 --- a/containers/node.dockerfile +++ b/containers/node.dockerfile @@ -2,5 +2,6 @@ FROM node:latest ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] diff --git a/containers/scratch.dockerfile b/containers/scratch.dockerfile index 44bbc28e3..a6f95f317 100644 --- a/containers/scratch.dockerfile +++ b/containers/scratch.dockerfile @@ -2,5 +2,6 @@ FROM scratch ARG TARGETARCH COPY gleam-${TARGETARCH} /bin/gleam +COPY gleam-${TARGETARCH}.sbom.spdx.json /opt/sbom/ CMD ["gleam"] -- 2.51.2