32

Sometimes there is a need to use sensitive data when building a Docker image. For example, an API token or SSH key to download a remote file or to install dependencies from a private repository. It may be desirable to distribute the resulting image and leave out the sensitive credentials that were used to build it. How can this be done?

I have seen docker-squash which can squash multiple layers in to one, removing any deleted files from the final image. But is there a more idiomatic approach?

Ben Whaley
  • 32,811
  • 7
  • 87
  • 85

5 Answers5

4

Regarding idiomatic approach, I'm not sure, although docker is still quite young to have too many idioms about.

We have had this same issue at our company, however. We have come to the following conclusions, although these are our best efforts rather than established docker best practices.

1) If you need the values at build time: Supply a properties file in the build context with the values that can be read at build, then the properties file can be deleted after build. This isn't as portable but will do the job.

2) If you need the values at run time: Pass values as environment variables. They will be visible to someone who has access to ps on the box, but this can be restricted via SELinux or other methods (honestly, I don't know this process, I'm a developer and the operations teams will deal with that part).

Mitch Kent
  • 574
  • 5
  • 23
  • 5
    Regarding 1, the problem is that when pushing the image to an index (Docker hub, quay.io, etc), all the layers of the image are pushed, including the layer that had the properties file. Anyone who pulls that image can start a container with the layer that still has the property file and see the sensitive variables. – Ben Whaley Sep 24 '14 at 17:14
  • Ah ok - apologies. I thought you were referring to sharing a build file (in which others could build from and then supply their own properties file at build time). – Mitch Kent Sep 25 '14 at 08:21
  • 11
    if within a single `RUN` command you download some file, do whatever you need with its content and finally delete that file, the downloaded file won't be committed into a layer. It has to be in a single `RUN` command – Thomasleveil Sep 27 '14 at 00:45
4

Sadly, there is still no proper solution for handling sensitive data while building a docker image.

This bug has a good summary of what is wrong with every hack that people suggest: https://github.com/moby/moby/issues/13490

And most advice seems to confuse secrets that need to go INTO the container with secrets that are used to build the container, like several of the answers here.

The current solutions that seem to actually be secure, all seem to center around writing out the secret file to disk or memory, and then starting a silly little HTTP server, and then having the build process pull in the secret from the http server, use it, and not store it in the image.

The best I've found without going to that level of complexity, is to (mis)use the built in predefined-args feature of docker compose files, as specified in this comment:

https://github.com/moby/moby/issues/13490#issuecomment-403612834

That does seem to keep the secrets out of the image build history.

user2163960
  • 1,871
  • 19
  • 22
2

Matthew Close talks about this in this blog article.

Summarized: You should use docker-compose to mount sensitive information into the container.

kev
  • 8,928
  • 14
  • 61
  • 103
Xaser
  • 2,066
  • 2
  • 22
  • 45
2

2019, and I'm not sure there is an idomatic approach or best practices regarding secrets when using docker: https://github.com/moby/moby/issues/13490 remains open so far.


Secrets at runtime:

So far, the best approach I could find was using environment variables in a container:

  • with docker run -e option... but then your secrets are available in command line history
  • with docker env_file option or docker-compose env_file option. At least secrets are not passed in command line
  • Problem: in any case, secrets are now available for anyone able to run docker commands on your docker host (using docker inspect command)

Secrets at build time (your question):

I can see 2 additional (partial?) solutions to this problem:

Multistage build:

use a multi-stage docker build: basically, your dockerfile will define 2 images:

  1. One first intermediate image (the "build image") in which:

    • you add your secrets to this image: either use build args or copy secret files (be careful with build args: they have to be passed in docker build command line)
    • you build your artefact (you now have access to your private repository)
  2. A second image (the "distribution image") in which:

    • you copy the built artefact from the "build image"
    • distribute your image on a docker registry

This approach is explained by several comments in the quoted github thread:

Caution

This multistage build approach is far from being ideal: the "build image" is still lying on your host after the build command (and is containing your sensitive information). There are precautions to take

A new --secret build option:

I discovered this option today, and therefore did not experiment it yet... What I know so far:

Géraud
  • 1,923
  • 3
  • 20
  • 20
1

The way we solve this issue is that we have a tool written on top of docker build. Once you initiate a build using the tool, it will download a dockerfile and alters it. It changes all instructions which require "the secret" to something like:

RUN printf "secret: asd123poi54mnb" > /somewhere && tool-which-uses-the-secret run && rm /somewhere

However, this leaves the secret data available to anyone with access to the image unless the layer itself is removed with a tool like docker-squash. The command used to generate each intermediate layer can be found using the history command

Tomas Tomecek
  • 6,226
  • 3
  • 30
  • 26