Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article is part of a series on ensuring the integrity and authenticity of container images and other Open Container Initiative (OCI) artifacts. For the complete picture, start with the overview, which explains why signing matters and outlines the various scenarios.
Signing and verifying container images by using a certificate from a trusted certificate authority (CA) is a valuable security practice. It helps you responsibly identify, authorize, and validate the identity of both the publisher of a container image and the container image itself. Trusted CAs such as GlobalSign, DigiCert, and others play a crucial role in:
- Validating a user's or organization's identity.
- Maintaining the security of digital certificates.
- Revoking certificates immediately upon any risk or misuse.
Here are some essential components that help you to sign and verify container images by using a certificate from a trusted CA:
- Notation is an open-source supply-chain security tool developed by the Notary Project community and backed by Microsoft. It supports signing and verifying container images and other artifacts.
- Azure Key Vault is a cloud-based service for managing cryptographic keys, secrets, and certificates. It helps you securely store and manage a certificate with a signing key.
- The Key Vault plug-in (
notation-azure-kv) is an extension of Notation. It uses the keys stored in Key Vault for signing and verifying the digital signatures of container images and artifacts. - Azure Container Registry is a private registry that you can use to attach signatures to container images, along with storing and managing these images.
When you verify an image, the signature is used to validate the integrity of the image and the identity of the signer. This validation helps ensure that container images aren't tampered with and are from a trusted source.
In this article, you learn how to:
- Install the Notation command-line interface (CLI) and the Key Vault plug-in.
- Create or import a CA-issued certificate in Key Vault.
- Build and push a container image by using Container Registry tasks.
- Sign a container image by using the Notation CLI and the Key Vault plug-in.
- Verify a container image signature by using the Notation CLI.
- Use timestamping.
Prerequisites
- Create or use a container registry for storing container images and signatures.
- Create or use a key vault. We recommend that you create a new key vault for storing certificates only.
- Install and configure the latest Azure CLI version, or run commands in Azure Cloud Shell.
Install the Notation CLI and Key Vault plug-in
Install Notation v1.3.2 in a Linux AMD64 environment. To download the package for other environments, follow the Notation installation guide.
# Download, extract, and install curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.3.2/notation_1.3.2_linux_amd64.tar.gz tar xvzf notation.tar.gz # Copy the Notation CLI to the desired bin directory in PATH, for example cp ./notation /usr/local/binInstall Key Vault plug-in (
notation-azure-kv) v1.2.1 in a Linux AMD64 environment.Note
You can find the URL and SHA256 checksum for the plug-in on the plug-in's release page.
notation plugin install --url https://github.com/Azure/notation-azure-kv/releases/download/v1.2.1/notation-azure-kv_1.2.1_linux_amd64.tar.gz --sha256sum 67c5ccaaf28dd44d2b6572684d84e344a02c2258af1d65ead3910b3156d3eaf5List the available plug-ins and confirm that the
notation-azure-kvplug-in with version1.2.1is included in the list:notation plugin ls
Configure environment variables
This article uses environment variables for convenience in the configuration of Key Vault and Container Registry. Update the values of these environment variables for your specific resources.
Configure environment variables for Key Vault and certificates:
AKV_SUB_ID=myAkvSubscriptionId AKV_RG=myAkvResourceGroup AKV_NAME=myakv # Name of the certificate created or imported in Key Vault CERT_NAME=wabbit-networks-io # X.509 certificate subject CERT_SUBJECT="CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US"Configure environment variables for Container Registry and images:
ACR_SUB_ID=myAcrSubscriptionId ACR_RG=myAcrResourceGroup # Name of the existing registry example: myregistry.azurecr.io ACR_NAME=myregistry # Existing full domain of the container registry REGISTRY=$ACR_NAME.azurecr.io # Container name inside the container registry where the image will be stored REPO=net-monitor TAG=v1 # Source code directory that contains the Dockerfile to build IMAGE_SOURCE=https://github.com/wabbit-networks/net-monitor.git#main
Sign in by using the Azure CLI
az login
For more information, see Authenticate to Azure by using the Azure CLI.
Create or import a CA-issued certificate in Key Vault
Understand certificate requirements
When you're creating certificates for signing and verification, the certificates must meet the Notary Project certificate requirements.
Here are the requirements for root and intermediate certificates:
- The
basicConstraintsextension must be present and marked ascritical. TheCAfield must be set totrue. - The
keyUsageextension must be present and marked ascritical. Bit positions forkeyCertSignmust be set.
Here are the requirements for certificates that a CA issues:
- X.509 certificate properties:
- Subject must contain common name (
CN), country/region (C), state or province (ST), and organization (O). This article uses$CERT_SUBJECTas the subject. - X.509 key usage flag must be
DigitalSignatureonly. - Extended Key Usages (EKUs) must be empty or
1.3.6.1.5.5.7.3.3(for code signing).
- Subject must contain common name (
- Key properties:
- The
exportableproperty must be set tofalse. - Select a supported key type and size from the Notary Project specification.
- The
Important
To ensure successful integration with Image Integrity, the content type of certificate should be set to PEM.
This guide uses version 1.0.1 of the Key Vault plug-in. Prior versions of the plug-in had a limitation that required a specific certificate order in a certificate chain. Version 1.0.1 of the plug-in doesn't have this limitation, so we recommend that you use version 1.0.1 or later.
Create a CA-issued certificate
Create a certificate signing request (CSR) by following the instructions in Create and merge a certificate signing request in Key Vault.
When you're merging the CSR, make sure that you merge the entire chain that you brought back from the CA vendor.
Import the certificate in Key Vault
To import the certificate:
- Get the certificate file from CA vendor with the entire certificate chain.
- Import the certificate into Key Vault by following the instructions in Import a certificate in Azure Key Vault.
If the certificate doesn't contain a certificate chain after you create or import it, you can obtain the intermediate and root certificates from your CA vendor. You can ask your vendor to provide you with a PEM file that contains the intermediate certificates (if any) and root certificate. You can then use this file when you're signing container images.
Sign a container image by using the Notation CLI and Key Vault plug-in
When you're working with Container Registry and Key Vault, it's essential to grant the appropriate permissions to help ensure secure and controlled access. You can authorize access for various entities, such as user principals, service principals, or managed identities, depending on your specific scenarios. In this article, the access is authorized for a signed-in Azure user.
Authorize access to Container Registry
For registries enabled for Microsoft Entra attribute-based access control (ABAC), the Container Registry Repository Reader and Container Registry Repository Writer roles are required for building and signing container images in Container Registry.
For registries not enabled for ABAC, the AcrPull and AcrPush roles are required.
For more information on ABAC, see Microsoft Entra attribute-based access control for repository permissions.
Set the subscription that contains the Container Registry resource:
az account set --subscription $ACR_SUB_IDAssign the roles:
USER_ID=$(az ad signed-in-user show --query id -o tsv) ROLE1="Container Registry Repository Reader" # For ABAC-enabled registries. Otherwise, use "AcrPull" for non-ABAC-enabled registries. ROLE2="Container Registry Repository Writer" # For ABAC-enabled registries. Otherwise, use "AcrPush" for non-ABAC-enabled registries. az role assignment create --role "$ROLE1" --role "$ROLE2" --assignee $USER_ID --scope "/subscriptions/$ACR_SUB_ID/resourceGroups/$ACR_RG/providers/Microsoft.ContainerRegistry/registries/$ACR_NAME"
Build and push container images to Container Registry
Authenticate to your container registry by using your individual Azure identity:
az acr login --name $ACR_NAMEImportant
If you have Docker installed on your system and you used
az acr loginordocker loginto authenticate to your container registry, your credentials are already stored and available to Notation. In this case, you don't need to runnotation loginagain to authenticate to your container registry. To learn more about authentication options for Notation, see Authenticate with OCI-compliant registries.Build and push a new image by using Container Registry tasks. Always use
digestto identify the image for signing, because tags are mutable and can be overwritten.DIGEST=$(az acr build -r $ACR_NAME -t $REGISTRY/${REPO}:$TAG $IMAGE_SOURCE --no-logs --query "outputImages[0].digest" -o tsv) IMAGE=$REGISTRY/${REPO}@$DIGESTIn this article, if the image is already built and is stored in the registry, the tag serves as an identifier for that image for convenience:
IMAGE=$REGISTRY/${REPO}@$TAG
Authorize access to Key Vault
This section explores two options for authorizing access to Key Vault.
Use Azure RBAC (recommended)
Set the subscription that contains the Key Vault resource:
az account set --subscription $AKV_SUB_IDAssign the roles.
If the certificate contains the entire certificate chain, the principal must be assigned with the following roles:
Key Vault Secrets Userfor reading secretsKey Vault Certificates Userfor reading certificatesKey Vault Crypto Userfor signing operations
USER_ID=$(az ad signed-in-user show --query id -o tsv) az role assignment create --role "Key Vault Secrets User" --role "Key Vault Certificates User" --role "Key Vault Crypto User" --assignee $USER_ID --scope "/subscriptions/$AKV_SUB_ID/resourceGroups/$AKV_RG/providers/Microsoft.KeyVault/vaults/$AKV_NAME"If the certificate doesn't contain the chain, the principal must be assigned with the following roles:
Key Vault Certificates Userfor reading certificatesKey Vault Crypto Userfor signing operations
USER_ID=$(az ad signed-in-user show --query id -o tsv) az role assignment create --role "Key Vault Certificates User" --role "Key Vault Crypto User" --assignee $USER_ID --scope "/subscriptions/$AKV_SUB_ID/resourceGroups/$AKV_RG/providers/Microsoft.KeyVault/vaults/$AKV_NAME"
To learn more about Key Vault access with Azure role-based access control (RBAC), see Provide access to Key Vault keys, certificates, and secrets by using Azure role-based access control.
Use an access policy (legacy)
To set the subscription that contains the Key Vault resources, run the following command:
az account set --subscription $AKV_SUB_ID
If the certificate contains the entire certificate chain, the principal must be granted the key permission Sign, the secret permission Get, and the certificate permission Get. To grant these permissions to the principal, use this command:
USER_ID=$(az ad signed-in-user show --query id -o tsv)
az keyvault set-policy -n $AKV_NAME --key-permissions sign --secret-permissions get --certificate-permissions get --object-id $USER_ID
If the certificate doesn't contain the chain, the principal must be granted the key permission Sign and the certificate permission Get. To grant these permissions to the principal, use this command:
USER_ID=$(az ad signed-in-user show --query id -o tsv)
az keyvault set-policy -n $AKV_NAME --key-permissions sign --certificate-permissions get --object-id $USER_ID
To learn more about assigning a policy to a principal, see Assign a Key Vault access policy (legacy).
Sign container images by using the certificate in Key Vault
Get the key ID for a certificate. A certificate in Key Vault can have multiple versions. The following command gets the key ID for the latest version of the
$CERT_NAMEcertificate:KEY_ID=$(az keyvault certificate show -n $CERT_NAME --vault-name $AKV_NAME --query 'kid' -o tsv)Sign the container image with the CBOR Object Signing and Encryption (COSE) signature format by using the key ID.
If the certificate contains the entire certificate chain, run the following command:
notation sign --signature-format cose $IMAGE --id $KEY_ID --plugin azure-kvIf the certificate doesn't contain the chain, use the
--plugin-config ca_certs=<ca_bundle_file>parameter to pass the CA certificates in a PEM file to the Key Vault plug-in. Run the following command:notation sign --signature-format cose $IMAGE --id $KEY_ID --plugin azure-kv --plugin-config ca_certs=<ca_bundle_file>To authenticate with Key Vault, by default, the following credential types (if enabled) are tried in order:
- Environment credential
- Workload identity credential
- Managed identity credential
- Azure CLI credential
If you want to specify a credential type, use an additional plug-in configuration called
credential_type. For example, you can explicitly setcredential_typetoazureclifor using an Azure CLI credential, as demonstrated in this example:notation sign --signature-format cose --id $KEY_ID --plugin azure-kv --plugin-config credential_type=azurecli $IMAGEThe following table shows the values of
credential_typefor various credential types.Credential type Value for credential_typeEnvironment credential environmentWorkload identity credential workloadidManaged identity credential managedidAzure CLI credential azurecliView the graph of signed images and associated signatures:
notation ls $IMAGEIn the following example output, a signature of type
application/vnd.cncf.notary.signatureidentified by digestsha256:d7258166ca820f5ab7190247663464f2dcb149df4d1b6c4943dcaac59157de8eis associated with$IMAGE:myregistry.azurecr.io/net-monitor@sha256:17cc5dd7dfb8739e19e33e43680e43071f07497ed716814f3ac80bd4aac1b58f └── application/vnd.cncf.notary.signature └── sha256:d7258166ca820f5ab7190247663464f2dcb149df4d1b6c4943dcaac59157de8e
Note
Since Notation v1.2.0, Notation uses the OCI referrers tag schema to store the signature in Container Registry by default. You can also enable the OCI Referrers API by using the flag --force-referrers-tag false, if necessary. Container Registry features support the OCI Referrers API, except for the registry encrypted via customer-managed keys (CMKs).
Verify a container image by using the Notation CLI
Add the root certificate to a named trust store for signature verification. If you don't have the root certificate, you can obtain it from your CA. The following example adds the root certificate
$ROOT_CERTto the$STORE_NAMEtrust store:STORE_TYPE="ca" STORE_NAME="wabbit-networks.io" notation cert add --type $STORE_TYPE --store $STORE_NAME $ROOT_CERTList the root certificate to confirm that the
$ROOT_CERTis added successfully:notation cert lsConfigure a trust policy before verification. Trust policies enable users to specify fine-tuned verification policies. Use the following command:
cat <<EOF > ./trustpolicy.json { "version": "1.0", "trustPolicies": [ { "name": "wabbit-networks-images", "registryScopes": [ "$REGISTRY/$REPO" ], "signatureVerification": { "level" : "strict" }, "trustStores": [ "$STORE_TYPE:$STORE_NAME" ], "trustedIdentities": [ "x509.subject: $CERT_SUBJECT" ] } ] } EOFThe preceding
trustpolicy.jsonfile defines one trust policy namedwabbit-networks-images. This trust policy applies to all the artifacts stored in the$REGISTRY/$REPOrepositories. The named trust store$STORE_NAMEof type$STORE_TYPEcontains the root certificates. This policy also assumes that the user trusts a specific identity with the X.509 subject$CERT_SUBJECT. For more information, see Trust store and trust policy specification.Use
notation policyto import the trust policy configuration fromtrustpolicy.json:notation policy import ./trustpolicy.jsonShow the trust policy configuration to confirm its successful import:
notation policy showUse
notation verifyto verify the integrity of the image:notation verify $IMAGEUpon successful verification of the image via the trust policy, the SHA256 digest of the verified image is returned in a successful output message. Here's an example of the output:
Successfully verified signature for myregistry.azurecr.io/net-monitor@sha256:17cc5dd7dfb8739e19e33e43680e43071f07497ed716814f3ac80bd4aac1b58f
Use timestamping
Since the Notation v1.2.0 release, Notation supports RFC 3161-compliant timestamping. This enhancement extends the trust of signatures created within the certificate's validity period by trusting a time stamp authority (TSA). This trust enables successful signature verification even after the certificates expire.
As an image signer, you should ensure that you sign container images with time stamps that a trusted TSA generated. As an image verifier, you should ensure that you trust both the image signer and the associated TSA, and establish trust through trust stores and trust policies.
Timestamping reduces costs by eliminating the need to periodically re-sign images due to certificate expiry. This ability is especially critical when you use short-lived certificates. For detailed instructions on how to sign and verify images by using timestamping, refer to the Notary Project timestamping guide.
FAQ
What should I do if the certificate expires?
If your certificate expires, you need to obtain a new one from a trusted CA vendor, along with a new private key. You can't use an expired certificate to sign container images.
Images that were signed before the certificate expired might still be validated successfully if they were signed with timestamping. Without timestamping, the signature verification fails, and you need to re-sign those images with the new certificate for successful verification.
What should I do if the certificate is revoked?
Revocation of your certificate invalidates the signature. This situation can happen for several reasons, such as compromise of the private key or changes in the certificate holder's affiliation.
To resolve this problem, you should first ensure that your source code and build environment are up to date and secure. Then, build container images from the source code, obtain a new certificate from a trusted CA vendor along with a new private key, and sign new container images with the new certificate by following this guide.
Related content
Notation provides continuous integration and continuous delivery (CI/CD) solutions on Azure Pipelines and GitHub Actions:
- To sign and verify container images in an Azure DevOps pipeline, see Sign and verify a container image by using Notation in an Azure pipeline.
- To sign container images by using GitHub Actions, see Sign a container image by using Notation in GitHub Actions.
- To verify container images by using GitHub Actions, see Verify a container image by using Notation in GitHub Actions.
To ensure that only trusted container images are deployed on Azure Kubernetes Service (AKS):
- Use Azure Policy Image Integrity (preview) by following the guide Use Image Integrity to validate signed images before deploying them to your Azure Kubernetes Service clusters (preview).
- Use Ratify and Azure Policy by following the guide Verify container image signatures by using Ratify and Azure Policy.