ci: sign the released images with cosign (#963)

Published images carry no signature, so there is no way for a user to verify
that the image they pull was built by this workflow from this repository.

Sign them keyless, using the Github OIDC token, so verification needs no key
material on either side. The manifest is signed by digest rather than by tag:
every tag produced for a release points at the same digest, and the same
digest is valid in both registries the build pushes to, so signing each tag
would only repeat the same signature.

Verify with:

    cosign verify ghcr.io/donkie/spoolman:<version> \
      --certificate-identity-regexp '^https://github\.com/Donkie/Spoolman/\.github/workflows/ci\.yml@refs/' \
      --certificate-oidc-issuer https://token.actions.githubusercontent.com

Assisted-By: Claude <noreply@anthropic.com>

Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
Aleksei Sviridkin authored and GitHub committed 2026-07-21 22:07:57 +02:00
1 parent f7da164599
commit fc606ec8fc
1 file changed
+28
+28
View File
@@ -393,6 +393,8 @@ jobs:
runs-on: ubuntu-latest
permissions:
packages: write
# Lets cosign fetch an OIDC token from Github and sign without a private key.
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -452,7 +454,15 @@ jobs:
echo "GIT_COMMIT=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
echo "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$GITHUB_ENV"
- name: Install cosign
uses: sigstore/cosign-installer@v4.1.2
with:
# Installer v4+ verifies cosign v3 downloads via the Sigstore bundle
# format; pin an explicit, current cosign release for reproducibility.
cosign-release: v3.1.1
- name: Build and push Docker images
id: build
uses: docker/build-push-action@v6.7.0
with:
context: .
@@ -469,6 +479,24 @@ jobs:
type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-amd64
type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-arm64
type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-armv7
# Sign the manifest by digest rather than by tag: all the tags above point
# at the same digest, and the same digest is valid in both registries, so
# signing every tag would just repeat the same signature.
- name: Sign the published images
env:
DIGEST: ${{ steps.build.outputs.digest }}
TAGS: |
${{ steps.meta-ghcr.outputs.tags }}
${{ steps.meta-dh.outputs.tags }}
run: |
echo "${TAGS}" | while read -r tag; do
[ -n "${tag}" ] || continue
echo "${tag%:*}"
done | sort --unique | while read -r repository; do
echo "Signing ${repository}@${DIGEST}"
cosign sign --yes "${repository}@${DIGEST}"
done
#
# Create Github Release if CI was triggered by a tag
#