Docker is an incredibly useful tool for running reproducible analysis workflows. For useRs
, the rocker collection of images is very convenient for creating version-controlled R environments. This is pretty straightforward if you are using packages on CRAN, or publicly available packages on GitHub. But what if we want to use private packages on GitHub, or need for any other reason to enter authentication credentials during the build?
There are various ways to copy data into the image during the build, but when handling secrets that we don’t want hanging around after it’s finished, caution is needed. Approaches such as using COPY
or ARGS
will leave traces in the build. Staged builds are more secure, but tricky. Fortunately, as of v. 18.09, Docker is now providing official support for handling secrets.
A simple example
Here is how to use the new Docker features to securely pass a secret during a build 1.
There are few non-default settings that need to be specified for this. First of all, prior to the docker build
command, you need to specify that you want to use the new BuildKit backend with DOCKER_BUILDKIT=1
. So the command starts DOCKER_BUILDKIT=1 docker build ...
Next, we must add a syntax directive to the top line of the Dockerfile
. For example, for a Dockerfile
based on rocker/tidyverse
:
# syntax=docker/dockerfile:1.0.0-experimental
FROM rocker/tidyverse
Save your secrets in a text file. Let’s call it my_secret_stash
2. If you are using it to store your GitHub PAT, it would just be one line with the PAT. Here, let’s put in some random word:
echo "FABULOUS" > my_secret_stash
This is all we need to use secrets during the build. Here is an example Dockerfile
similar to the one in the Docker documentation.
# syntax = docker/dockerfile:1.0-experimental
FROM alpine
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
To see how it works, save this as Dockerfile
, then from the same directory containing Dockerfile
and my_secret_stash
, build the image:
DOCKER_BUILDKIT=1 docker build --progress=plain --no-cache \
--secret id=mysecret,src=my_secret_stash .
I’ve truncated the output, but you should see something like this (the exact build step number may vary).
Can you spot our secret? It’s showing up from the cat
command. However, it will not remain in the image.
Installing a private R package
To install a package from my private GitHub repo, I created an additional simple R script, called install_git_packages.R
:
# install_git_packages.R
<- commandArgs(trailing = TRUE)
secret ::install_github("joelnitta/my-private-package", auth_token = secret) devtools
commandArgs(trailing = TRUE)
will return whatever command line arguments were passed to Rscript
after the name of the script, as a character vector.
We will call this script from the Dockerfile
and pass the secret to it.
Here is the Dockerfile to do that. (Note that although we copy the install_git_packages.R
script into the image, we are passing it the secret variable that is only present during the build, so this should not remain afterwards.)
# syntax = docker/dockerfile:1.0-experimental
FROM rocker/tidyverse:3.5.1
ENV DEBIAN_FRONTEND noninteractive
COPY install_git_packages.R .
RUN apt-get update
RUN --mount=type=secret,id=mysecret \
Rscript install_git_packages.R `cat /run/secrets/mysecret`
Let’s build the image and tag it:
DOCKER_BUILDKIT=1 docker build --progress=plain --no-cache \
--secret id=mysecret,src=my_secret_stash . -t my_special_image
That’s it!
Reproducibility
Footnotes
No guarantees!! This is just my understanding from reading the docker documentation and other blogs↩︎
Of course, be sure to add the file containing the secret to
.gitignore
!↩︎
Reuse
Citation
@online{nitta2019,
author = {Nitta, Joel},
title = {Building {R} Docker Images with Secrets},
date = {2019-02-16},
url = {https://www.joelnitta.com/posts/2019-02-16_building-r-docker-images-with-secrets/},
langid = {en}
}