From 8f5e4afe43731f833d683e197c1c113b84b0e5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Cuadrado=20Juan?= <2196685+viccuad@users.noreply.github.com> Date: Sat, 12 Oct 2024 10:14:37 +0200 Subject: [PATCH] ci: Generate and sign provenance information as image layer for SLSA lvl 3 (#504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: Generate and sign provenance info as image layer Configure docker buildx to generate provenance attestations as explained in https://docs.docker.com/build/metadata/attestations/slsa-provenance. Find the specific layer digest that corresponds to that provenance attestation, both for linux/amd64 and linux/arm64 architectures, and sign it. Signed-off-by: Víctor Cuadrado Juan * ci: Use github.repository_owner instead of hardcoded org This allows to test the CI workflows in a fork, and to not need push permissions to production OCI registry namespace under ghcr.io/kyverno. Signed-off-by: Víctor Cuadrado Juan --------- Signed-off-by: Víctor Cuadrado Juan --- .github/workflows/release-chart.yaml | 6 +- .github/workflows/release-image.yaml | 104 ++++++++++++++++++++++----- 2 files changed, 90 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release-chart.yaml b/.github/workflows/release-chart.yaml index ea911855..41a265a5 100644 --- a/.github/workflows/release-chart.yaml +++ b/.github/workflows/release-chart.yaml @@ -75,11 +75,11 @@ jobs: - name: Push to OCI run: | set -e - output=$(helm push .cr-release-packages/policy-reporter-${{ env.VERSION }}.tgz oci://ghcr.io/kyverno/charts 2>&1) + output=$(helm push .cr-release-packages/policy-reporter-${{ env.VERSION }}.tgz oci://ghcr.io/${{github.repository_owner}}/charts 2>&1) digest=$( echo "$output" | grep Digest | cut -c9-) echo CR_DIGEST=$digest echo "CR_DIGEST=$digest" >> "$GITHUB_ENV" - + - name: Sign Chart run: | - cosign sign --yes ghcr.io/kyverno/charts/policy-reporter@${{ env.CR_DIGEST }} \ No newline at end of file + cosign sign --yes ghcr.io/${{github.repository_owner}}/charts/policy-reporter@${{ env.CR_DIGEST }} diff --git a/.github/workflows/release-image.yaml b/.github/workflows/release-image.yaml index 88738ede..9e9de6f4 100644 --- a/.github/workflows/release-image.yaml +++ b/.github/workflows/release-image.yaml @@ -2,7 +2,7 @@ name: release-image on: push: tags: - - 'v*' + - "v*" paths-ignore: - README.md @@ -12,7 +12,7 @@ on: permissions: contents: read packages: write - id-token: write + id-token: write jobs: push-policy-reporter: @@ -54,10 +54,26 @@ jobs: with: push: true platforms: linux/arm64,linux/amd64 - cache-from: type=registry,ref=ghcr.io/kyverno/policy-reporter:buildcache - cache-to: type=registry,ref=ghcr.io/kyverno/policy-reporter:buildcache,mode=max + provenance: mode=max + cache-from: type=registry,ref=ghcr.io/${{github.repository_owner}}/policy-reporter:buildcache + cache-to: type=registry,ref=ghcr.io/${{github.repository_owner}}/policy-reporter:buildcache,mode=max tags: | - ghcr.io/kyverno/policy-reporter:${{ env.VERSION }} + ghcr.io/${{github.repository_owner}}/policy-reporter:${{ env.VERSION }} + + - name: Install Cosign + uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 + + - name: Sign image + shell: bash + env: + COSIGN_REPOSITORY: ghcr.io/${{github.repository_owner}}/signatures + run: | + set -e + cosign sign --yes \ + -a "repo=${{ github.repository }}" \ + -a "workflow=${{ github.workflow }}" \ + -a "ref=${{ github.sha }}" \ + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ steps.push.outputs.digest }} - name: Set up Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.2.0 @@ -65,7 +81,8 @@ jobs: go-version-file: go.mod cache-dependency-path: go.sum - - uses: CycloneDX/gh-gomod-generate-sbom@efc74245d6802c8cefd925620515442756c70d8f # v2.0.0 + - name: Generate SBOM + uses: CycloneDX/gh-gomod-generate-sbom@efc74245d6802c8cefd925620515442756c70d8f # v2.0.0 with: version: v1 args: app -licenses -json -output policy-reporter-bom.cdx.json -main . @@ -75,22 +92,75 @@ jobs: name: policy-reporter-bom-cdx path: policy-reporter-bom.cdx.json - - name: Install Cosign - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 - - - shell: bash + - name: Attach SBOM to image + shell: bash env: - COSIGN_REPOSITORY: ghcr.io/kyverno/signatures + COSIGN_REPOSITORY: ghcr.io/${{github.repository_owner}}/sbom + run: | + cosign attach sbom --sbom ./policy-reporter-bom.cdx.json --type cyclonedx ghcr.io/${{github.repository_owner}}/policy-reporter@${{ steps.push.outputs.digest }} + + - name: Install the crane command + uses: kubewarden/github-actions/crane-installer@d94509d260ee11a92b4f65bc0acd297feec24d7f # v3.3.5 + + - name: Find platform digest + shell: bash run: | set -e + DIGEST_AMD64=$(crane digest \ + --platform "linux/amd64" \ + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ steps.push.outputs.digest }}) + echo "PLATFORM_DIGEST_AMD64=${DIGEST_AMD64}" >> "$GITHUB_ENV" + DIGEST_ARM64=$(crane digest \ + --platform "linux/arm64" \ + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ steps.push.outputs.digest }}) + echo "PLATFORM_DIGEST_ARM64=${DIGEST_ARM64}" >> "$GITHUB_ENV" + + - name: Find attestation digest + run: | + set -e + DIGEST_AMD64=$(crane manifest ghcr.io/${{github.repository_owner}}/policy-reporter@${{ steps.push.outputs.digest }} \ + | jq '.manifests[] | select(.annotations["vnd.docker.reference.type"]=="attestation-manifest") | select(.annotations["vnd.docker.reference.digest"]=="${{ env.PLATFORM_DIGEST_AMD64 }}") | .digest' + ) + echo "ATTESTATION_MANIFEST_DIGEST_AMD64=${DIGEST_AMD64}" >> "$GITHUB_ENV" + DIGEST_ARM64=$(crane manifest ghcr.io/${{github.repository_owner}}/policy-reporter@${{ steps.push.outputs.digest }} \ + | jq '.manifests[] | select(.annotations["vnd.docker.reference.type"]=="attestation-manifest") | select(.annotations["vnd.docker.reference.digest"]=="${{ env.PLATFORM_DIGEST_ARM64 }}") | .digest' + ) + echo "ATTESTATION_MANIFEST_DIGEST_ARM64=${DIGEST_ARM64}" >> "$GITHUB_ENV" + + - name: Sign attestation manifest + run: | cosign sign --yes \ -a "repo=${{ github.repository }}" \ -a "workflow=${{ github.workflow }}" \ -a "ref=${{ github.sha }}" \ - ghcr.io/kyverno/policy-reporter@${{ steps.push.outputs.digest }} - - - shell: bash - env: - COSIGN_REPOSITORY: ghcr.io/kyverno/sbom + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ env.ATTESTATION_MANIFEST_DIGEST_AMD64}} + + cosign sign --yes \ + -a "repo=${{ github.repository }}" \ + -a "workflow=${{ github.workflow }}" \ + -a "ref=${{ github.sha }}" \ + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ env.ATTESTATION_MANIFEST_DIGEST_ARM64}} + + - name: Find provenance manifest digest + run: | + set -e + DIGEST_AMD64=$(crane manifest ghcr.io/${{github.repository_owner}}/policy-reporter@${{ env.ATTESTATION_MANIFEST_DIGEST_AMD64}} | \ + jq '.layers[] | select(.annotations["in-toto.io/predicate-type"] == "https://slsa.dev/provenance/v0.2") | .digest') + echo "PROVENANCE_DIGEST_AMD64=${DIGEST_AMD64}" >> "$GITHUB_ENV" + DIGEST_ARM64=$(crane manifest ghcr.io/${{github.repository_owner}}/policy-reporter@${{ env.ATTESTATION_MANIFEST_DIGEST_ARM64}} | \ + jq '.layers[] | select(.annotations["in-toto.io/predicate-type"] == "https://slsa.dev/provenance/v0.2") | .digest') + echo "PROVENANCE_DIGEST_ARM64=${DIGEST_ARM64}" >> "$GITHUB_ENV" + + - name: Sign provenance manifest run: | - cosign attach sbom --sbom ./policy-reporter-bom.cdx.json --type cyclonedx ghcr.io/kyverno/policy-reporter@${{ steps.push.outputs.digest }} \ No newline at end of file + cosign sign --yes \ + -a "repo=${{ github.repository }}" \ + -a "workflow=${{ github.workflow }}" \ + -a "ref=${{ github.sha }}" \ + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ env.PROVENANCE_DIGEST_AMD64}} + + cosign sign --yes \ + -a "repo=${{ github.repository }}" \ + -a "workflow=${{ github.workflow }}" \ + -a "ref=${{ github.sha }}" \ + ghcr.io/${{github.repository_owner}}/policy-reporter@${{ env.PROVENANCE_DIGEST_ARM64}}