From b5bcbfe4e0e35053d46ef62677d27baecd27cfff Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Tue, 17 Mar 2026 09:26:10 +0000 Subject: [PATCH 1/3] feat: add SLSA provenance and attestations to all Docker build workflows Enable provenance: true and sbom: true on all image builds, upgrade attest-build-provenance to v2, and add merged manifest attestation for multi-platform bake workflows. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-agent.yml | 21 ++++++++++++++++++++- .github/workflows/build.yml | 23 +++++++++++++++++++++-- .github/workflows/quay.yml | 21 ++++++++++++++++++++- .github/workflows/saas.yml | 11 +++++++++++ .github/workflows/ws-server.yml | 11 +++++++++++ 5 files changed, 83 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-agent.yml b/.github/workflows/build-agent.yml index 75222bdfa..64a3a4e1e 100644 --- a/.github/workflows/build-agent.yml +++ b/.github/workflows/build-agent.yml @@ -84,6 +84,7 @@ jobs: uses: docker/bake-action@v5 with: sbom: true + provenance: true files: | ./docker-bake-agent.hcl /tmp/bake-meta.json @@ -108,7 +109,7 @@ jobs: retention-days: 1 - name: Attest Build Provenance - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v2 with: subject-digest: "${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}" push-to-registry: false @@ -116,6 +117,10 @@ jobs: merge: runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + attestations: write needs: - build steps: @@ -147,6 +152,20 @@ jobs: docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("${{ env.REGISTRY_IMAGE }}")) | "-t " + .) | join(" ")' /tmp/bake-meta.json) \ $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Get merged manifest digest + id: manifest + run: | + TAG=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) + DIGEST=$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --format '{{.Manifest.Digest}}') + echo "digest=${DIGEST}" >> $GITHUB_OUTPUT + + - name: Attest Merged Manifest Provenance + uses: actions/attest-build-provenance@v2 + with: + subject-digest: ${{ steps.manifest.outputs.digest }} + subject-name: ${{ env.REGISTRY_IMAGE }} + push-to-registry: false + - name: Inspect image run: | docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f4f2f243..fd3fb329a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,6 +85,7 @@ jobs: uses: docker/bake-action@v5 with: sbom: true + provenance: true files: | ./docker-bake.hcl /tmp/bake-meta.json @@ -109,14 +110,18 @@ jobs: retention-days: 1 - name: Attest Build Provenance - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v2 with: subject-digest: "${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}" push-to-registry: false - subject-name: ${{ env.REGISTRY_IMAGE }} + subject-name: ${{ env.REGISTRY_IMAGE }} merge: runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + attestations: write needs: - build steps: @@ -148,6 +153,20 @@ jobs: docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("${{ env.REGISTRY_IMAGE }}")) | "-t " + .) | join(" ")' /tmp/bake-meta.json) \ $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Get merged manifest digest + id: manifest + run: | + TAG=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) + DIGEST=$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --format '{{.Manifest.Digest}}') + echo "digest=${DIGEST}" >> $GITHUB_OUTPUT + + - name: Attest Merged Manifest Provenance + uses: actions/attest-build-provenance@v2 + with: + subject-digest: ${{ steps.manifest.outputs.digest }} + subject-name: ${{ env.REGISTRY_IMAGE }} + push-to-registry: false + - name: Inspect image run: | docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) diff --git a/.github/workflows/quay.yml b/.github/workflows/quay.yml index 7fba6ed55..d0a7ed86b 100644 --- a/.github/workflows/quay.yml +++ b/.github/workflows/quay.yml @@ -92,6 +92,7 @@ jobs: uses: docker/bake-action@v5 with: sbom: true + provenance: true files: | ./docker-bake.hcl /tmp/bake-meta.json @@ -116,7 +117,7 @@ jobs: retention-days: 1 - name: Attest Build Provenance - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v2 with: subject-digest: "${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}" push-to-registry: false @@ -124,6 +125,10 @@ jobs: merge: runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + attestations: write needs: - build steps: @@ -156,6 +161,20 @@ jobs: docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("${{ env.REGISTRY_IMAGE }}")) | "-t " + .) | join(" ")' /tmp/bake-meta.json) \ $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Get merged manifest digest + id: manifest + run: | + TAG=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) + DIGEST=$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --format '{{.Manifest.Digest}}') + echo "digest=${DIGEST}" >> $GITHUB_OUTPUT + + - name: Attest Merged Manifest Provenance + uses: actions/attest-build-provenance@v2 + with: + subject-digest: ${{ steps.manifest.outputs.digest }} + subject-name: ${{ env.REGISTRY_IMAGE }} + push-to-registry: false + - name: Inspect image run: | docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) diff --git a/.github/workflows/saas.yml b/.github/workflows/saas.yml index 0b7a45ef7..1f771e256 100644 --- a/.github/workflows/saas.yml +++ b/.github/workflows/saas.yml @@ -27,6 +27,7 @@ jobs: permissions: contents: read packages: write + attestations: write # This is used to complete the identity challenge # with sigstore/fulcio when running outside of PRs. id-token: write @@ -92,6 +93,16 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + sbom: true + provenance: true + + - name: Attest Build Provenance + if: ${{ github.event_name != 'pull_request' }} + uses: actions/attest-build-provenance@v2 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.build-and-push.outputs.digest }} + push-to-registry: true # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker diff --git a/.github/workflows/ws-server.yml b/.github/workflows/ws-server.yml index a18c66c90..975c309ff 100644 --- a/.github/workflows/ws-server.yml +++ b/.github/workflows/ws-server.yml @@ -27,6 +27,7 @@ jobs: permissions: contents: read packages: write + attestations: write # This is used to complete the identity challenge # with sigstore/fulcio when running outside of PRs. id-token: write @@ -80,6 +81,16 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + sbom: true + provenance: true + + - name: Attest Build Provenance + if: ${{ github.event_name != 'pull_request' }} + uses: actions/attest-build-provenance@v2 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.build-and-push.outputs.digest }} + push-to-registry: true # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker From 919d8975fe6e94ebed71c9e0a043011687a5cd85 Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Tue, 17 Mar 2026 09:50:23 +0000 Subject: [PATCH 2/3] fix: compute merged manifest digest from raw bytes instead of Go template The --format '{{.Manifest.Digest}}' template doesn't work for OCI image indexes. Use --raw | sha256sum to reliably compute the digest. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-agent.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/quay.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-agent.yml b/.github/workflows/build-agent.yml index 64a3a4e1e..fbb044410 100644 --- a/.github/workflows/build-agent.yml +++ b/.github/workflows/build-agent.yml @@ -156,7 +156,7 @@ jobs: id: manifest run: | TAG=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) - DIGEST=$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --format '{{.Manifest.Digest}}') + DIGEST="sha256:$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --raw | sha256sum | cut -d ' ' -f1)" echo "digest=${DIGEST}" >> $GITHUB_OUTPUT - name: Attest Merged Manifest Provenance diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd3fb329a..a1f3a9228 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -157,7 +157,7 @@ jobs: id: manifest run: | TAG=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) - DIGEST=$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --format '{{.Manifest.Digest}}') + DIGEST="sha256:$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --raw | sha256sum | cut -d ' ' -f1)" echo "digest=${DIGEST}" >> $GITHUB_OUTPUT - name: Attest Merged Manifest Provenance diff --git a/.github/workflows/quay.yml b/.github/workflows/quay.yml index d0a7ed86b..ccad59efe 100644 --- a/.github/workflows/quay.yml +++ b/.github/workflows/quay.yml @@ -165,7 +165,7 @@ jobs: id: manifest run: | TAG=$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json) - DIGEST=$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --format '{{.Manifest.Digest}}') + DIGEST="sha256:$(docker buildx imagetools inspect "${{ env.REGISTRY_IMAGE }}:${TAG}" --raw | sha256sum | cut -d ' ' -f1)" echo "digest=${DIGEST}" >> $GITHUB_OUTPUT - name: Attest Merged Manifest Provenance From 8ca38d527ff96876182824df75fcb3167caa9e10 Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Tue, 17 Mar 2026 09:53:49 +0000 Subject: [PATCH 3/3] chore: remove setup-qemu-action from build workflows QEMU is unnecessary since all platform builds run on native runners. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-agent.yml | 3 --- .github/workflows/build.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/build-agent.yml b/.github/workflows/build-agent.yml index fbb044410..13437a8d5 100644 --- a/.github/workflows/build-agent.yml +++ b/.github/workflows/build-agent.yml @@ -67,9 +67,6 @@ jobs: name: bake-meta path: /tmp - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1f3a9228..77aad45db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,9 +68,6 @@ jobs: name: bake-meta path: /tmp - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3