Skip to content

Commit de5b905

Browse files
holyspectralflavio
andcommitted
ci: ensure all the images we built are signed with cosign
Prior to this commit, we did NOT sign the container images built by our pipelines. We only signed the artifacts attached to the final GitHub release (like the SBOM and the provenance file). With this commit we sign (using both cosign v2 and v3 formats): - architecture specific images - multi-arc image manifest Currently we support only amd64 images. Co-authored-by: Flavio Castelli <fcastelli@suse.com> Signed-off-by: Sam Wang (holyspectral) <sam.wang@suse.com>
1 parent 564b616 commit de5b905

1 file changed

Lines changed: 71 additions & 2 deletions

File tree

.github/workflows/container-build.yml

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
@@ -53,6 +56,28 @@ jobs:
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
@@ -70,6 +95,7 @@ jobs:
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]
@@ -80,7 +106,8 @@ jobs:
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

Comments
 (0)