How do I add a user when I'm using Alpine as a base image?
Asked Answered
L

4

278

I'm using alpine (or an image that is based on Alpine) as the base image in my Dockerfile. Which instructions do I need to add to create a user?

Eventually I'll use this user to run the application I'll place into the container so that the root user does not.

Lewert answered 21/4, 2018 at 11:4 Comment(0)
L
469

Alpine uses the command adduser and addgroup for creating users and groups (rather than useradd and usergroup).

FROM alpine:latest

# Create a group and user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Tell docker that all future commands should run as the appuser user
USER appuser

The flags for adduser are:

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          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)

Add new user official docs

Lewert answered 21/4, 2018 at 11:4 Comment(8)
Or alternatively, you can replace the whole snippet above using this: USER 405 which is the guest user within Alpine Linux.Lewert
Why not USER guest?Caporetto
I'd go with creating a new user because I want that user to have the same UID/GID as the one on the host OS, so that there's no permission issue when running docker in Linux. (not an issue with macOS/Windows users)Cogitate
Note that since Alpine is based on BusyBox, its adduser and addgroup commands are different from adduser and addgroup as provided by Debian and Ubuntu which in turn are front ends to useradd and groupadd. Notably, the Debian and Ubuntu commands only support long form options. See: manpages.debian.org/stretch/adduser/adduser.8.en.htmlShakhty
I've already created a docker container, now what command do I need to run to add a user?Acentric
Or Alternatively, you can use github.com/docker-library/httpd/issues/…Racehorse
Why are these short hand flags not documented in the official docs? edit: apparently this command is added by busybox: busybox.net/downloads/BusyBox.htmlIdentity
What's a "system" user?Thou
I
162

The commands are adduser and addgroup.

Here's a template for Docker you can use in busybox environments (alpine) as well as Debian-based environments (Ubuntu, etc.):

ENV USER=docker
ENV GROUPNAME=$USER
ENV UID=12345
ENV GID=23456

RUN addgroup \
    --gid "$GID" \
    "$GROUPNAME" \
&&  adduser \
    --disabled-password \
    --gecos "" \
    --home "$(pwd)" \
    --ingroup "$GROUPNAME" \
    --no-create-home \
    --uid "$UID" \
    $USER

Note the following:

  • --disabled-password prevents prompt for a password
  • --gecos "" circumvents the prompt for "Full Name" etc. on Debian-based systems
  • --home "$(pwd)" sets the user's home to the WORKDIR. You may not want this.
  • --no-create-home prevents cruft getting copied into the directory from /etc/skel

The usage description for these applications is missing the long flags present in the code for adduser and addgroup.

The following long-form flags should work both in alpine as well as debian-derivatives:

adduser

BusyBox v1.28.4 (2018-05-30 10:45:57 UTC) multi-call binary.

Usage: adduser [OPTIONS] USER [GROUP]

Create new user, or add USER to GROUP

        --home DIR           Home directory
        --gecos GECOS        GECOS field
        --shell SHELL        Login shell
        --ingroup GRP        Group (by name)
        --system             Create a system user
        --disabled-password  Don't assign a password
        --no-create-home     Don't create home directory
        --uid UID            User id

One thing to note is that if --ingroup isn't set then the GID is assigned to match the UID. If the GID corresponding to the provided UID already exists adduser will fail.

addgroup

BusyBox v1.28.4 (2018-05-30 10:45:57 UTC) multi-call binary.

Usage: addgroup [-g GID] [-S] [USER] GROUP

Add a group or add a user to a group

        --gid GID  Group id
        --system   Create a system group

I discovered all of this while trying to write my own alternative to the fixuid project for running containers as the hosts UID/GID.

My entrypoint helper script can be found on GitHub.

The intent is to prepend that script as the first argument to ENTRYPOINT which should cause Docker to infer UID and GID from a relevant bind mount.

An environment variable "TEMPLATE" may be required to determine where the permissions should be inferred from.

(At the time of writing I don't have documentation for my script. It's still on the todo list!!)

Incoming answered 19/4, 2019 at 6:21 Comment(9)
+1, using long form for command args increases readability and makes maintenance easier. When writing shell scripts always use the long form (Dockerfile RUN is nothing else than a shell script).Cyndie
Great answer, and thank for sharing your script. I am interested in retaining the host uid/gid: on debian based images my approach consisted in passing these over through env variables or infer from a bound workspace folder. All this was made easier by using commands useradd/groupadd with the --non-unique flag. Is there anyway to create duplicate id users/groups in alpine?Loritalorn
I found an answer to my question: installing shadow.Loritalorn
Using this in an entrypoint is probably not sufficient, because you start the container as root. What stops the user from exiting back to root?Glycol
Isn't --ingroup "$USER" supposed to be --ingroup "$GID"? I'm confused.Herv
Is RUN adduser even necessary? Why don't we just use USER 10001:10001 in the Dockerfile?Adieu
@Herv you're right. Without that, the build will fail (and $GID would be useless). The group needs to exist already, tooViral
@Herv as it turns out I edited my post and left that part of the code a bit unclear. The flag --ingroup specifies a group by name. The flag --gid is intended for specifying a group number. In both cases the group must already exist. In the short term I'll update my answer with a GROUPNAME. A clearer answer would also include addgroup however my original answer failed to run.Incoming
@Viral --ingroup accepts a group name only, it will fail if you use a GID. As Alpine's Busybox adduser command does not support the --gid option, --ingroup is the only way to configure the default group across platforms as far as I can tell.Incoming
M
16

There is package shadow that brings useradd & usermod.

adduser has some stupid limitations:

$ sudo adduser --disabled-password root
adduser: user 'root' in use

but usermod doesn't:

$ sudo apk add shadow
$ sudo usermod --unlock root
Manthei answered 4/8, 2021 at 12:31 Comment(1)
shadow also includes groupadd too and makes alpine consistent in this regard with other distros so if you have a multi distro script (e.g. docker) using these then just install shadow when spinning up an alpine container and your script will work anywhere.Wrongdoer
B
3

Group and User Setup:

Create default values for User ID (UID), Group ID (GID), and username, which can be customized using arguments.

ARG USER_UID=1000
ARG GROUP_GID=1000
ARG UGNAME=webapp

Remove Existing Group and User:

Check for the presence of an existing group and user. If either exists, remove them.

RUN if getent passwd ${USER_UID} >/dev/null; then \
    deluser $(getent passwd ${USER_UID} | cut -d: -f1); fi

RUN if getent group ${GROUP_GID} >/dev/null; then \
    delgroup $(getent group ${GROUP_GID} | cut -d: -f1); fi

Create New Group and User:

Create a new system group and a new system user with the specified UID, GID, and username.

RUN addgroup --system --gid ${GROUP_GID} ${UGNAME}

RUN adduser --system --disabled-password --home /home/${UGNAME} \
    --uid ${USER_UID} --ingroup ${UGNAME} ${UGNAME}

Set the user as the default user for the container. All subsequent commands will run with this user.

USER ${UGNAME}
Boulanger answered 7/10, 2023 at 5:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.