Manipulating Signed Docker Images

Image by Daniel von Appen

Docker Content Trust (DCT) is Docker’s mechanism for code signing. Developers can sign images they create and people using these images can verify if they have been manipulated somewhere on their way from the developer to the docker host.

Recently we looked at DCT more closely and were surprised to find out that signed images can be manipulated locally on a docker host!

We will give you a short proof-of-concept. We did our tests using Docker 19.03.5 (both, server and client).

First we check that DCT is enabeld and the alpine:latest image is digitally signed:

root@ubuntu:~# echo $DOCKER_CONTENT_TRUST
1
root@ubuntu:~# notary -s https://notary.docker.io -d ~/.docker/trust/ list docker.io/library/alpine | grep latest
  latest            2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78   1638

Next, we pull the signed alpine image:

root@ubuntu:~# docker pull alpine:latest
Pull (1 of 1): alpine:latest@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78: Pulling from library/alpine
e6b0cf9c0882: Pull complete 
Digest: sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
Status: Downloaded newer image for alpine@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
Tagging alpine@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78 as alpine:latest
docker.io/library/alpine:latest

Here we manipulate the image and add a new file:

root@ubuntu:/var/lib/docker/overlay2/488bf1a003eb59f32d2783b8b9cdddb73948fa9553384560e7073e36b3cbf862/diff# cat > testfile.txt
This file changes the image and should invalidate the signature
^C
root@ubuntu:/var/lib/docker/overlay2/488bf1a003eb59f32d2783b8b9cdddb73948fa9553384560e7073e36b3cbf862/diff# ls
bin  etc   lib    mnt  proc  run   srv  testfile.txt  usr
dev  home  media  opt  root  sbin  sys  tmp           var

We expected a signature verification error when we try to run this manipulated image. But as you can see, nothing happens and docker starts the manipulated container:

root@ubuntu:~# docker run -it alpine:latest /bin/sh
/ # ls
bin           lib           proc          srv           usr
dev           media         root          sys           var
etc           mnt           run           testfile.txt
home          opt           sbin          tmp
/ # cat testfile.txt
This file changes the image and should invalidate the signature

Assuming this is vulnerability, we contacted the Docker security team and were told that this is works-as-designed. This is because the packed image is digitally signed. After the image is pulled, it gets unpacked and there is no signature for the unpacked version. So, the threat model of DCT is mainly about the registry being compromised.

However, one would generally assume that image signing is an end-to-end security mechanism protecting the image from the time it is signed until the time it is started. Also the documentation is not very clear about that and says that signatures are verified upon docker run. But, actually protection stops as soon as the image is pulled on the docker host.

We created a pull request to update the Docker documentation and make this a bit clearer.