docker build + private NPM (+ private docker hub)
Asked Answered
A

7

28

I have an application which runs in a Docker container. It requires some private modules from the company's private NPM registry (Sinopia), and accessing these requires user authentication. The Dockerfile is FROM iojs:latest.

I have tried:

1) creating an .npmrc file in the project root, this actually makes no difference and npm seems to ignore it 2) using env variables for NPM_CONFIG_REGISTRY, NPM_CONFIG_USER etc., but the user doesn't log in.

Essentially, I seem to have no way of authenticating the user within the docker build process. I was hoping that someone might have run into this problem already (seems like an obvious enough issue) and would have a good way of solving it.

(To top it off, I'm using Automated Builds on Docker Hub (triggered on push) so that our servers can access a private Docker registry with the prebuilt images.)

Are there good ways of either: 1) injecting credentials for NPM at build time (so I don't have to commit credentials to my Dockerfile) OR 2) doing this another way that I haven't thought of ?

Anecdote answered 1/6, 2015 at 12:27 Comment(2)
Did you ever solve this? I'm running into the same problem :(Flowers
yep, just posted my answer!Anecdote
A
4

The buildkit answer is correct, except it runs everything as root which is considered a bad security practice.

Here's a Dockerfile that works and uses the correct user node as the node Dockerfile sets up. Note the secret mount has the uid parameter set, otherwise it mounts as root which user node can't read. Note also the correct COPY commands that chown to user:group of node:node

FROM node:12-alpine

USER node

WORKDIR /home/node/app

COPY --chown=node:node package*.json ./

RUN --mount=type=secret,id=npm,target=./.npmrc,uid=1000 npm ci

COPY --chown=node:node index.js .

COPY --chown=node:node src ./src

CMD [ "node", "index.js" ]

Anglaangle answered 5/3, 2022 at 21:44 Comment(3)
Since when is running commands as root in docker build bad practice? That is vastly different from having your container entrypoint running as root. I good practice your entrypoint wouldn't run as node user either, you'd use shadow-utils to create a new app user following least-privilege principles.Nanete
a user has to be created or the default is root for run. The above is how nodejs does it. Got an example that works correctly? Link to it pleaseAnglaangle
Right, but I would rather create my own than use the user node whose permissions are subject to upstream changes on new image pulls.Nanete
A
35

I found a somewhat elegant-ish solution in creating a base image for your node.js / io.js containers (you/iojs):

  1. log in to your private npm registry with the user you want to use for docker
  2. copy the .npmrc file that this generates

Example .npmrc:

registry=https://npm.mydomain.com/
username=dockerUser
[email protected]
strict-ssl=false
always-auth=true
//npm.mydomain.com/:_authToken="someAuthToken"
  1. create a Dockerfile that copies the .npmrc file appropriately.

Here's my Dockerfile (based on iojs:onbuild):

FROM iojs:2.2.1

MAINTAINER YourSelf

# Exclude the NPM cache from the image
VOLUME /root/.npm

# Create the app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Copy npm config
COPY .npmrc /root/.npmrc

# Install app
ONBUILD COPY package.json /usr/src/app/
ONBUILD RUN npm install
ONBUILD COPY . /usr/src/app

# Run
CMD [ "npm", "start" ]
  1. Make all your node.js/io.js containers FROM you/iojs and you're good to go.
Anecdote answered 22/6, 2015 at 22:15 Comment(3)
I ended up doing a similar thing today. It's the only secret key out of all my projects that I need at build time :(. I think this is the best solution currently, thanks for posting your answer!Flowers
Here is the official dock about private npm repo and Docker: docs.npmjs.com/private-modules/docker-and-private-modulesDalenedalenna
Beware MAINTAINER is deprecated, use LABEL instead.Tempura
C
18

In 2020 we've got BuildKit available. You don't have to pass secrets via COPY or ENV anymore, as it's not considered safe.

Sample Dockerfile:

# syntax=docker/dockerfile:experimental
FROM node:13-alpine

WORKDIR /app

COPY package.json yarn.lock ./

RUN --mount=type=ssh --mount=type=secret,id=npmrc,dst=$HOME/.npmrc \
  yarn install --production --ignore-optional --frozen-lockfile

# More stuff...

Then, your build command can look like this:

docker build --no-cache --progress=plain --secret id=npmrc,src=/path-to/.npmrc .

For more details, check out: https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information

Cannady answered 22/9, 2020 at 15:15 Comment(2)
You are using root user, security recommendation is to user USER node. However this is broken, see github.com/docker/for-mac/issues/3668. I don't have a solution yet.Anglaangle
The security rule is to use root user in your Dockerfile to build your image, and set USER node only at the very end of it. This way, the runtime user (node) won't have write rights on the files, but only read right.Insomuch
R
13

For those who are finding this article via google and are still looking for an alternative way that doesn't involve leaving you private npm tokens on your docker images and containers:

We were able to get this working by doing the npm install prior to the docker build (By doing this it lets you have your .npmrc outside of your image\container). Once the private modules have been installed locally you can copy your files across to the image as part of your build:

    # Make sure the node_modules contain only the production modules when building this image
    COPY . /usr/src/app

You also need to make sure that your .dockerignore file doesn't exclude the node_modules folder.

Once you have the folder copied into your image, the trick is to to npm rebuild instead of npm install. This will rebuild any native dependancies that are effected by any differences between your build server and your docker OS:

    FROM nodesource/vivid:LTS

    # For application location, default from nodesource is /usr/src/app
    # Make sure the node_modules contain only the production modules when building this image
    COPY . /usr/src/app
    WORKDIR /usr/src/app
    RUN npm rebuild
    CMD npm start
Rhyner answered 5/8, 2016 at 5:59 Comment(0)
L
5

I would recommend not using a .npmrc file but instead use npm config set. This works like a charm and is much cleaner:

ARG AUTH_TOKEN_PRIVATE_REGISTRY

FROM node:latest

ARG AUTH_TOKEN_PRIVATE_REGISTRY
ENV AUTH_TOKEN_PRIVATE_REGISTRY=${AUTH_TOKEN_PRIVATE_REGISTRY}

WORKDIR /home/usr/app

RUN npm config set @my-scope:registry https://my.private.registry && npm config set '//my.private.registry/:_authToken' ${AUTH_TOKEN_PRIVATE_REGISTRY}

RUN npm ci

CMD ["bash"]
Lumbago answered 15/1, 2022 at 18:44 Comment(1)
Both ways makes no difference; $ npm config handles itself with the global NPM config (~/.npmrc) or the local project config (./.npmrc), it's the same and have no performance or security implications.Woolgrower
A
4

The buildkit answer is correct, except it runs everything as root which is considered a bad security practice.

Here's a Dockerfile that works and uses the correct user node as the node Dockerfile sets up. Note the secret mount has the uid parameter set, otherwise it mounts as root which user node can't read. Note also the correct COPY commands that chown to user:group of node:node

FROM node:12-alpine

USER node

WORKDIR /home/node/app

COPY --chown=node:node package*.json ./

RUN --mount=type=secret,id=npm,target=./.npmrc,uid=1000 npm ci

COPY --chown=node:node index.js .

COPY --chown=node:node src ./src

CMD [ "node", "index.js" ]

Anglaangle answered 5/3, 2022 at 21:44 Comment(3)
Since when is running commands as root in docker build bad practice? That is vastly different from having your container entrypoint running as root. I good practice your entrypoint wouldn't run as node user either, you'd use shadow-utils to create a new app user following least-privilege principles.Nanete
a user has to be created or the default is root for run. The above is how nodejs does it. Got an example that works correctly? Link to it pleaseAnglaangle
Right, but I would rather create my own than use the user node whose permissions are subject to upstream changes on new image pulls.Nanete
E
1

@paul-s Should be the accepted answer now because it's more recent IMO. Just as a complement, you mentioned you're using the docker/build-push-action action so your workflow must be as following:

- uses: docker/build-push-action@v3
  with:
    context: .
    # ... all other config inputs
    secret-files: |
      NPM_CREDENTIALS=./.npmrc

And then, of course, bind the .npmrc file from your dockerfile using the ID you specified. In my case I'm using a Debian based image (uid starts from 1000). Anyways:

RUN --mount=type=secret,id=NPM_CREDENTIALS,target=<container-workdir>/.npmrc,uid=1000 \
npm install --only=production
Endanger answered 10/11, 2022 at 20:27 Comment(0)
N
0

For future people who might try to solve a failing install in their setup: Don't forget to copy over the .npmrc file if you rely on it for resolving the dependencies:

COPY .npmrc ./
Niacin answered 5/7, 2023 at 19:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.