A Comprehensive Guide for Notation & Docker Image Signatures

As the adoption of containerization grows, so does the need for securing containerized applications. Containers allow developers to package and distribute their applications along with all dependencies, ensuring consistency across different environments. However, this ease of distribution also presents security challenges. One critical aspect is verifying the integrity and authenticity of Docker images.

This is where Notation comes into play. I had the opportunity at work to come across it, and I wanted to share with you what I consider to be a future cybersecurity standard, if it isn't one already.

This article will delve into what Notation is, why it should be used, and provide a step-by-step guide to setting it up and using it to sign Docker images.

What is Notation?

Notation is an open-source tool developed under the Notary Project, designed to sign and verify OCI (Open Container Initiative) artifacts, such as Docker images and Helm charts. Written in Go, Notation enables developers to ensure that these artifacts have not been tampered with and originate from a trusted source. This article will explore what Notation is, its benefits, and provide a step-by-step guide on how to set it up and use it for signing Docker images.

Why Use Notation?

Security: Signing an artifact ensures that it has not been tampered with and originates from a trusted source. This is crucial for securing the supply chain in a containerized environment.

Trust: Verifying a signature before pulling an image into a production environment ensures that only trusted and verified images are deployed.

Compliance: Many industries require proof that the software being used is authentic and has not been altered. Signing images can help meet these compliance requirements.

OCI Artifacts and Registries

Before diving into Notation, it's essential to understand the concept of OCI artifacts and registries.

OCI Artifacts

An OCI artifact is any content that conforms to the OCI specifications. Docker images are the most common type of OCI artifact. They consist of layers that represent filesystem changes and a manifest that describes the layers.

OCI Registries

OCI registries are repositories where OCI artifacts are stored. They are often used to distribute Docker images. Examples include Docker Hub, Amazon Elastic Container Registry (ECR), and Azure Container Registry (ACR). These registries also support storing and distributing other types of OCI artifacts like Helm charts.

How Notation Works

Notation works by creating a cryptographic signature for an OCI artifact. This signature is then attached to the artifact and stored alongside it in the OCI registry. When another user pulls the artifact, they can verify the signature to ensure that the artifact is authentic and has not been altered. Using a tool like ORAS also allows a user to transfer the OCI artifact along with its signature to another registry.

Signature Generation

When you sign an artifact with Notation, the tool uses a private key to generate a cryptographic signature. This signature is a unique hash that represents the contents of the artifact at the time of signing.

Attaching the Signature

After generating the signature, Notation attaches it to the artifact. The signature is stored as an OCI artifact itself in the registry, associated with the original artifact. This makes it possible for others to verify the artifact using the corresponding public key.

Verifying the Signature

When an artifact is pulled from the registry, Notation can verify its signature using the public key. If the signature is valid, it means the artifact has not been tampered with and is from a trusted source.

Setting Up Notation

1. Install Notation CLI:

You can download it from the official Notation GitHub repository or by checking their Website

Setup the OCI-compliant registry, if you want a quick one to setup you can use the regsitry from Docker Hub :

docker run -d -p 5001:5000 -e REGISTRY_STORAGE_DELETE_ENABLED=true --name registry registry

2. Generate a test key and self-signed certificate:

notation cert generate-test --default "myfirstsign"

This command generates a test key along with a self-signed X.509 certificate and stores them locally. The --default flag sets this key as the default one for signing operations.

Create and import a trust policy

Since Notation has its own trust store, you need to configure a trust policy where you can specify trusted identities that sign the artifacts and the level of signature verification to apply. A simple one would be :

{
	    "version": "1.0",
	    "trustPolicies": [
	        {
	            "name": "simple-image-policy",
	            "registryScopes": [ "*" ],
	            "signatureVerification": {
	                "level" : "strict"
	            },
	            "trustStores": [ "ca:myfirstsign" ],
	            "trustedIdentities": [
	                "*"
	            ]
	        }
	    ]
	}

registryScopes : determines which trust policy is applicable for the given artifact (we are using a wildcard here so the policy applies to every artifact)

signatureVerification : strict means we check for every validations of the verification process (Integrity, Authenticity, Authentic timestamp, Expiry and Revocation check) if one of them fails, the verification fails. This is fully customizable and you can override the different presets to match your needs.

trustStores : specifies a set of one or more named trust stores.

trustedIdentities : specifies a set of identities that the user trusts. A wildcard means you trust any identity (signing certificate) issued by the CA(s) in trustStore.

If you want additionnal informations regarding trust-policy I strongly suggest checking their well-detailed github.

Let's import it :

notation policy import ./yourtrustpolicy.json

Signing and Verifying

1. Build and push the Docker Image to the Registry:

docker build -t <your_registry>/myfirstimage:1.0
docker push <your_registry>/myfirstimage:1.0

The output of this command will provide the digest (a unique identifier) of the image in the registry in the format sha256:xyz. It's important to keep this digest, as using it is a better security practice than relying on the tag (e.g., 1.0), since the digest is immutable.

You can always get the digest value using :

docker inspect --format='{{index .RepoDigests 0}}' <your_registry>/myfirstimage:1.0

2. Sign the Docker Image

notation sign <your_registry>/myimage:1.0

This command generates a signature for the image and pushes it to the registry alongside the image, there is no key specified so Notation uses the default one.

3. Verify the Signature

notation verify <your_registry>/myimage:1.0

If the signature is valid, Notation will confirm that the image is authentic and has not been altered.

Advanced Features to Explore

Automating Signature Verification

You can automate the signature verification process in your CI/CD pipeline to ensure that only signed images are deployed to production. This can be done by integrating Notation with your CI/CD tool, such as Jenkins or GitHub Actions.

Signing Other OCI Artifacts

While this guide focuses on Docker images, Notation can also be used to sign other OCI artifacts like Helm charts. The process is similar, but the artifact type will differ.

Trusted CA and Internal PKI

For organizations looking to enhance security and streamline their container management processes, integrating Notation with a trusted Certificate Authority (CA) and an internal Public Key Infrastructure (PKI) can provide significant advantages. By doing so, you can ensure that all signatures are backed by a trusted CA, making it easier to manage and validate signatures across different environments.

1. Using a Trusted CA with Notation

In a production environment, using certificates from a trusted CA ensures that your signatures are recognized and trusted by external entities. This integration involves generating a signing certificate from a CA, configuring Notation to use this certificate, and distributing the corresponding public certificate to the necessary parties for signature verification.

2. Developing Notation Plugins for Internal PKI Integration

For organizations with an established internal PKI, developing custom plugins for Notation can offer seamless integration and automation of the signing and verification processes. These plugins can connect Notation directly with your PKI, automating tasks such as certificate management, revocation checking, and centralized logging of signature events.

In Summary

Securing Docker images is a critical aspect of maintaining a safe and reliable containerized environment. Notation provides an easy-to-use tool for signing and verifying OCI artifacts, ensuring the authenticity and integrity of Docker images.

By following the steps outlined in this guide, you can set up Notation, sign Docker images, and verify their signatures. This not only enhances security but also fosters trust in your containerized applications. While this guide covers the basics, there are many possibilities for extending and automating the use of Notation in your development workflow.

AFZ-logoJeeZy Blog

© 2024-2025 JeeZy Blog