Docker Secrets and hiding sensitive information in Docker build output

Posted by ads on Wednesday, 2025-07-30
Posted in [Docker][Linux][Software]

While building a container, I need to pass a token into the build process. The container build step needs to download a package using said secret token - and that token is part of the URL. The token will show up on the screen, and potentially in any logfile. Not good.

Note: This applies to docker compose (and docker buildx).

The step in the Dockerfile looks like this:

1
RUN curl -1Lfs "https://website.example/verysecrettoken/setup.rpm" -o /root/setup.sh

And shows up in the build output on the screen, and in the log:

1
 => [5/23] RUN curl -1Lfs "https://downloads.website.secret/wNwKABR6G622KeQYTKEfg9/download.rpm" -o /tmp/download.rpm    9.4s

To make matters worde, the token - in the current build process - is passed in as commandline parameter:

1
docker compose build --build-arg TOKEN=$(TOKEN)

This too shows up in the build log:

1
docker compose build --build-arg TOKEN="wNwKABR6G622KeQYTKEfg9"

And potentially in the process list of the host.

It is easy to copy and paste the build output, and forget about sanitizing the secret tokens. Let’s improve this entire process.

Secrets

Docker has the capability to manage secrets for the build process.

This will mount the defined secret as a file under /run/secrets/, which then can be accessed using standard commandline tools.

compose.yaml

The compose.yaml needs two entries, first the secrets section:

1
2
3
secrets:
  token:
    environment: TOKEN

This example defines the token secret, and the content is provided from the environment variable $TOKEN.

Secrets can be read from a file, using the file method. Or from the environment, using the environment method. The environment variable name does not have to match the secret name.

In the services section, the service itself also gets a secrets subsection:

1
2
3
4
5
6
7
services:
  myservice:
    build:
      context: .
      dockerfile: Dockerfile
      secrets:
        - token

This will mount the token secret into the process for the myservice. By specifying the secrets in the service definition, one can select which secrets are available for which service. Not every service needs to see every secret.

In this example, I need the secrets during build, therefore the secrets section is a subsection of build. If you need the secrets during runtime, the secrets section is a subsection of myservice. If you need both, possibly with different secrets, define both subsections.

Dockerfile

As mentioned before, each secret is mounted under /run/secrets/. In the example above, the token secret is /run/secrets/token. The RUN command in the Dockerfile has a --mount option:

1
RUN --mount=type=secret,id=token cat /run/secrets/token

Let’s see if that works:

1
2
3
#8 [build-stage 2/2] RUN --mount=type=secret,id=token cat /run/secrets/token
#8 0.198 "wNwKABR6G622KeQYTKEfg9"
#8 DONE 0.2s

That works. By the way, the build output is made visible using the --progress plain option.

URL handling

Now that we have hidden the secrets, and found a way to securely pass the token into the build process, there is however still the problem with the URL with the token shown in the build output. This problem can be solved by creating a script and run the script, instead of running the curl command directly.

1
2
3
4
5
RUN --mount=type=secret,id=token echo "curl -Lfs 'https://downloads.website.secret/$(cat /run/secrets/token)/download.rpm' -o /tmp/download.rpm" > /tmp/download.sh
RUN cat /tmp/download.sh

#12 0.216 curl -Lfs 'https://downloads.website.secret/wNwKABR6G622KeQYTKEfg9/download.rpm' -o /tmp/download.rpm
#12 DONE 0.2s

The -s option silences curl, otherwise the URL might appear again. Now run the script:

1
RUN bash +x /tmp/download.sh

The +x option for bash is the same as setting set +x in the script: it disables tracing of the executed commands.

To show that this step does not print the URL at all, here’s the script run with the - invalid - URL, which will fail. After all, downloads.website.secret is a made up website name.

1
2
3
4
5
 => [build-stage 4/5] RUN cat /tmp/download.sh                        0.2s
 => ERROR [build-stage 5/5] RUN bash +x /tmp/download.sh              9.4s
------
 > [build-stage 5/5] RUN bash +x /tmp/download.sh:
------

No URL shown in the output, not even when there is an error. No accidental copy of credentials. Good.

Summary

Docker secrets are a secure way to pass sensitive data from files or from the environment into the build process or the running container.

This avoids accidentally exposing credentials to the viewer, or copy and paste sensitive information.


Categories: [Docker] [Linux] [Software]

Comments

With an account on the Fediverse or Mastodon, you can respond to this post. Known replies are displayed below:



Note: This will load data from mastodon.social.

Learn how this is implemented here.


Share: