diff --git a/.github/workflows/attestation.yml b/.github/workflows/attestation.yml new file mode 100644 index 00000000..7025d109 --- /dev/null +++ b/.github/workflows/attestation.yml @@ -0,0 +1,131 @@ +name: Sign attestation files + +on: + workflow_call: + inputs: + image-digest: + type: string + required: true + +jobs: + sbom: + name: Generate SBOM, sign and attach them to OCI image + strategy: + matrix: + arch: [amd64, arm64] + + permissions: + packages: write + id-token: write + + runs-on: ubuntu-latest + steps: + - name: Install cosign + uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # v3.6.0 + + - name: Install the syft command + uses: kubewarden/github-actions/syft-installer@d94509d260ee11a92b4f65bc0acd297feec24d7f # v3.3.5 + + - name: Install the crane command + uses: kubewarden/github-actions/crane-installer@d94509d260ee11a92b4f65bc0acd297feec24d7f # v3.3.5 + + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Find platform digest + shell: bash + run: | + set -e + DIGEST=$(crane digest \ + --platform "linux/${{ matrix.arch }}" \ + ghcr.io/${{ github.repository_owner }}/audit-scanner@${{ inputs.image-digest }}) + echo "PLATFORM_DIGEST=${DIGEST}" >> "$GITHUB_ENV" + + - name: Find attestation digest + run: | + set -e + DIGEST=$(crane manifest ghcr.io/${{github.repository_owner}}/audit-scanner@${{ inputs.image-digest }} \ + | jq '.manifests[] | select(.annotations["vnd.docker.reference.type"]=="attestation-manifest") | select(.annotations["vnd.docker.reference.digest"]=="${{ env.PLATFORM_DIGEST }}") | .digest' + ) + echo "ATTESTATION_MANIFEST_DIGEST=${DIGEST}" >> "$GITHUB_ENV" + + - name: Sign attestation manifest + run: | + cosign sign --yes \ + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.ATTESTATION_MANIFEST_DIGEST}} + + cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity-regexp="https://github.com/${{github.repository_owner}}/audit-scanner*" \ + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.ATTESTATION_MANIFEST_DIGEST}} + + - name: Find provenance manifest digest + run: | + set -e + DIGEST=$(crane manifest ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.ATTESTATION_MANIFEST_DIGEST}} | \ + jq '.layers[] | select(.annotations["in-toto.io/predicate-type"] == "https://slsa.dev/provenance/v0.2") | .digest') + echo "PROVENANCE_DIGEST=${DIGEST}" >> "$GITHUB_ENV" + + - name: Sign provenance manifest + run: | + cosign sign --yes \ + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.PROVENANCE_DIGEST}} + + cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity-regexp="https://github.com/${{github.repository_owner}}/audit-scanner*" \ + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.PROVENANCE_DIGEST}} + + - name: Find SBOM manifest digest + run: | + set -e + DIGEST=$(crane manifest ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.ATTESTATION_MANIFEST_DIGEST}} | \ + jq '.layers[] | select(.annotations["in-toto.io/predicate-type"] == "https://spdx.dev/Document") | .digest') + echo "SBOM_DIGEST=${DIGEST}" >> "$GITHUB_ENV" + + - name: Sign SBOM manifest + run: | + cosign sign --yes \ + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.SBOM_DIGEST}} + + cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity-regexp="https://github.com/${{github.repository_owner}}/audit-scanner*" \ + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.SBOM_DIGEST}} + + - name: Download provenance and SBOM files + run: | + set -e + crane blob ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.PROVENANCE_DIGEST}} > audit-scanner-attestation-${{ matrix.arch }}-provenance.json + crane blob ghcr.io/${{github.repository_owner}}/audit-scanner@${{ env.SBOM_DIGEST}} > audit-scanner-attestation-${{ matrix.arch }}-sbom.json + + - name: Sign provenance and SBOM files + run: | + cosign sign-blob --yes \ + --output-certificate audit-scanner-attestation-${{ matrix.arch }}-provenance.cert \ + --output-signature audit-scanner-attestation-${{ matrix.arch }}-provenance.sig \ + audit-scanner-attestation-${{ matrix.arch }}-provenance.json + + cosign verify-blob --certificate audit-scanner-attestation-${{ matrix.arch }}-provenance.cert \ + --signature audit-scanner-attestation-${{ matrix.arch }}-provenance.sig \ + --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity-regexp="https://github.com/${{github.repository_owner}}/audit-scanner*" \ + audit-scanner-attestation-${{ matrix.arch }}-provenance.json + + cosign sign-blob --yes \ + --output-certificate audit-scanner-attestation-${{ matrix.arch }}-sbom.cert \ + --output-signature audit-scanner-attestation-${{ matrix.arch }}-sbom.sig \ + audit-scanner-attestation-${{ matrix.arch }}-provenance.json + + cosign verify-blob --certificate audit-scanner-attestation-${{ matrix.arch }}-sbom.cert \ + --signature audit-scanner-attestation-${{ matrix.arch }}-sbom.sig \ + --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity-regexp="https://github.com/${{github.repository_owner}}/audit-scanner*" \ + audit-scanner-attestation-${{ matrix.arch }}-sbom.json + + - name: Upload SBOMs as artifacts + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: attestation-${{ matrix.arch }} + path: audit-scanner-attestation-${{ matrix.arch }}* diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index f8747036..20e10615 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -31,7 +31,7 @@ jobs: sbom: needs: build - uses: ./.github/workflows/sbom.yml + uses: ./.github/workflows/attestation.yml permissions: packages: write id-token: write diff --git a/.github/workflows/container-image.yml b/.github/workflows/container-image.yml index d342392c..68051e89 100644 --- a/.github/workflows/container-image.yml +++ b/.github/workflows/container-image.yml @@ -65,6 +65,8 @@ jobs: file: ./Dockerfile platforms: linux/amd64, linux/arm64 push: true + sbom: true + provenance: mode=max tags: | ghcr.io/${{github.repository_owner}}/audit-scanner:${{ env.TAG_NAME }} - # Only build amd64 because buildx does not allow multiple platforms when @@ -77,6 +79,8 @@ jobs: context: . file: ./Dockerfile platforms: linux/amd64 + sbom: true + provenance: mode=max outputs: type=docker,dest=/tmp/audit-scanner-image-${{ env.TAG_NAME }}.tar tags: | ghcr.io/${{github.repository_owner}}/audit-scanner:${{ env.TAG_NAME }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1069e81..fb7f1df8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,10 +92,10 @@ jobs: } core.setFailed(`Draft release not found`) - - name: Download SBOM artifacts + - name: Download attestation artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: - pattern: sbom-* + pattern: attestation-* path: ./ merge-multiple: true @@ -118,12 +118,12 @@ jobs: let path = require('path'); let files = [ - 'audit-scanner-sbom-amd64.spdx', - 'audit-scanner-sbom-amd64.spdx.cert', - 'audit-scanner-sbom-amd64.spdx.sig', - 'audit-scanner-sbom-arm64.spdx', - 'audit-scanner-sbom-arm64.spdx.cert', - 'audit-scanner-sbom-arm64.spdx.sig', + 'audit-scanner-attestation-amd64-provenance.cert', + 'audit-scanner-attestation-amd64-provenance.json', + 'audit-scanner-attestation-amd64-provenance.sig', + 'audit-scanner-attestation-arm64-sbom.cert', + 'audit-scanner-attestation-arm64-sbom.json', + 'audit-scanner-attestation-arm64-sbom.sig', "CRDS.tar.gz"] const {RELEASE_ID} = process.env diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml deleted file mode 100644 index 3350e62b..00000000 --- a/.github/workflows/sbom.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Generate SBOMs - -on: - workflow_call: - inputs: - image-digest: - type: string - required: true - -jobs: - sbom: - name: Generate SBOM, sign and attach them to OCI image - strategy: - matrix: - arch: [amd64, arm64] - - permissions: - packages: write - id-token: write - - runs-on: ubuntu-latest - steps: - - name: Install cosign - uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # v3.6.0 - - - name: Install the syft command - uses: kubewarden/github-actions/syft-installer@d94509d260ee11a92b4f65bc0acd297feec24d7f # v3.3.5 - - - name: Install the crane command - uses: kubewarden/github-actions/crane-installer@d94509d260ee11a92b4f65bc0acd297feec24d7f # v3.3.5 - - - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Find platform digest - shell: bash - run: | - set -e - DIGEST=$(crane digest \ - --platform "linux/${{ matrix.arch }}" \ - ghcr.io/${{ github.repository_owner }}/audit-scanner@${{ inputs.image-digest }}) - echo "PLATFORM_DIGEST=${DIGEST}" >> "$GITHUB_ENV" - - - name: Create SBOM file - shell: bash - run: | - syft \ - -o spdx-json \ - --file audit-scanner-sbom-${{ matrix.arch }}.spdx \ - ghcr.io/${{ github.repository_owner }}/audit-scanner@${{ env.PLATFORM_DIGEST }} - - - name: Sign SBOM file - run: | - cosign sign-blob --yes \ - --output-certificate audit-scanner-sbom-${{ matrix.arch }}.spdx.cert \ - --output-signature audit-scanner-sbom-${{ matrix.arch }}.spdx.sig \ - audit-scanner-sbom-${{ matrix.arch }}.spdx - - - name: Attach SBOM file in the container image - shell: bash - run: | - cosign attach \ - sbom --sbom audit-scanner-sbom-${{ matrix.arch }}.spdx \ - ghcr.io/${{ github.repository_owner }}/audit-scanner@${{ env.PLATFORM_DIGEST }} - - - name: Sign SBOM file pushed to OCI registry - shell: bash - run: | - set -e - SBOM_TAG="$(echo ${{ env.PLATFORM_DIGEST }} | sed -e 's/:/-/g').sbom" - - cosign sign --yes \ - ghcr.io/${{github.repository_owner}}/audit-scanner:${SBOM_TAG} - - - name: Upload SBOMs as artifacts - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 - with: - name: sbom-${{ matrix.arch }} - path: audit-scanner-sbom-${{ matrix.arch }}* diff --git a/.github/workflows/sign-image.yml b/.github/workflows/sign-image.yml index 8a98cccf..85f2e15b 100644 --- a/.github/workflows/sign-image.yml +++ b/.github/workflows/sign-image.yml @@ -30,3 +30,8 @@ jobs: run: | cosign sign --yes \ ghcr.io/${{github.repository_owner}}/audit-scanner@${{ inputs.image-digest }} + + cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity-regexp="https://github.com/${{github.repository_owner}}/audit-scanner*" + ghcr.io/${{github.repository_owner}}/audit-scanner@${{ inputs.image-digest }} +