2.2. Image Signing and Verification

This lab focuses on understanding and securing image distribution using modern image signing and verification with Cosign. We’ll explore why image verification is important and how to use Cosign to ensure image integrity and authenticity.

Understanding the Problem

Let’s start with a common pull:

docker pull alpine:edge

This command will pull the Alpine image tagged as edge. The corresponding image can be found here on Docker Hub .

If no tag is specified, Docker will pull the image with the latest tag.

$ docker pull alpine:edge

edge: Pulling from library/alpine
e587fa4f6e1f: Pull complete
Digest: sha256:e5ab6f0941eb01c41595d35856f16215021a941e9893501d632ed4c0ee4e53a6
Status: Downloaded newer image for alpine:edge

Now run a new container from the image and check songlaa.com:

docker run --rm -it alpine:edge nc -zv songlaa.com 443

Pulling by tag is easy and convenient. However, tags are mutable, and the same tag can refer to different images over time. For example, you can add updates to an image and push the updated image using the same tag as a previous version of the image. This scenario where a single tag points to multiple versions of an image can lead to bugs and vulnerabilities in your production environments. Here you cannot be sure if the nc binary is a specific version with a bug or not.

This is why pulling by digest is such a powerful operation. Thanks to the content-addressable storage model used by container images, we can target pulls to specific image contents by pulling by digest.

Pull the Alpine image with a specific digest:

docker pull alpine@sha256:115729ec5cb049ba6359c3ab005ac742012d92bbaa5b8bc1a878f1e8f62c0cb8

While pulling by digest ensures you get the exact same image content, it doesn’t provide guarantees about:

  • Who published the image
  • When it was published (freshness)
  • Whether the image has been tampered with

This is where image signing comes in.

Introduction to Cosign

Cosign is a tool for signing and verifying container images. It’s part of the Sigstore project, which provides a new standard for signing, verifying, and protecting software.

Key advantages of Cosign:

  • Simple and lightweight: Easy to use with minimal setup
  • Keyless signing: Support for OIDC-based keyless signing (no key management needed)
  • OCI-native: Stores signatures in OCI registries alongside images

Verifying Signed Images

As of 2025, most Docker Hub official images (including Alpine) are not yet signed with Cosign. Docker Hub previously used Docker Content Trust (DCT) based on Notary, but that system is being deprecated. The container ecosystem is transitioning to modern signing solutions like Cosign, but adoption is still in progress for many base images. You are left with using digests.

To show how to verify a signature of an image, we pull alpine from our repository and verify if it was signed by our throwaway e-mail address.

cosign verify --certificate-oidc-issuer https://accounts.google.com \
  --certificate-identity [email protected] \
   quay.io/songlaa/alpine:latest

If the image is properly signed, you’ll see output showing the signature verification succeeded, along with details about who signed it.

By contrast let’s check if the official Docker Alpine images are signed:

cosign tree alpine:latest

This will show that Alpine images currently have no Cosign signatures attached.

For production use, consider:

  • Using images from registries that support Cosign signing (like GitHub Container Registry, Google Container Registry)
  • Building your own base images and signing them, maybe even using your private Container registry

Understanding the Verification Process

When you run cosign verify, several things happen:

  1. Signature Discovery: Cosign looks for signatures stored in the registry
  2. Certificate Validation: Validates the signing certificate against the specified OIDC issuer
  3. Identity Verification: Checks that the certificate identity matches your expectations
  4. Signature Verification: Cryptographically verifies the image hasn’t been tampered with

The --certificate-identity and --certificate-identity-regexp flags ensure the signature comes from a trusted source (e.g., a specific GitHub repository or organization). The --certificate-oidc-issuer flag ensures the signature was created through a trusted OIDC provider.

Signing Your Own Images

While we won’t create signatures in this lab, here’s how you would sign an image in a CI/CD pipeline:

# Using keyless signing with OIDC (recommended for CI/CD)
cosign sign ghcr.io/yourorg/yourimage:tag

# Or using a key pair (for manual signing)
cosign generate-key-pair
cosign sign --key cosign.key ghcr.io/yourorg/yourimage:tag

Keyless signing is particularly powerful because:

  • No private keys to manage or secure
  • Uses your existing OIDC identity (GitHub, Google, etc.)
  • Transparency log ensures accountability
  • Certificates are short-lived and automatically managed

Excerpt: Docker Official Images (DOI)

All images in Docker Hub under the library organization (currently viewable at: https://hub.docker.com/explore/ ) are deemed “Official Images.” These images undergo a rigorous, open-source review process to ensure they follow best practices. These best practices include being lean and having clearly written Dockerfiles. For these reasons, it is strongly recommended that you use official images whenever possible.

Official images can be pulled with just their name and tag. You do not have to precede the image name with library/ or any other repository name.