Skip to content

Merge pull request #45 from neuvector/crd-lifecycle-rfc #20

Merge pull request #45 from neuvector/crd-lifecycle-rfc

Merge pull request #45 from neuvector/crd-lifecycle-rfc #20

name: Build, Sign, and Generate SBOM, Attestation & Provenance
on:
workflow_call:
inputs:
version:
type: string
push:
branches:
- "main"
jobs:
build:
strategy:
matrix:
component: [operator, daemon]
arch: [amd64]
include:
- arch: amd64
runner: ubuntu-latest
platform: linux/amd64
permissions:
contents: read
packages: write # Pushing images to ghcr.io
id-token: write # Signing images with cosign
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
with:
images: ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}
- name: Build and push ${{ matrix.component }} for ${{ matrix.arch }}
id: build-image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./package/Dockerfile.${{ matrix.component }}
labels: ${{ steps.meta.outputs.labels }}
platforms: ${{ matrix.platform }}
push: true
sbom: true
provenance: mode=max
tags: ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}
outputs: |
type=image,push=true,push-by-digest=true,name-canonical=true
# We need to disable the new bundle format enabled by default since
# cosign v3.x.x because some verification tools (e.g. slsactl, hauler and old
# cosign) are not able to properly verify the signatures using this
# new format
- name: Sign container image with cosign v2 signature format
run: |
cosign sign --yes --new-bundle-format=false --use-signing-config=false \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ steps.build-image.outputs.digest }}
- name: Sign container image with cosign v3 signature format
run: |
cosign sign --yes --new-bundle-format=true --use-signing-config=true \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ steps.build-image.outputs.digest }}
- name: Verify container image signature
run: |
cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity="https://github.com/${{github.repository_owner}}/runtime-enforcer/.github/workflows/container-build.yml@${{ github.ref }}" \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ steps.build-image.outputs.digest }}
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build-image.outputs.digest }}"
echo "${digest}" > "${{ runner.temp }}/digests/${{ matrix.component }}-${{ matrix.arch }}.txt"
- name: Upload digest
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: digest-${{ matrix.component }}-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*.txt
retention-days: 1
merge:
runs-on: ubuntu-latest
needs: [build]
permissions:
packages: write # Pushing multi-arch manifest to ghcr.io
id-token: write # Signing images with cosign
strategy:
matrix:
component: [operator, daemon]
steps:
- name: Download all digests
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: ${{ runner.temp }}/digests
pattern: digest-${{ matrix.component }}-*
merge-multiple: true
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- name: Login to GHCR
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Retrieve tag name (main)
if: ${{ startsWith(github.ref, 'refs/heads/main') }}
run: |
echo TAG_NAME=latest >> $GITHUB_ENV
- name: Retrieve tag name (release)
if: ${{ !startsWith(github.ref, 'refs/heads/main') }}
run: |
echo TAG_NAME=${{ inputs.version }} >> $GITHUB_ENV
- name: Create and push multi-arch manifest for ${{ matrix.component }}
id: create-manifest
working-directory: ${{ runner.temp }}/digests
run: |
set -e
amd64_digest=$(cat ${{ matrix.component }}-amd64.txt)
# Create the manifest locally
docker buildx imagetools create \
-t ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }} \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${amd64_digest} \
--dry-run > expected-multi-arch-manifest.json
# Create the manifest and push it
docker buildx imagetools create \
-t ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }} \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${amd64_digest} \
# The previous command is NOT printing the digest of the multi-arch manifest, we have to obtain it by
# fetching it from the OCI registry and **verify** its contents before signing it
# Fetch the multi arch manifest
docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }} \
--raw > multi-arch-manifest.json
multi_arch_manifest_digest="sha256:$(sha256sum multi-arch-manifest.json | awk '{print $1}')"
# Compare the contents of the manifest we previously computed and the actual one.
# Use jq to sort the contents and build a compact output (removing useless whitespaces),
# this is done to ensure the JSON documents have the same structure.
expected_digest="sha256:$(jq -S -c . expected-multi-arch-manifest.json | sha256sum | awk '{print $1}')"
actual_digest="sha256:$(jq -S -c . multi-arch-manifest.json | sha256sum | awk '{print $1}')"
if [ "$expected_digest" != "$actual_digest" ]; then
echo "Error: digests do not match!"
exit 1
fi
# Sign the multi-arch image manifest using cosign v2 format
cosign sign --yes --new-bundle-format=false --use-signing-config=false \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${multi_arch_manifest_digest}
# Sign the multi-arch image manifest using cosign v3 format
cosign sign --yes --new-bundle-format=true --use-signing-config=true \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${multi_arch_manifest_digest}
echo "MULTI_ARCH_MANIFEST_DIGEST=$multi_arch_manifest_digest" >> $GITHUB_ENV
- name: Verify multi-arch manifest signature
run: |
cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity="https://github.com/${{github.repository_owner}}/runtime-enforcer/.github/workflows/container-build.yml@${{ github.ref }}" \
ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ env.MULTI_ARCH_MANIFEST_DIGEST}}
attest:
needs: [merge]
if: ${{ inputs.version }}
uses: ./.github/workflows/attestation.yml
permissions:
id-token: write # Generating OIDC token for Sigstore/Cosign authentication
packages: write # Pushing attestations to ghcr.io
strategy:
matrix:
component: [operator, daemon]
arch: [amd64]
include:
- arch: amd64
runner: ubuntu-latest
platform: linux/amd64
with:
component: ${{ matrix.component }}
arch: ${{ matrix.arch }}