Content trust in Azure Container Registry

Important to any distributed system designed with security in mind is verifying both the source and the integrity of data entering the system. Consumers of the data need to be able to verify both the publisher (source) of the data, as well as ensure it's not been modified after it was published (integrity). Azure Container Registry supports both by implementing Docker's content trust model, and this article gets you started.

Important

This feature is currently in preview. Previews are made available to you on the condition that you agree to the supplemental terms of use. Some aspects of this feature may change prior to general availability (GA).

How content trust works

As an image publisher, content trust allows you to sign the images you push to your registry. Consumers of your images (people or systems pulling images from your registry) can configure their clients to pull only signed images. When an image consumer pulls a signed image, their Docker client verifies the integrity of the image. In this model, consumers are assured that the signed images in your registry were indeed published by you, and that they've not been modified since being published.

Trusted images

Content trust works with the tags in a repository. Image repositories can contain images with both signed and unsigned tags. For example, you might sign only the myimage:stable and myimage:latest images, but not myimage:dev.

Signing keys

Content trust is managed through the use of a set of cryptographic signing keys. These keys are associated with a specific repository in a registry. There are several types of signing keys that Docker clients and your registry use in managing trust for the tags in a repository. When you enable content trust and integrate it into your container publishing and consumption pipeline, you must manage these keys carefully. For more information, see Key management later in this article and Manage keys for content trust in the Docker documentation.

Tip

This was a very high-level overview of Docker's content trust model. For an in-depth discussion of content trust, see Content trust in Docker.

Enable registry content trust

Your first step is to enable content trust at the registry level. Once you enable content trust, clients (users or services) can push signed images to your registry. Enabling content trust on your registry does not restrict registry usage only to consumers with content trust enabled. Consumers without content trust enabled can continue to use your registry as normal. Consumers who have enabled content trust in their clients, however, will be able to see only signed images in your registry.

To enable content trust for your registry, first navigate to the registry in the Azure portal. Under POLICIES, select Content Trust (Preview) > Enabled > Save.

Enabling content trust for a registry in the Azure portal

Enable client content trust

To work with trusted images, both image publishers and consumers need to enable content trust for their Docker clients. As a publisher, you can sign the images you push to a content trust-enabled registry. As a consumer, enabling content trust limits your view of a registry to signed images only. Content trust is disabled by default in Docker clients, but you can enable it per shell session or per command.

To enable content trust for a shell session, set the DOCKER_CONTENT_TRUST environment variable to 1. For example, in the Bash shell:

# Enable content trust for shell session
export DOCKER_CONTENT_TRUST=1

If instead you'd like to enable or disable content trust for a single command, several Docker commands support the --disable-content-trust argument. To enable content trust for a single command:

# Enable content trust for single command
docker build --disable-content-trust=false -t myacr.azurecr.io/myimage:v1 .

If you've enabled content trust for your shell session and want to disable it for a single command:

# Disable content trust for single command
docker build --disable-content-trust -t myacr.azurecr.io/myimage:v1 .

Grant image signing permissions

Only the users or systems you've granted permission can push trusted images to your registry. To grant trusted image push permission to a user (or a system using a service principal), grant their Azure Active Directory identities the AcrImageSigner role. This is in addition to the Contributor (or Owner) role required for pushing images to the registry.

Details for granting the AcrImageSigner role in the Azure portal and the Azure CLI follow.

Azure portal

Navigate to your registry in the Azure portal, then select Access control (IAM) > Add role assignment. Under Add role assignment, select AcrImageSigner under Role, then Select one or more users or service principals, then Save.

In this example, two entities have been assigned the AcrImageSigner role: a service principal named "service-principal," and a user named "Azure User."

Enabling content trust for a registry in the Azure portal

Azure CLI

To grant signing permissions to a user with the Azure CLI, assign the AcrImageSigner role to the user, scoped to your registry. The format of the command is:

az role assignment create --scope <registry ID> --role AcrImageSigner --assignee <user name>

For example, to grant yourself the role, you can run the following commands in an authenticated Azure CLI session. Modify the REGISTRY value to reflect the name of your Azure container registry.

# Grant signing permissions to authenticated Azure CLI user
REGISTRY=myregistry
USER=$(az account show --query user.name --output tsv)
REGISTRY_ID=$(az acr show --name $REGISTRY --query id --output tsv)

az role assignment create --scope $REGISTRY_ID --role AcrImageSigner --assignee $USER

You can also grant a service principal the rights to push trusted images to your registry. Using a service principal is useful for build systems and other unattended systems that need to push trusted images to your registry. The format is similar to granting a user permission, but specify a service principal ID for the --assignee value.

az role assignment create --scope $REGISTRY_ID --role AcrImageSigner --assignee <service principal ID>

The <service principal ID> can be the service principal's appId, objectId, or one of its servicePrincipalNames. For more information about working with service principals and Azure Container Registry, see Azure Container Registry authentication with service principals.

Push a trusted image

To push a trusted image tag to your container registry, enable content trust and push the image with docker push. The first time you push a signed tag, you're asked to create a passphrase for both a root signing key and a repository signing key. Both the root and repository keys are generated and stored locally on your machine.

$ docker push myregistry.azurecr.io/myimage:v1
The push refers to repository [myregistry.azurecr.io/myimage]
ee83fc5847cb: Pushed
v1: digest: sha256:aca41a608e5eb015f1ec6755f490f3be26b48010b178e78c00eac21ffbe246f1 size: 524
Signing and pushing trust metadata
You are about to create a new root signing key passphrase. This passphrase
will be used to protect the most sensitive key in your signing system. Please
choose a long, complex passphrase and be careful to keep the password and the
key file itself secure and backed up. It is highly recommended that you use a
password manager to generate the passphrase and keep it safe. There will be no
way to recover this key. You can find the key in your config directory.
Enter passphrase for new root key with ID 4c6c56a:
Repeat passphrase for new root key with ID 4c6c56a:
Enter passphrase for new repository key with ID bcd6d98:
Repeat passphrase for new repository key with ID bcd6d98:
Finished initializing "myregistry.azurecr.io/myimage"
Successfully signed myregistry.azurecr.io/myimage:v1

After your first docker push with content trust enabled, the Docker client uses the same root key for subsequent pushes. On each subsequent push to the same repository, you're asked only for the repository key. Each time you push a trusted image to a new repository, you're asked to supply a passphrase for a new repository key.

Pull a trusted image

To pull a trusted image, enable content trust and run the docker pull command as normal. Consumers with content trust enabled can pull only images with signed tags. Here's an example of pulling a signed tag:

$ docker pull myregistry.azurecr.io/myimage:signed
Pull (1 of 1): myregistry.azurecr.io/myimage:signed@sha256:0800d17e37fb4f8194495b1a188f121e5b54efb52b5d93dc9e0ed97fce49564b
sha256:0800d17e37fb4f8194495b1a188f121e5b54efb52b5d93dc9e0ed97fce49564b: Pulling from myimage
8e3ba11ec2a2: Pull complete
Digest: sha256:0800d17e37fb4f8194495b1a188f121e5b54efb52b5d93dc9e0ed97fce49564b
Status: Downloaded newer image for myregistry.azurecr.io/myimage@sha256:0800d17e37fb4f8194495b1a188f121e5b54efb52b5d93dc9e0ed97fce49564b
Tagging myregistry.azurecr.io/myimage@sha256:0800d17e37fb4f8194495b1a188f121e5b54efb52b5d93dc9e0ed97fce49564b as myregistry.azurecr.io/myimage:signed

If a client with content trust enabled tries to pull an unsigned tag, the operation fails:

$ docker pull myregistry.azurecr.io/myimage:unsigned
No valid trust data for unsigned

Behind the scenes

When you run docker pull, the Docker client uses the same library as in the Notary CLI to request the tag-to-SHA-256 digest mapping for the tag you're pulling. After validating the signatures on the trust data, the client instructs Docker Engine to do a "pull by digest." During the pull, the Engine uses the SHA-256 checksum as a content address to request and validate the image manifest from the Azure container registry.

Key management

As stated in the docker push output when you push your first trusted image, the root key is the most sensitive. Be sure to back up your root key and store it in a secure location. By default, the Docker client stores signing keys in the following directory:

~/.docker/trust/private

Back up your root and repository keys by compressing them in an archive and storing it securely offline (such as on a USB storage device). For example, in Bash:

umask 077; tar -zcvf docker_private_keys_backup.tar.gz ~/.docker/trust/private; umask 022

Along with the locally generated root and repository keys, several others are generated and stored by Azure Container Registry when you push a trusted image. For a detailed discussion of the various keys in Docker's content trust implementation, including additional management guidance, see Manage keys for content trust in the Docker documentation.

Lost root key

If you lose access to your root key, you lose access to the signed tags in any repository whose tags were signed with that key. Azure Container Registry cannot restore access to image tags signed with a lost root key. To remove all trust data (signatures) for your registry, first disable, then re-enable content trust for the registry.

Warning

Disabling and re-enabling content trust in your registry deletes all trust data for all signed tags in every repository in your registry. This action is irreversible--Azure Container Registry cannot recover deleted trust data. Disabling content trust does not delete the images themselves.

To disable content trust for your registry, navigate to the registry in the Azure portal. Under POLICIES, select Content Trust (Preview) > Disabled > Save. You're warned of the loss of all signatures in the registry. Select OK to permanently delete all signatures in your registry.

Disabling content trust for a registry in the Azure portal

Next steps

See the Docker documentation for additional information about content trust. While several key points were touched on in this article, content trust is an extensive topic and is covered more in-depth in the Docker documentation.

Content trust in Docker