Skip to content

Latest commit

 

History

History
119 lines (71 loc) · 6.34 KB

README.md

File metadata and controls

119 lines (71 loc) · 6.34 KB

Docker images and .npmrc security

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.

Setup

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.

Clone the repo

  1. git clone https://github.com/alulsh/docker-npmrc-security.git or git clone [email protected]:alulsh/docker-npmrc-security.git
  2. cd docker-npmrc-security

Npm

  1. Install Node.js and npm. I recommend using nvm.
  2. Sign up for an account on npmjs.com.
  3. Run npm token create --read-only to create a read-only npm token.
  4. Run export NPM_TOKEN=<npm token> to set your npm token as an environment variable.

Docker

Download the version of Docker CE for your operating system. The BuildKit mode --secret flag requires Docker 18.09 and later.

Insecure Dockerfiles

#1 - Leaving .npmrc files in Docker containers

Dockerfile-insecure-1

To build this image, run docker build . -f Dockerfile-insecure-1 -t insecure-app-1 --build-arg NPM_TOKEN=$NPM_TOKEN.

Problem

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.

Exploitation

  1. Run docker run -it insecure-app-1 ash to start the container. We need to use ash instead of bash since we're running Alpine Linux.
  2. Run ls -al. You should see an .npmrc file in the /private-app directory.
  3. Run cat .npmrc.

#2 - Leaving .npmrc files in Docker intermediate images

Dockerfile-insecure-2

To build this image, run docker build . -f Dockerfile-insecure-2 -t insecure-app-2 --build-arg NPM_TOKEN=$NPM_TOKEN.

Problem

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.

Exploitation

  1. Run docker save insecure-app-2 -o ~/insecure-app-2.tar to save the Docker image as a tarball.
  2. Run mkdir ~/insecure-app-2 && tar xf ~/insecure-app-2.tar -C ~/insecure-app-2 to untar to ~/insecure-app-2.
  3. Run cd ~/insecure-app-2.
  4. 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.
  5. Run tar xf <layer id>/layer.tar private-app/.npmrc to extract private-app/.npmrc from the layer tarball.
  6. Run cat private-app/.npmrc to view the .npmrc file and npm token.

#3 - Leaking npm tokens in the image commit history

Dockerfile-insecure-3

To build this images, run docker build . -f Dockerfile-insecure-3 -t insecure-app-3 --build-arg NPM_TOKEN=$NPM_TOKEN.

Problem

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.

Exploitation

  1. Run docker history insecure-app-3.

Secure Dockerfiles

Multi-stage builds

Dockerfile-secure-multistage

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.

Experimental BuildKit mode --secret flag

Dockerfile-secure-secrets

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.