Back to Blog
Lesson 49 of the Docker: Containers & Your First Image course
DevOpsSeptember 21, 20266 min read

Image Signing and Notary: Securing Your Docker Supply Chain

Master image signing, verification, and supply chain security using Sigstore Cosign. Learn how to cryptographically protect your Docker images.

securitysigningnotarytrustdockercosignsupply-chain
Close-up of an 'Employees Only' sign on a rustic wooden door secured by a chain.

Previously in this course, we explored how to run vulnerability scans on container assets in Image Scanning for Vulnerabilities: A Practical Docker Guide. While scanning tells you what packages live inside a container today, it does not guarantee that the image running in production is the exact artifact built by your CI/CD pipeline. This lesson adds cryptographic provenance by teaching you how to apply digital signatures to container artifacts, ensuring true supply chain integrity from build server to runtime.

Container registries are public or private directories, but being able to pull an image named myapp:latest doesn't mean it hasn't been maliciously swapped by an attacker who compromised your registry credentials. To establish genuine security, we must adopt image signing and a notary workflow. In this lesson, we will use modern cryptographic tools to sign and verify our container artifacts so that unauthorized or tampered images are rejected before they ever touch our infrastructure.

Understanding Container Supply Chain Trust

When you build and push an image, anyone with read access can pull it. If an attacker gains access to your registry, they can push a modified tag pointing to a malicious binary containing cryptominers or backdoors. Traditional transport layer security (TLS) only protects data in transit between your CLI and the registry; it says nothing about who built the image or whether it was altered at rest.

Digital image signing solves this problem through public-key cryptography. The builder (such as your CI/CD pipeline) generates a cryptographic signature using a private key. When you pull the image, your runtime or deployment script uses the matching public key to verify that signature. If a single byte of the image manifest or layer changes, the signature verification fails instantly.

MechanismWhat It Protects AgainstWhat It Misses
TLS / HTTPSEavesdropping and MITM attacks during upload/downloadCompromised registry storage or malicious insider pushes
Vulnerability ScanningKnown CVEs in OS packages and dependenciesZero-day exploits and malicious payload injection
Image Signing & NotaryTampering, unauthorized builds, and provenance forgeryFlaws in the underlying application source code

Generating Keys and Signing Images with Cosign

Close-up image of keys and scrabble tiles spelling 'safety' on a marble surface.

To implement image signing without heavy, deprecated infrastructure like Docker Notary v1, modern cloud-native workflows rely on Sigstore Cosign. Cosign allows you to sign container images stored in any OCI-compliant registry using keypairs, hardware tokens, or keyless OpenID Connect (OIDC) identities.

First, let's generate a local keypair to understand the mechanics of key-based signing. Run the following command in your terminal:

Bash
cosign generate-key-pair

This command prompts you for a password and creates two files in your current working directory:

  • cosign.key (Your private key—keep this secure and never check it into version control)
  • cosign.pub (Your public key—share this with anyone who needs to verify your images)

Now, let's assume you have built and pushed our course running project image to a registry: registry.example.com/myorg/web-app:v1.0.0. To sign this image using your private key, execute:

Bash
cosign sign --key cosign.key registry.example.com/myorg/web-app:v1.0.0

Cosign calculates a cryptographic hash of the container image manifest, signs that hash with your private key, and pushes the resulting signature back to the container registry alongside the image tag, using a predictable naming convention (appending .sig to the image digest).

Verifying Image Signatures

Signing is only half the battle; enforcement happens at verification time. Before your orchestrator or runtime spins up a container, it must query the registry for the signature and check it against your trusted public key (cosign.pub).

Run the following command to verify the image we just signed:

Bash
cosign verify --key cosign.pub registry.example.com/myorg/web-app:v1.0.0

If the signature is valid and matches the image digest, Cosign outputs a JSON block detailing the signing certificate or key metadata and exits with a status code of 0. If an attacker replaces the image in the registry without your private key, the verification command fails with a non-zero exit code:

TEXT
Error: no matching signatures found

You can integrate this verification step into your deployment scripts, Kubernetes admission controllers (such as Kyverno or Policy-controller), or CI/CD gate checks to block unsigned or invalid images from reaching production environments.

Hands-On Exercise: Sign and Verify a Local Image

Let's practice the complete workflow on your local machine using an insecure local registry or an image reference.

  1. Build a simple test image:
    Bash
    docker build -t localhost:5000/demo-app:latest .
  2. Generate your local signing keys:
    Bash
    cosign generate-key-pair
    # Enter a secure password when prompted
  3. Sign the image reference: (Note: For local testing without pushing to a remote registry, you can sign local images by digest, or spin up a local registry container on port 5000).
    Bash
    # Push your test image to a local registry first if needed, then sign:
    cosign sign --key cosign.key --allow-insecure-registry localhost:5000/demo-app:latest
  4. Verify the signature:
    Bash
    cosign verify --key cosign.pub --allow-insecure-registry localhost:5000/demo-app:latest
  5. Observe the successful JSON output confirming the image's cryptographic provenance.

Common Pitfalls

  • Hardcoding Private Keys in CI/CD Runners: Never store cosign.key directly inside a Dockerfile or git repository. Always inject private keys via encrypted CI/CD secrets managers (GitHub Actions Secrets, GitLab CI Variables, or HashiCorp Vault).
  • Ignoring Image Digests: Tags like latest or v1.0.0 are mutable; an image can be overwritten under the same tag. Cosign signs the immutable digest (e.g., @sha256:abc123...), but your deployment pipelines must also reference images by digest to prevent race conditions where an image is swapped between verification and deployment.
  • Forgetting Registry Permissions: Your signing identity or CI/CD token needs write permissions back to the container registry to upload the .sig and .cert artifacts generated by Cosign.

Frequently Asked Questions

What is the difference between Docker Notary v1 and Sigstore Cosign?

Docker Notary v1 was an early implementation based on The Update Framework (TUF), which proved complex to operate and manage operationally. Sigstore Cosign is the modern industry standard, offering simpler key management, support for keyless signing via OIDC, and native integration with major cloud registries.

Can I sign images without managing my own private keys?

Yes! Cosign supports keyless signing using OpenID Control (OIDC) tokens from providers like GitHub Actions, Google, or Microsoft. When you run cosign sign in a CI/CD pipeline without a key flag, it authenticates via your CI provider and logs the signature transparency event in Sigstore's public ledger, Rekor.

Does image signing prevent application bugs?

No. Cryptographic signing guarantees provenance and integrity—it proves who built the image and that it hasn't been altered since signing. It does not evaluate code quality or stop vulnerabilities, which is why it should be paired with the vulnerability scanning practices we covered previously.

Recap

Team members presenting a project in a modern office setting with a focus on collaboration.

In this lesson, we established rigorous container supply chain security by learning how to cryptographically sign images, push signatures to registries, and verify artifact authenticity using Sigstore Cosign. By enforcing signature verification before deployment, you ensure that only trusted, unaltered artifacts execute in production environments.

Up next, we will explore Multi-Architecture Builds, where you'll learn how to build images that run seamlessly across different CPU architectures like AMD64 and ARM64.

Similar Posts