There may be cases where you need to pass in secrets to a RUN
command in a Dockerfile
, and it's very important that these secrets not be leaked into the environment or the image. In particular, these secrets should not be stored in the image (either on disk or in the environment, not even in intermediate layers), they should not show up when using docker history
.
While working this topic, I found many blog posts that point to pieces that may be used, but nothing that pulls it all together, so I decided to write this post with everything I've found. I'll provide a list of references at the end.
In my case, I needed to temporarily pass a valid odbc.ini file to my Julia code so that I could build a SysImage with the appropriate database query and result parsing functions compiled. I did not want the odbc.ini file available in the image.
Step 0: Make sure you have docker > 18.09
docker version
You most likely have a new enough version of docker, but in the odd chance that you're running a version older than 18.09, please upgrade. My tests were run on 19.03 and 20.10.
Developing the Dockerfile:
Step 1: Specify the Dockerfile syntax
At the top of your Dockerfile
(this has to be the absolute first line), add the following:
# syntax=docker/dockerfile:1
This tells docker build
to use the latest 1.x version of the Dockerfile syntax.
There are various docs that specify using 1.2 or 1.0-experimental. These values were valid when the docs were written, but are dated at this point. Specifying version 1
tells docker build
to use whatever is latest on the 1.x tree, so you can still use 1.3, 1.4, etc. Specifying 1.2 restricts it to the 1.2.x tree.
Step 2: Mount a secret file where you need it
At the RUN
command where you need a secret, --mount
it as follows:
RUN --mount=type=secret,id=mysecret,dst=/path/to/secret.key,uid=1000 your-command-here
There are a few things in here, which I'll explain one by one.
- type=secret
- This tells docker that we're mounting a secret file from the host (as opposed to a directory or something else)
- id=mysecret
- This is any string you'd like. It has to match the
id
passed in on thedocker build
command line - dst=/path/to/secret.key
- This is where you'd like the secret file to be accessible. Any file already at this location will be temporarily hidden while the secret file is mounted, so it's safe to use a location that your code will expect at run time.
- uid=1000
- This is the userid that should own the file. This defaults to
0 (root)
, so is useful if your command runs as a different user. You can also specify agid
The full list of supported parameters for secret mounts is available at the buildkit github page
You can add the same --mount
at different locations in your Dockerfile
, and with different dst
and uid
values. The file is mounted only for the duration of that RUN
command and not persisted to any layers.
Running docker build
Step 3: Set the environment variable
This step is optional on newer versions of Docker.
Once you're ready to run docker build
, tell docker
to use BuildKit
DOCKER_BUILDKIT=1
You can either put this right before running the command, or export it into your shell.
Step 4: Run docker build
with your secret file
docker build --secret id=mysecret,src=/full/path/to/secret.key .
It's important to note that tilde (~
) expansion does not work here. You can use an absolute or relative path, but you cannot use expansion.
That's IT!!!
Jenkins
If you run your docker builds through jenkins, you'll need a few more steps. The bulk of it is documented in this Cloudbees-CI article about injecting secrets.
Once you've gotten your secret file into Jenkins, and bound it to an environment variable in your Build Environment, you have to update the docker build
command to use this variable instead.
For example, if we bound the secret file to a variable called MYSECRETFILE
, then we'd change our build command to:
docker build --secret id=mysecret,src=${MYSECRETFILE} .
References
These links were very useful in figuring out this solution.
0 comments :
Post a Comment