Docker-node: Adding a non-root user with UID 1000 breaks custom builds

Created on 14 Dec 2016  路  17Comments  路  Source: nodejs/docker-node

When using node as base image to build my own images, I usually create a user with UID 1000 (due to some internal restriction, this UID must be 1000), but now with node creating a user with UID 1000 by itself, my Dockerfiles no longer builds.

As Dockerfiles is now, it is not possible to fix this issue without having to fork it.

Most helpful comment

I have this pattern where I create a user inside containers based on build args:
First I add default values at the top of the dockerfile:

ARG HOST_USER_UID=1000
ARG HOST_USER_GID=1000

Then I have this RUN instruction:

RUN set -ex                                                 && \
                                                               \
    echo 'Creating notroot user and group from host'        && \
    groupadd -g $HOST_USER_GID notroot                      && \
    useradd -lm -u $HOST_USER_UID -g $HOST_USER_GID notroot

Then I use the notroot name when i have to change permissions and execute commands:

RUN set -ex                                                 && \
                                                               \
    echo 'Giving permissions to files'                      && \
    chown -R notroot:notroot /work                          && \
                                                               \
    echo 'Installing yarn'                                  && \
    npm install -g yarn                                     && \
                                                               \
    echo 'Installing production packages as notroot'        && \
    runuser notroot -c "                                       \
      cd /work                                              && \
      yarn install                                             \
        --modules-folder /home/notroot/node_modules            \
        --production                                           \
    "

To build I just pass build args in the docker-compose.yml:

build:
  args:
    HOST_USER_UID: ${HOST_USER_UID}
    HOST_USER_GID: ${HOST_USER_GID}

Or in the cli:

docker build \
  --build-arg HOST_USER_UID=`id -u` \
  --build-arg HOST_USER_GID=`id -g` \
  ...

The change introduced in this PR breaks this kind of workflow.

This might only affect me and a few other people and yes, there are simple workarounds and you have no reason to care about how I build stuff downstream but what is the reason to add such a user if you are not using it during the build process, nor is it enforced as the image default user.

Anybody who needs or wants such a user will just create one themselves based on their workflow (as I did above).
Also the default uid/gid of 1000 is useless if the host user is created through logging in with Active Directory, or if someone simply uses a different uid/gid combination.

Right now I'm using the userdel -r node workaround.

\ Just my 2垄, feel free to ignore

All 17 comments

@rafaelsierra
If simply using the provided node user is not an option, the following RUN command added in your Dockerfile should allow you to use the base Node images without having to fork:

RUN groupmod -g 1001 node \
  && usermod -u 1001 -g 1001 node

This simply changes the user/group ID of node to 1001, freeing up the ID 1000.

Alternatively, you could also delete the node user and group in a RUN command if you don't need them.

I didn't adopted any solution so far because this might change again in the future, forcing me to fix this once again, so I will just wait to see if any merge is accepted that would bring a better fix

Well, if you ask me, any Docker image should ideally use a non-privileged user.
The node Docker group decided to add one by default called node.
Although it's not used by default, it's only a USER node Dockerfile instruction away.

For compatibility with host-mounts on docker-machine setups on OSX, I recommended to use 1000 as ID, probably the same reason you're using that.
This recommendation was merged, so any node Docker image now has a node user with ID 1000.

As far as breaking compatibility goes, simply adding the node user already broke dependant Docker images (e.g. all that added a node user by themselves).
But I think fixing downstream images is easy and I also don't think the node Docker group makes those decisions without consideration (e.g. there were multiple reviews), so I'd say it's safe to say that those changes will stay.

I haven't tested this but since the image runs as root, couldn't you rename the node user to whatever you like in your Dockerfile?

RUN usermod -d /home/newname -l newname node

I have this pattern where I create a user inside containers based on build args:
First I add default values at the top of the dockerfile:

ARG HOST_USER_UID=1000
ARG HOST_USER_GID=1000

Then I have this RUN instruction:

RUN set -ex                                                 && \
                                                               \
    echo 'Creating notroot user and group from host'        && \
    groupadd -g $HOST_USER_GID notroot                      && \
    useradd -lm -u $HOST_USER_UID -g $HOST_USER_GID notroot

Then I use the notroot name when i have to change permissions and execute commands:

RUN set -ex                                                 && \
                                                               \
    echo 'Giving permissions to files'                      && \
    chown -R notroot:notroot /work                          && \
                                                               \
    echo 'Installing yarn'                                  && \
    npm install -g yarn                                     && \
                                                               \
    echo 'Installing production packages as notroot'        && \
    runuser notroot -c "                                       \
      cd /work                                              && \
      yarn install                                             \
        --modules-folder /home/notroot/node_modules            \
        --production                                           \
    "

To build I just pass build args in the docker-compose.yml:

build:
  args:
    HOST_USER_UID: ${HOST_USER_UID}
    HOST_USER_GID: ${HOST_USER_GID}

Or in the cli:

docker build \
  --build-arg HOST_USER_UID=`id -u` \
  --build-arg HOST_USER_GID=`id -g` \
  ...

The change introduced in this PR breaks this kind of workflow.

This might only affect me and a few other people and yes, there are simple workarounds and you have no reason to care about how I build stuff downstream but what is the reason to add such a user if you are not using it during the build process, nor is it enforced as the image default user.

Anybody who needs or wants such a user will just create one themselves based on their workflow (as I did above).
Also the default uid/gid of 1000 is useless if the host user is created through logging in with Active Directory, or if someone simply uses a different uid/gid combination.

Right now I'm using the userdel -r node workaround.

\ Just my 2垄, feel free to ignore

who in the heck added the node user? there wasn't any on version 4.4.5 for sure

@tszymanek i did

Using the -o option on usermod to set a duplicate 1000 ID for the user worked for me.

RUN usermod -o -u 1000 <user>

I agree with @sdwolf. We have the same workflow.

IMHO, it's not the role of a base image to make some assumptions on the execution environment. When a specific user is not absolutely required, like in this case, using a non-root user should be of the final user's responsibility and not of the base image.

@syfonseq I followed the lead of most docker core images such as postgres who define a default user to avoid running command in elevated mode.

@LaurentGoderre is it OK for me to make a PR that documents the existence of the node user in the README.md and what workarounds can be used in case it creates problems to others as well (removing or renaming it)?

Maybe this will help calm everybody down. I agree that removing it now might create more harm to people that already started using it or who apply the workarounds themselves.

Also, postgres is a different kind of beast and I don't think it's fair to compare with it.

@sdwolf Absolutely.

I'm curious to know why you think the comparison isn't fair.

AFAIK postgres uses the system's user and password to authenticate connections, so it's kind of a run-time dependency, at least this is how I usually had it set up when i installed it on an Ubuntu server.

I guess not everyone feels like the Node image should be used as is in Production but I feel like it's a valid use case to get the image, mount your project in, install the dependencies and push to a production server without having to create an intermediary image.

This is a breaking change. It鈥檚 a bit of a tricky position to be in because the image version number is tracking node, not this project, but there should be a plan for stuff like this going forward to avoid impacting users without warning. Maybe a special tag to force explicit selection?

Production ready images is standard practice for official Docker Images. There should be no need to create intermediate images for standard use cases.

Closing as the corresponding doc updates landed a while ago: https://github.com/nodejs/docker-node/commit/c3694d8baa8a95472c007af5cadbb0068b81618e

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kmleow picture kmleow  路  5Comments

frankbaele picture frankbaele  路  3Comments

dionysiusmarquis picture dionysiusmarquis  路  3Comments

marciomsm picture marciomsm  路  4Comments

polys picture polys  路  3Comments