Image Signing with cosign¶
Overview¶
All container images published from this repository are signed using cosign via Sigstore's keyless signing flow. This means:
- No private keys are stored in GitHub Secrets or any KMS.
- Signatures are cryptographically bound to the exact GitHub Actions workflow that produced them.
- All signatures are publicly logged in Rekor, an append-only transparency log.
How the signing works¶
1. GitHub OIDC token¶
When the release-sign-and-attest job runs with id-token: write permission, GitHub issues an OIDC JWT for the workflow invocation.
2. Fulcio certificate¶
cosign exchanges the OIDC token with Fulcio, Sigstore's CA. Fulcio issues a short-lived X.509 certificate (10-minute TTL) where the Subject Alternative Name (SAN) is:
1 | |
This SAN is the cryptographic identity of the signer. It encodes: which organization, which repository, which workflow file, and which Git ref signed this image. Forging this requires compromising GitHub's OIDC infrastructure.
3. Signature stored in OCI registry¶
The signature and certificate are stored as an OCI artifact in GHCR alongside the image, using the cosign OCI layout. When you pull an image, you can also pull its signature:
1 | |
4. Rekor transparency log entry¶
Every signature is also submitted to Rekor, creating a timestamped, tamper-evident log entry. This entry can be independently verified even if the OCI registry is unavailable.
Verifying a signature¶
Basic verification¶
1 2 3 4 5 6 | |
Expected output:
1 2 3 4 5 6 7 8 9 10 11 | |
Verify with regex subject (all releases)¶
1 2 3 4 5 | |
Search Rekor for all signatures of an image¶
1 2 3 4 5 6 | |
Verify SLSA provenance via GitHub CLI¶
1 2 3 | |
What Kyverno checks¶
In the homelab cluster, the verify-webgrip-images Kyverno ClusterPolicy:
- Intercepts every Pod admission request containing a
ghcr.io/webgrip/*image. - Resolves the tag to its digest (obtaining the immutable image reference).
- Verifies the cosign signature against the expected subject and issuer.
- Verifies the CycloneDX SBOM attestation is present and signed by the same identity.
- Mutates the Pod spec to use the digest reference, preventing tag-mutation attacks on running workloads.
- Rejects (when in
Enforcemode) any Pod whose images fail verification.
Troubleshooting¶
MANIFEST_UNKNOWN or 404 Not Found when verifying¶
The image reference must include the full registry hostname. GHCR requires authentication even for public images when verifying signatures:
1 2 | |
no signatures found¶
The image was published before this signing pipeline was introduced. Re-release the image (create a new GitHub Release) to trigger signing.
Certificate subject does not match¶
This happens if: - The workflow file was renamed after signing - A prerelease image is checked against a tag-only policy - The image was built from a fork
Check the actual subject with:
1 | |