How docker build args expose passwords

Avoiding using docker build --build-arg to inject secrets or passowrds into Docker image builds is established wisdom within the Docker community. Here’s why.

TLDR: Using build args for secrets exposes the secret to users of your image via docker history.

Take the following Dockerfile:

FROM alpine:latest
ARG password
RUN echo hello world

This looks pretty innocent – we’re not even using the password during the build!

Let’s build the image, using the password secretsquirrel:

> docker build --build-arg password=secretsquirrel .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM alpine:latest
latest: Pulling from library/alpine
bdf0201b3a05: Pull complete
Digest: sha256:28ef97b86[...]
Status: Downloaded newer image for alpine:latest
 ---> cdf98d1859c1
Step 2/3 : ARG password
 ---> Running in 38993dbd0f31
Removing intermediate container 38993dbd0f31
 ---> 8bef9d60eae8
Step 3/3 : RUN echo hello world
 ---> Running in 0c4214ebfce8
hello world
Removing intermediate container 0c4214ebfce8
 ---> 2fd2a25cfdb3
Successfully built 2fd2a25cfdb3

Again, looks pretty safe – the password doesn’t appear in the output.

However, let’s take a look at this using docker history:

> docker history 2fd2a25cfdb3
IMAGE         ...  CREATED BY                                      ...
2fd2a25cfdb3  ...  |1 password=secretsquirrel /bin/sh -c echo h…   ...
8bef9d60eae8  ...  /bin/sh -c #(nop)  ARG password                 ...
cdf98d1859c1  ...  /bin/sh -c #(nop)  CMD ["/bin/sh"]              ...
<missing>     ...  /bin/sh -c #(nop) ADD file:2e3a37883f56a4a27…   ...

There we go, our password is right there for anyone with access to the image. The docker build command passes ARG values to all RUN steps as environment variables which appear in the history output 😭