@@ -22,10 +22,13 @@ jobs:
2222 permissions :
2323 contents : read
2424 packages : write # Pushing images to ghcr.io
25+ id-token : write # Signing images with cosign
2526 runs-on : ${{ matrix.runner }}
2627 steps :
2728 - name : Checkout code
2829 uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
30+ - name : Install cosign
31+ uses : sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
2932 - name : Set up Docker Buildx
3033 uses : docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
3134 - name : Login to GitHub Container Registry
5356 tags : ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}
5457 outputs : |
5558 type=image,push=true,push-by-digest=true,name-canonical=true
59+
60+ # We need to disable the new bundle format enabled by default since
61+ # cosign v3.x.x because some verification tools (e.g. slsactl, hauler and old
62+ # cosign) are not able to properly verify the signatures using this
63+ # new format
64+ - name : Sign container image with cosign v2 signature format
65+ run : |
66+ cosign sign --yes --new-bundle-format=false --use-signing-config=false \
67+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ steps.build-image.outputs.digest }}
68+
69+ - name : Sign container image with cosign v3 signature format
70+ run : |
71+ cosign sign --yes --new-bundle-format=true --use-signing-config=true \
72+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ steps.build-image.outputs.digest }}
73+
74+ - name : Verify container image signature
75+ run : |
76+ cosign verify \
77+ --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
78+ --certificate-identity="https://github.com/${{github.repository_owner}}/runtime-enforcer/.github/workflows/container-build.yml@${{ github.ref }}" \
79+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ steps.build-image.outputs.digest }}
80+
5681 - name : Export digest
5782 run : |
5883 mkdir -p ${{ runner.temp }}/digests
7095 needs : [build]
7196 permissions :
7297 packages : write # Pushing multi-arch manifest to ghcr.io
98+ id-token : write # Signing images with cosign
7399 strategy :
74100 matrix :
75101 component : [operator, daemon]
80106 path : ${{ runner.temp }}/digests
81107 pattern : digest-${{ matrix.component }}-*
82108 merge-multiple : true
83-
109+ - name : Install cosign
110+ uses : sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
84111 - name : Login to GHCR
85112 uses : docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
86113 with :
@@ -105,13 +132,55 @@ jobs:
105132 id : create-manifest
106133 working-directory : ${{ runner.temp }}/digests
107134 run : |
135+ set -e
108136 amd64_digest=$(cat ${{ matrix.component }}-amd64.txt)
109137
138+ # Create the manifest locally
139+ docker buildx imagetools create \
140+ -t ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }} \
141+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${amd64_digest} \
142+ --dry-run > expected-multi-arch-manifest.json
143+
144+ # Create the manifest and push it
110145 docker buildx imagetools create \
111146 -t ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }} \
112147 ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${amd64_digest} \
113148
114- docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }}
149+ # The previous command is NOT printing the digest of the multi-arch manifest, we have to obtain it by
150+ # fetching it from the OCI registry and **verify** its contents before signing it
151+
152+ # Fetch the multi arch manifest
153+ docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}:${{ env.TAG_NAME }} \
154+ --raw > multi-arch-manifest.json
155+ multi_arch_manifest_digest="sha256:$(sha256sum multi-arch-manifest.json | awk '{print $1}')"
156+
157+ # Compare the contents of the manifest we previously computed and the actual one.
158+ # Use jq to sort the contents and build a compact output (removing useless whitespaces),
159+ # this is done to ensure the JSON documents have the same structure.
160+ expected_digest="sha256:$(jq -S -c . expected-multi-arch-manifest.json | sha256sum | awk '{print $1}')"
161+ actual_digest="sha256:$(jq -S -c . multi-arch-manifest.json | sha256sum | awk '{print $1}')"
162+
163+ if [ "$expected_digest" != "$actual_digest" ]; then
164+ echo "Error: digests do not match!"
165+ exit 1
166+ fi
167+
168+ # Sign the multi-arch image manifest using cosign v2 format
169+ cosign sign --yes --new-bundle-format=false --use-signing-config=false \
170+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${multi_arch_manifest_digest}
171+
172+ # Sign the multi-arch image manifest using cosign v3 format
173+ cosign sign --yes --new-bundle-format=true --use-signing-config=true \
174+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${multi_arch_manifest_digest}
175+
176+ echo "MULTI_ARCH_MANIFEST_DIGEST=$multi_arch_manifest_digest" >> $GITHUB_ENV
177+
178+ - name : Verify multi-arch manifest signature
179+ run : |
180+ cosign verify \
181+ --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
182+ --certificate-identity="https://github.com/${{github.repository_owner}}/runtime-enforcer/.github/workflows/container-build.yml@${{ github.ref }}" \
183+ ghcr.io/${{ github.repository_owner }}/runtime-enforcer/${{ matrix.component }}@${{ env.MULTI_ARCH_MANIFEST_DIGEST}}
115184
116185 attest :
117186 needs : [merge]
0 commit comments