This is a companion repo with code samples for https://www.alexandraulsh.com/2018/06/25/docker-npmrc-security/, a blog post I wrote about using .npmrc
files securely in Docker images.
To build these example Docker images you'll need git, Node.js, npm, an npm account, and Docker. You'll need to set an NPM_TOKEN
environment variable so you can pass it as a build argument to Docker.
git clone https://github.com/alulsh/docker-npmrc-security.git
orgit clone git@github.com:alulsh/docker-npmrc-security.git
cd docker-npmrc-security
- Install Node.js and npm. I recommend using nvm.
- Sign up for an account on npmjs.com.
- Run
npm token create --read-only
to create a read-only npm token. - Run
export NPM_TOKEN=<npm token>
to set your npm token as an environment variable.
Download the version of Docker CE for your operating system. The BuildKit mode --secret
flag requires Docker 18.09 and later.
To build this image, run docker build . -f Dockerfile-insecure-1 -t insecure-app-1 --build-arg NPM_TOKEN=$NPM_TOKEN
.
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
RUN npm install
The .npmrc
file is never deleted from this image. The .npmrc
file is on the file system of any containers created from this image.
- Run
docker run -it insecure-app-1 ash
to start the container. We need to useash
instead ofbash
since we're running Alpine Linux. - Run
ls -al
. You should see an.npmrc
file in the/private-app
directory. - Run
cat .npmrc
.
To build this image, run docker build . -f Dockerfile-insecure-2 -t insecure-app-2 --build-arg NPM_TOKEN=$NPM_TOKEN
.
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
RUN npm install
RUN rm -f .npmrc
The .npmrc
file is deleted from this Docker image but in a separate RUN
instruction. Each RUN
instruction creates a new Docker layer (intermediate image). If an attacker has access to the Docker daemon or obtains a copy of our image then they can steal the .npmrc
file from the layers of the Docker image.
- Run
docker save insecure-app-2 -o ~/insecure-app-2.tar
to save the Docker image as a tarball. - Run
mkdir ~/insecure-app-2 && tar xf ~/insecure-app-2.tar -C ~/insecure-app-2
to untar to~/insecure-app-2
. - Run
cd ~/insecure-app-2
. - Run
for layer in */layer.tar; do tar -tf $layer | grep -w .npmrc && echo $layer; done
. You should see a list of layers with.npmrc
files. - Run
tar xf <layer id>/layer.tar private-app/.npmrc
to extractprivate-app/.npmrc
from the layer tarball. - Run
cat private-app/.npmrc
to view the.npmrc
file and npm token.
To build this images, run docker build . -f Dockerfile-insecure-3 -t insecure-app-3 --build-arg NPM_TOKEN=$NPM_TOKEN
.
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && \
npm install && \
rm -f .npmrc
The .npmrc
file is created, used, and deleted in the same RUN
instruction and Docker layer. Since we passed in the npm token as a build argument (ARG NPM_TOKEN
) our npm tokens are still leaked in the Docker image commit history. If the attacker gains access to the Docker daemon or obtains a copy of our Docker image then they can steal our npm tokens using docker history
.
- Run
docker history insecure-app-3
.
To build this image, run docker build . -f Dockerfile-secure-multistage -t secure-app-multistage --build-arg NPM_TOKEN=$NPM_TOKEN
.
This Dockerfile
uses multi-stage builds to protect our .npmrc
file. In the first stage build, we create our .npmrc
, run npm install
, and delete our .npmrc
. We then copy over our built Node application to our second stage build. We can use the same base image - node:8.11.3-alpine
- for both stages of our build.
To verify that this Docker image does not leak our npm tokens, run docker history secure-app-multistage
.
To build this image, run DOCKER_BUILDKIT=1 docker build . -f Dockerfile-secure-secrets -t secure-app-secrets --secret id=npm,src=$HOME/.npmrc
. You can also run export DOCKER_BUILDKIT=1
to enable BuildKit, then run docker build . -f Dockerfile-secure-secrets -t secure-app-secrets --secret id=npm,src=$HOME/.npmrc
.
This Dockerfile
uses the --secret
flag for docker build
released with Docker 18.09. It uses the experimental RUN --mount=type=secret
syntax from the experimental Docker frontend for BuildKit. This Docker CLI pull request added support for --secret
to docker build
in August 2018.
To verify that this Docker image does not leak our npm tokens, run docker history secure-app-secrets
.