Skip to content
This repository has been archived by the owner on Jan 15, 2023. It is now read-only.

Best Practices for Non-root User #48

Closed
styfle opened this issue Jul 7, 2016 · 16 comments
Closed

Best Practices for Non-root User #48

styfle opened this issue Jul 7, 2016 · 16 comments

Comments

@styfle
Copy link

styfle commented Jul 7, 2016

First of all, thanks for creating and maintaining such a wonderful docker image! You're always so quick to update when a new version of node comes out 💯. And now for my question...

In the official docker-node repo, there is a Best Practices doc that explains you should create a user instead of running as root.

# Add our user and group first to make sure their IDs get assigned consistently
RUN groupadd -r app && useradd -r -g app app 

Does alpine-node have a similar best practice?
What is the equivalent groupadd and useradd in Alpine?

@mhart
Copy link
Owner

mhart commented Jul 7, 2016

Try adduser and addgroup – lemme know if that works

@styfle
Copy link
Author

styfle commented Jul 7, 2016

I think I figured it out. The -S flag will add to system group which is equivalent to -r in Ubuntu.

RUN addgroup -S app && adduser -S -G app app 

@styfle
Copy link
Author

styfle commented Jul 7, 2016

Should I add a best practices to this repo? Or are we close to getting alpine in the official docker node and just wait to add it there? See docker-node/pull/156.

@mhart
Copy link
Owner

mhart commented Jul 7, 2016

Yeah, let's wait for the official image – I'm sure they'll be getting that in soon

Thanks for highlighting this though and finding the appropriate command line options!

@mhart mhart closed this as completed Jul 7, 2016
@peterpme
Copy link

Hey @styfle just wanted to stop by and say thank you for sharing this! I'm thinking about curating a list of best practices for docker / alpine because I'm personally having a hard time making the right decisions.

@styfle
Copy link
Author

styfle commented Jan 6, 2017

@peterpme I am working with the official docker-node image to get these practices updated.
See linked PR 99 above.

@ORESoftware
Copy link

adduser isn't available on Alpine out of the box?

@mhart
Copy link
Owner

mhart commented Sep 9, 2017

@ORESoftware yeah it is – what version you running?

$ docker run alpine:3.6 adduser
BusyBox v1.26.2 (2017-06-11 06:38:32 GMT) multi-call binary.

Usage: adduser [OPTIONS] USER [GROUP]

Create new user, or add USER to GROUP

	-h DIR		Home directory
	-g GECOS	GECOS field
	-s SHELL	Login shell
	-G GRP		Add user to existing group
	-S		Create a system user
	-D		Don't assign a password
	-H		Don't create home directory
	-u UID		User id
	-k SKEL		Skeleton directory (/etc/skel)

$ docker run alpine:3.4 adduser
BusyBox v1.24.2 (2017-01-18 14:13:46 GMT) multi-call binary.

Usage: adduser [OPTIONS] USER [GROUP]

Create new user, or add USER to GROUP

	-h DIR		Home directory
	-g GECOS	GECOS field
	-s SHELL	Login shell
	-G GRP		Add user to existing group
	-S		Create a system user
	-D		Don't assign a password
	-H		Don't create home directory
	-u UID		User id
	-k SKEL		Skeleton directory (/etc/skel)

@evaporei
Copy link

evaporei commented Feb 1, 2018

For some reason using the command @styfle posted, my user stayed at the nogroup.
I tested a lot and this worked:

RUN addgroup -S myawesomegroup
RUN adduser -S myawesomeuser -G myawesomegroup

:)

@Exadra37
Copy link

Exadra37 commented Mar 3, 2018

For latest Alpine image, the 3.7 the correct command is:

RUN addgroup -g 1000 -S username && \
    adduser -u 1000 -S username -G username

Setting the UID to 1000 ensures we will not run in permissions issues when mapping volumes from our computer to the running container, once 1000 is the first UID assigned to a non root user in Linux, at least in Debian and Ubuntu ;)

Testing on Alpine 3.7

$ sudo docker run --rm -it alpine:3.7 sh                                                       
Unable to find image 'alpine:3.7' locally
3.7: Pulling from library/alpine
Digest: sha256:7b848083f93822dd21b0a2f14a110bd99f6efb4b838d499df6d04a49d0debf8b
Status: Downloaded newer image for alpine:3.7
/ # 
/# addgroup -g 1000 -S username  && adduser -u 1000 -S username -G username
/#
/ # grep username /etc/passwd
username:x:1000:1000:Linux User,,,:/home/username:/bin/false
/ # 
/ # grep username /etc/group
username:x:1000:username
/ # 

@binarymist
Copy link

@Exadra37

Setting the UID to 1000 ensures we will not run in permissions issues when mapping volumes from our computer to the running container, once 1000 is the first UID assigned to a non root user in Linux, at least in Debian and Ubuntu ;)

docker-compose up  
Building orchestrator
Step 1/13 : FROM node:10-alpine
 ---> 7ca2f9cb5536
Step 2/13 : ENV user orchestrator
 ---> Using cache
 ---> bb0d334f29c6
Step 3/13 : ENV workdir /usr/src/app/
 ---> Using cache
 ---> a14d0251cf82
Step 4/13 : RUN addgroup -g 1000 -S $user && adduser -u 1000 -S -G $user $user
 ---> Running in 8545a3ed4d32
addgroup: gid '1000' in use
ERROR: Service 'orchestrator' failed to build: The command '/bin/sh -c addgroup -g 1000 -S $user && adduser -u 1000 -S -G $user $user' returned a non-zero code: 1

@elquimista
Copy link

elquimista commented Oct 18, 2018

Here's what I do using docker-compose:

Dockerfile

FROM node:carbon-alpine
LABEL author="el que m'est"

ARG UID
ARG GID

USER root
RUN apk add --no-cache shadow sudo && \
    if [ -z "`getent group $GID`" ]; then \
      addgroup -S -g $GID cetacean; \
    else \
      groupmod -n cetacean `getent group $GID | cut -d: -f1`; \
    fi && \
    if [ -z "`getent passwd $UID`" ]; then \
      adduser -S -u $UID -G cetacean -s /bin/sh mobydick; \
    else \
      usermod -l mobydick -g $GID -d /home/mobydick -m `getent passwd $UID | cut -d: -f1`; \
    fi && \
    echo "mobydick ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/mobydick && \
    chmod 0440 /etc/sudoers.d/mobydick

WORKDIR /home/mobydick/app
RUN chown mobydick:cetacean /home/mobydick/app
USER mobydick

docker-compose.yml

version: '3'
services:
  app:
    build:
      context: .
      args:
        UID: ${UID}
        GID: ${GID}
    command: sh
    tty: true
    stdin_open: true
    volumes:
      - .:/home/mobydick/app
    ports:
      - ${PORT}:3000

.env

PORT=4000

package.json

{
  "scripts": {
    "dc": "GID=$(id -g) UID=$(id -u) docker-compose",
    "dc-build": "yarn dc build"
  }
}

Finally I run yarn dc-build to build a docker image.

@Exadra37
Copy link

Exadra37 commented Oct 18, 2018

@binarymist

addgroup: gid '1000' in use

As the error points out the group id 1000 is already in use.

This happens because the node image already as the user node with id 1000 and group id 1000.

╭─exadra37@exadra37-Vostro-470 ~  
╰─➤  sudo docker run --rm --user 1000 -it node:10-alpine sh
/ $ id
uid=1000(node) gid=1000(node) groups=1000(node)
/ $ cat /etc/passwd | grep -irn 1000 -
29:node:x:1000:1000:Linux User,,,:/home/node:/bin/sh
/ $ cat /etc/group | grep -irn 1000 -
49:node:x:1000:node

So no need for you to add your user when extending a node image, unless id is not 1000.

The @elquimista solution works with any user and group id and you can use it without docker compose, like:

sudo docker run --rm --user $(id -u) --env UID=$(id -u) --env GID=$(id -g) -it node:10-alpine sh

@binarymist
Copy link

binarymist commented Oct 19, 2018

@Exadra37

As the error points out the group id 1000 is already in use.

Yeah, that's my point.

@elquimista
Your solution is looking good (terse, readable, not too clever). The best I could find until this was the suggested (https://denibertovic.com/posts/handling-permissions-with-docker-volumes/) which has a few problems of it's own (ncopa/su-exec#2 (comment))
Nice!

Just a couple of questions:

Why is mobydick added to the sudoers?
Why is mobydick able to run ALL commands as root? In your example there don't appear to be any commands following the sudoers insertion that require root perms.

@elquimista
Copy link

elquimista commented Oct 19, 2018

@binarymist

  1. Adding mobydick to the sudoers list is generally not needed for images that are distributed to end consumers (i.e. they just run container from image and that's it). However, if you are a developer doing lots of development stuff on your local machine, you will find it convenient that mobydick is one of sudoers, when it comes to installing extra packages you might need for development, e.g., installing certain npm packages/ruby gems might require additional software packages installed.
    You can do this inside Dockerfile and rebuild the image, but that takes longer than doing it on the fly inside the current running container. (I am not saying doing it inside container is enough - you will still need to update Dockerfile accordingly about adding new packages.)

  2. In the Dockerfile I demonstrated above, all commands are run in the capacity of root user until it meets the line USER mobydick. All commands that come after this line will be assumed to run as mobydick.

@binarymist
Copy link

Yip, understand that, good work!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants