From 2e07c31e996d8503746fcd5fbf14aa9f54efcd10 Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Mon, 2 Feb 2026 10:12:47 -0500 Subject: [PATCH] Add SBOM and provenance attestations via cosign Use cosign to attach SPDX SBOM (generated by apko) and SLSA provenance attestations to all published images. Applied to publish, rebuild, and update-check pipelines. Also added push trigger on self-path for rebuild.yaml. Co-Authored-By: Claude Opus 4.5 --- .gitea/workflows/publish.yaml | 82 +++++++++++++++++++++++++++-- .gitea/workflows/rebuild.yaml | 84 ++++++++++++++++++++++++++++-- .gitea/workflows/update-check.yaml | 80 ++++++++++++++++++++++++++-- 3 files changed, 236 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml index a457af7..381a037 100644 --- a/.gitea/workflows/publish.yaml +++ b/.gitea/workflows/publish.yaml @@ -50,6 +50,14 @@ jobs: tar xzf /tmp/apko.tar.gz --strip-components=1 -C /usr/local/bin rm /tmp/apko.tar.gz + - name: Install cosign + run: | + COSIGN_ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + COSIGN_VERSION=$(curl -fsSL "https://api.github.com/repos/sigstore/cosign/releases/latest" | jq -r '.tag_name') + curl -fsSL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-${COSIGN_ARCH}" \ + -o /usr/local/bin/cosign + chmod +x /usr/local/bin/cosign + - name: Login to Docker Registry uses: docker/login-action@v3 with: @@ -57,9 +65,77 @@ jobs: password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build and push image + id: publish run: | + IMAGE_REF=${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} + mkdir -p /tmp/sbom apko publish ${{ matrix.config }} \ - ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} + --sbom-path /tmp/sbom \ + --image-refs /tmp/image-refs.txt \ + "${IMAGE_REF}" + echo "image_ref=${IMAGE_REF}" >> "$GITHUB_OUTPUT" + DIGEST=$(head -1 /tmp/image-refs.txt | sed 's/.*@//') + echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT" + + - name: Attach SBOM attestation + env: + COSIGN_YES: "true" + run: | + IMAGE_DIGEST="${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}@${{ steps.publish.outputs.digest }}" + # Attach SPDX SBOM + SBOM_FILE=$(ls /tmp/sbom/*.spdx.json 2>/dev/null | head -1) + if [ -n "$SBOM_FILE" ]; then + cosign attach sbom --sbom "${SBOM_FILE}" "${IMAGE_DIGEST}" + echo "SBOM attached successfully" + else + echo "No SBOM file found, skipping" + fi + + - name: Generate and attach provenance + env: + COSIGN_YES: "true" + run: | + IMAGE_DIGEST="${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}@${{ steps.publish.outputs.digest }}" + # Generate SLSA-style provenance + cat > /tmp/provenance.json << PROVEOF + { + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", + "subject": [ + { + "name": "${{ steps.publish.outputs.image_ref }}", + "digest": { + "sha256": "$(echo '${{ steps.publish.outputs.digest }}' | sed 's/sha256://')" + } + } + ], + "predicate": { + "buildType": "https://apko.dev/build/v1", + "builder": { + "id": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + "invocation": { + "configSource": { + "uri": "${{ github.server_url }}/${{ github.repository }}", + "digest": { + "sha1": "${{ github.sha }}" + }, + "entryPoint": "${{ matrix.config }}" + } + }, + "metadata": { + "buildInvocationID": "${{ github.run_id }}", + "completeness": { + "parameters": true, + "environment": true, + "materials": true + } + } + } + } + PROVEOF + cosign attest --predicate /tmp/provenance.json --type slsaprovenance "${IMAGE_DIGEST}" + echo "Provenance attestation attached successfully" - name: Install Docker Scout run: | @@ -68,5 +144,5 @@ jobs: - name: Docker Scout CVE Scan run: | - docker pull ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} - docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} --only-severity critical,high + docker pull ${{ steps.publish.outputs.image_ref }} + docker scout cves ${{ steps.publish.outputs.image_ref }} --only-severity critical,high diff --git a/.gitea/workflows/rebuild.yaml b/.gitea/workflows/rebuild.yaml index 50791db..793119c 100644 --- a/.gitea/workflows/rebuild.yaml +++ b/.gitea/workflows/rebuild.yaml @@ -4,7 +4,9 @@ on: schedule: # Rebuild weekly to pick up Wolfi security patches - cron: '0 6 * * 1' - workflow_dispatch: + push: + paths: + - '.gitea/workflows/rebuild.yaml' permissions: contents: read @@ -42,6 +44,14 @@ jobs: tar xzf /tmp/apko.tar.gz --strip-components=1 -C /usr/local/bin rm /tmp/apko.tar.gz + - name: Install cosign + run: | + COSIGN_ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + COSIGN_VERSION=$(curl -fsSL "https://api.github.com/repos/sigstore/cosign/releases/latest" | jq -r '.tag_name') + curl -fsSL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-${COSIGN_ARCH}" \ + -o /usr/local/bin/cosign + chmod +x /usr/local/bin/cosign + - name: Login to Docker Registry uses: docker/login-action@v3 with: @@ -49,9 +59,75 @@ jobs: password: ${{ secrets.REGISTRY_PASSWORD }} - name: Rebuild and push with latest Wolfi packages + id: publish run: | + IMAGE_REF=${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest + mkdir -p /tmp/sbom apko publish ${{ matrix.config }} \ - ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest + --sbom-path /tmp/sbom \ + --image-refs /tmp/image-refs.txt \ + "${IMAGE_REF}" + echo "image_ref=${IMAGE_REF}" >> "$GITHUB_OUTPUT" + DIGEST=$(head -1 /tmp/image-refs.txt | sed 's/.*@//') + echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT" + + - name: Attach SBOM attestation + env: + COSIGN_YES: "true" + run: | + IMAGE_DIGEST="${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}@${{ steps.publish.outputs.digest }}" + SBOM_FILE=$(ls /tmp/sbom/*.spdx.json 2>/dev/null | head -1) + if [ -n "$SBOM_FILE" ]; then + cosign attach sbom --sbom "${SBOM_FILE}" "${IMAGE_DIGEST}" + echo "SBOM attached successfully" + else + echo "No SBOM file found, skipping" + fi + + - name: Generate and attach provenance + env: + COSIGN_YES: "true" + run: | + IMAGE_DIGEST="${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}@${{ steps.publish.outputs.digest }}" + cat > /tmp/provenance.json << PROVEOF + { + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", + "subject": [ + { + "name": "${{ steps.publish.outputs.image_ref }}", + "digest": { + "sha256": "$(echo '${{ steps.publish.outputs.digest }}' | sed 's/sha256://')" + } + } + ], + "predicate": { + "buildType": "https://apko.dev/build/v1", + "builder": { + "id": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + "invocation": { + "configSource": { + "uri": "${{ github.server_url }}/${{ github.repository }}", + "digest": { + "sha1": "${{ github.sha }}" + }, + "entryPoint": "${{ matrix.config }}" + } + }, + "metadata": { + "buildInvocationID": "${{ github.run_id }}", + "completeness": { + "parameters": true, + "environment": true, + "materials": true + } + } + } + } + PROVEOF + cosign attest --predicate /tmp/provenance.json --type slsaprovenance "${IMAGE_DIGEST}" + echo "Provenance attestation attached successfully" - name: Install Docker Scout run: | @@ -60,5 +136,5 @@ jobs: - name: Docker Scout CVE Scan run: | - docker pull ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest - docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest --only-severity critical,high + docker pull ${{ steps.publish.outputs.image_ref }} + docker scout cves ${{ steps.publish.outputs.image_ref }} --only-severity critical,high diff --git a/.gitea/workflows/update-check.yaml b/.gitea/workflows/update-check.yaml index fcaf8d4..f00c7ca 100644 --- a/.gitea/workflows/update-check.yaml +++ b/.gitea/workflows/update-check.yaml @@ -134,6 +134,14 @@ jobs: tar xzf /tmp/apko.tar.gz --strip-components=1 -C /usr/local/bin rm /tmp/apko.tar.gz + - name: Install cosign + run: | + COSIGN_ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + COSIGN_VERSION=$(curl -fsSL "https://api.github.com/repos/sigstore/cosign/releases/latest" | jq -r '.tag_name') + curl -fsSL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-${COSIGN_ARCH}" \ + -o /usr/local/bin/cosign + chmod +x /usr/local/bin/cosign + - name: Login to Docker Registry uses: docker/login-action@v3 with: @@ -141,9 +149,75 @@ jobs: password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build and push image + id: publish run: | + IMAGE_REF=${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest + mkdir -p /tmp/sbom apko publish ${{ matrix.config }} \ - ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest + --sbom-path /tmp/sbom \ + --image-refs /tmp/image-refs.txt \ + "${IMAGE_REF}" + echo "image_ref=${IMAGE_REF}" >> "$GITHUB_OUTPUT" + DIGEST=$(head -1 /tmp/image-refs.txt | sed 's/.*@//') + echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT" + + - name: Attach SBOM attestation + env: + COSIGN_YES: "true" + run: | + IMAGE_DIGEST="${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}@${{ steps.publish.outputs.digest }}" + SBOM_FILE=$(ls /tmp/sbom/*.spdx.json 2>/dev/null | head -1) + if [ -n "$SBOM_FILE" ]; then + cosign attach sbom --sbom "${SBOM_FILE}" "${IMAGE_DIGEST}" + echo "SBOM attached successfully" + else + echo "No SBOM file found, skipping" + fi + + - name: Generate and attach provenance + env: + COSIGN_YES: "true" + run: | + IMAGE_DIGEST="${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}@${{ steps.publish.outputs.digest }}" + cat > /tmp/provenance.json << PROVEOF + { + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", + "subject": [ + { + "name": "${{ steps.publish.outputs.image_ref }}", + "digest": { + "sha256": "$(echo '${{ steps.publish.outputs.digest }}' | sed 's/sha256://')" + } + } + ], + "predicate": { + "buildType": "https://apko.dev/build/v1", + "builder": { + "id": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + }, + "invocation": { + "configSource": { + "uri": "${{ github.server_url }}/${{ github.repository }}", + "digest": { + "sha1": "${{ github.sha }}" + }, + "entryPoint": "${{ matrix.config }}" + } + }, + "metadata": { + "buildInvocationID": "${{ github.run_id }}", + "completeness": { + "parameters": true, + "environment": true, + "materials": true + } + } + } + } + PROVEOF + cosign attest --predicate /tmp/provenance.json --type slsaprovenance "${IMAGE_DIGEST}" + echo "Provenance attestation attached successfully" - name: Install Docker Scout run: | @@ -152,8 +226,8 @@ jobs: - name: Docker Scout CVE Scan run: | - docker pull ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest - docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest --only-severity critical,high + docker pull ${{ steps.publish.outputs.image_ref }} + docker scout cves ${{ steps.publish.outputs.image_ref }} --only-severity critical,high notify-flutter: name: Create release for new Flutter version