Signing Docker images using Docker Content Trust
Marco Franssen /
9 min read • 1620 words
In this blog I want to introduce you to the concept of signing Docker images. Signing your docker images will add some layer of trust to your images. This can guarantee a consumer of your image that this image is for sure published by you and hasn't been tampered with by others.
You might already used PGP to sign your Git commits. In this blogpost I shown a nice way of setting PGP signing keys using Krypton that adds 2FA. In practice Docker image signing is the same concept.
If this all sounds a bit fuzzy to you, please continue reading, hopefully I am able to make things more clear. ;-)
Docker Content Trust (DCT) provides the ability to use digital signatures for data sent to and received from remote Docker registries. These signatures allow client-side or runtime verification of the integrity and publisher of specific image tags.
Through DCT, image publishers can sign their images and image consumers can ensure that the images they use are signed. Publishers could be individuals or organizations manually signing their content or automated software supply chains signing content as part of their release process.
In practice for a consumer nothing changes until DCT is enabled. From then you will only be able to work with Signed images. So for us developers it is important that we sign our images, so they can also be used by more restricted environments with DCT enabled.
DCT builds on top of Notary which is a more general purpose solution to sign artifacts. DCT implements this functionality for Docker images.
To be able to sign your images we will have to create x509 certificates which are used to sign repositories but also to define who is allowed to sign the repositories. You can also import existing certificates. E.g. managed by a PKI solution or created via openssl
commandline.
Setting up your certificates
Before we start I want to mention it is very important to backup your certificates and store the credentials to use them in a password manager. Especially the root certificate we are about to generate.
WARNING: Loss of the root key is very difficult to recover from. Correcting this loss requires intervention from Docker Support to reset the repository state. This loss also requires manual intervention from every consumer that used a signed tag from this repository prior to the loss.
The keys will be stored at following location ~/.docker/trust/private
(MacOSX/Linux) or %USERPROFILE%\.docker\trust\private
(Windows). Please consult Docker Content Trust Documentation to properly manage the backups of your signing keys.
There are different types of keys, which have some hierachical structure as depicted in below diagram.
| Key | Description | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | root key | Root of content trust for an image tag. When content trust is enabled, you create the root key once. Also known as the offline key, because it should be kept offline. | | targets | This key allows you to sign image tags, to manage delegations including delegated keys or permitted delegation paths. Also known as the repository key, since this key determines what tags can be signed into an image repository. | | snapshot | This key signs the current collection of image tags, preventing mix and match attacks. | | timestamp | This key allows Docker image repositories to have freshness security guarantees without requiring periodic content refreshes on the client’s side. | | delegation | Delegation keys are optional tagging keys and allow you to delegate signing image tags to other publishers without having to share your targets key. |
Lets have a look on the docker trust
cli.
As you can see the docker trust
command allows us to manage keys, manage the entities allowed to sign our images, inspect the signatures, revoke and sign our images.
Lets first create our signing keys. The --dir
command below defines where you would like to store the public key.
Once we have our key created we can use notary
to get an overview of our keys. Oh, don't forget to store the credentials and ensure you will arrange some backups.
Manage entities for a repository
Now we have our personal signing key we can authorize our key to sign docker images for a given repository. This will also create new target keys in case the repository doesn't exist yet. The root key will be required to create new repository keys. The repository key will be required to add or remove new signing entities on the repository.
Running the notary
command now will show you a bunch more keys.
Signing
Now we have all the keys in place to be able to sign a docker image called marcofranssen/whalesay. To be able to do so I will first have to create this image or simply download an existing image and tag it as such.
Now we can inspect the signing details of our image.
If you would like to revoke a signed image tag you can do that using the following command.
Last but not least you could add your collegues as a signing entity to your repositories using their public key. This will require them to generate a key first and provide their public key in order to be authorized.
For your own reference you might want to create a bookmark now to the TL;DR below so you can easily lookup the commands and how to use as a future reference.
TL;DR
Generate / Load Keys
Create a new signing key or load an existing signing key.
NOTE: the name you provide will also be the name used in the signer data. Consider using a descriptive name.
Adding delegation key
Add another team member to allow them to sign images for a given repository.
Removing a signer
Remove a team member from being able to sign images for a given repository.
Signing an image
Sign a specific tag of the image.
Revoke trust
Remove the signature from a specific image tag.
Inspect image trust
Inspect the signing data of an image.
Enabling content trust
Content trust is disabled by default. To work only with signed images Docker Content Trust should be enabled.
NOTE: this will disallow you from using non signed images. Docker pull will fail if the image is not signed. You will not be able to build your own image using a non-signed image in the FROM definition
References
Thanks for reading my blog.