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
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
%HOMEPATH%\.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.
|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.
$ docker trust --help
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.
$ docker trust key generate marco --dir ~/.docker/trust
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.
$ notary -d ~/.docker/trust key list
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.
$ docker trust signer add --key ~/.docker/trust/marco.pub marco marcofranssen/whalesay
notary command now will show you a bunch more keys.
$ notary -d ~/.docker/trust key list
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.
docker pull docker/whalesay
Now we can inspect the signing details of our image.
$ docker trust inspect --pretty marcofranssen/whalesay
If you would like to revoke a signed image tag you can do that using the following command.
$ docker trust revoke marcofranssen/whalesay:latest
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.
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.
docker trust key generate marco --dir ~/.docker/trust
Add another team member to allow them to sign images for a given repository.
docker trust signer add --key buddy.pub buddy <image>:<tag>
Remove a team member from being able to sign images for a given repository.
docker trust signer remove buddy <image>:<tag>
Sign a specific tag of the image.
docker trust sign <image>:<tag>
Remove the signature from a specific image tag.
docker trust revoke <image>:<tag>
Inspect the signing data of an image.
docker trust inspect --pretty <image>:<tag>
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
Thanks for reading my blog.