Manipulating Signed Docker Images
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.