Docker Content Trust, powered by Notary v1, was supposed to solve container image signing. It did not. The deployment was complex, it required a separate Notary server, and the developer experience was poor. Adoption remained minimal outside of Docker Enterprise environments.
Notary v2, rebranded as Notation, takes a fundamentally different approach. It stores signatures as OCI artifacts alongside images in the registry, eliminating the need for separate infrastructure. It uses standard X.509 certificates, integrating with existing enterprise PKI. And it has backing from Microsoft, AWS, and the broader OCI community.
This guide covers practical implementation of Notation for teams ready to implement container image signing.
Architecture Overview
How Notation Differs From Notary v1
Notary v1 required a separate server to store signature metadata. The server implemented The Update Framework (TUF), which provided sophisticated protection against various attack scenarios but added significant operational complexity.
Notation stores signatures as OCI artifacts using the OCI distribution spec's referrers API. The signature is a separate OCI artifact in the same registry as the image, linked to the image through a reference relationship. No additional infrastructure is needed beyond your existing OCI-compliant registry.
Signature Structure
A Notation signature contains the signed image digest, the signing certificate chain, a timestamp, and optional signed attributes (annotations). The signature format is based on COSE (CBOR Object Signing and Encryption), providing a compact, well-specified signature envelope.
Trust Policy
Notation's trust policy defines which certificates are trusted for which registries and repositories. This allows fine-grained control: you can trust one certificate for production images and a different certificate for development images.
Setting Up Notation
Install the CLI
Notation provides a CLI tool for signing and verifying images. Install it alongside your existing container tooling:
# On Linux
curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.1.0/notation_1.1.0_linux_amd64.tar.gz
tar xzf notation.tar.gz -C /usr/local/bin/ notation
Generate or Import Signing Certificates
Notation uses X.509 certificates. For production use, generate certificates through your enterprise PKI:
# For testing, generate a self-signed certificate
notation cert generate-test --default "myregistry.com"
For production, import certificates from your PKI:
notation key add --default --name production \
--plugin-config "key_id=my-key-id" \
--id "my-key-id" \
--plugin azure-kv
Notation supports plugin-based key management, including Azure Key Vault, AWS KMS, and HashiCorp Vault.
Configure Trust Policy
Create a trust policy that defines which certificates are trusted for signature verification:
{
"version": "1.0",
"trustPolicies": [
{
"name": "production-images",
"registryScopes": ["myregistry.com/production/*"],
"signatureVerification": {
"level": "strict"
},
"trustStores": ["ca:production-ca"],
"trustedIdentities": [
"x509.subject: CN=production-signer,O=MyOrg"
]
}
]
}
This policy requires that images in the production repository are signed by a certificate with the specified subject, issued by the production CA.
Signing Images in CI/CD
Sign After Push
Integrate signing into your CI/CD pipeline immediately after pushing the image:
# Build and push
docker build -t myregistry.com/myapp:v1.0 .
docker push myregistry.com/myapp:v1.0
# Sign the image by digest
notation sign myregistry.com/myapp@sha256:abc123
Always sign by digest, not by tag. Tags are mutable, but digests are immutable. Signing by digest ensures the signature is bound to a specific image.
Automated Signing With KMS
For production pipelines, use a KMS-backed signing key. The private key never leaves the KMS, and signing operations are audited:
notation sign \
--plugin azure-kv \
--plugin-config "key_id=https://myvault.vault.azure.net/keys/notation-key/version" \
myregistry.com/myapp@sha256:abc123
Attach Metadata
Notation supports custom annotations on signatures. Use them to record build metadata:
notation sign \
--user-metadata "buildId=pipeline-123" \
--user-metadata "commit=abc123" \
myregistry.com/myapp@sha256:abc123
Verifying Images
CLI Verification
Verify an image before deployment:
notation verify myregistry.com/myapp@sha256:abc123
Notation checks the signature against the trust policy, verifies the certificate chain, and confirms the signature covers the specified image digest.
Kubernetes Admission Control
Enforce signature verification at deployment time using a Kubernetes admission controller. Ratify (formerly known as the ORAS-project Notation verifier) integrates with Gatekeeper to enforce Notation signatures:
apiVersion: config.ratify.deislabs.io/v1beta1
kind: Verifier
metadata:
name: notation-verifier
spec:
name: notation
artifactTypes: application/vnd.cncf.notary.signature
parameters:
trustPolicyDoc:
version: "1.0"
trustPolicies:
- name: production
registryScopes: ["*"]
signatureVerification:
level: strict
trustStores: ["ca:production"]
trustedIdentities: ["*"]
Verification in CI/CD
Add verification steps to deployment pipelines. Before deploying an image, verify its signature matches your trust policy. This provides defense-in-depth alongside admission controller enforcement.
Registry Support
Notation requires OCI-compliant registries that support the referrers API. Currently supported registries include Azure Container Registry, AWS ECR, Docker Hub, GitHub Container Registry, and Harbor (v2.5 and later).
Verify that your registry supports the OCI referrers API before implementing Notation. Some registries support it through a compatibility fallback that uses a tag-based scheme.
Migration From Docker Content Trust
If you currently use Docker Content Trust (Notary v1), migration to Notation requires re-signing existing images with Notation certificates, updating verification policies from DCT to Notation, deploying Notation-compatible admission controllers, and removing the Notary v1 server infrastructure after migration.
Plan for a transition period where both DCT and Notation signatures coexist. Verify your deployment pipelines can handle both during the migration.
How Safeguard.sh Helps
Safeguard.sh integrates with Notation to verify image signatures as part of its supply chain analysis. It checks that images are signed according to your trust policy, validates signature metadata against build provenance records, and alerts when unsigned or improperly signed images appear in your environment. Safeguard.sh provides a unified view of container integrity across all your registries and signing tools.