Run Docker containers with a non-root user by default.
run-non-root
runs Linux commands as a non-root user, creating a non-root user if necessary.
This allows us to
run Docker containers with a non-root user by default
without having to specify a USER
with hardcoded UIDs and GIDs in our Dockerfiles.
1.5.1-alpine
,1.5-alpine
,1-alpine
,1.5.1
,1.5
,1
,latest
(alpine/Dockerfile)1.5.1-centos
,1.5-centos
,1-centos
,centos
(centos/Dockerfile)1.5.1-debian
,1.5-debian
,1-debian
,debian
(debian/Dockerfile)1.5.1-fedora
,1.5-fedora
,1-fedora
,fedora
(fedora/Dockerfile)1.5.1-ubuntu
,1.5-ubuntu
,1-ubuntu
,ubuntu
(ubuntu/Dockerfile)
Examples
1.5.1-certbot
(certbot/Dockerfile)1.5.1-certbot-renew-cron
(certbot-renew-cron/Dockerfile)1.5.1-node-10.15.3
(node/Dockerfile)
run-non-root is a shell script that runs Linux commands as a non-root user.
Usage:
run-non-root [options] [--] [COMMAND] [ARGS...]
Run Linux commands as a non-root user, creating a non-root user if necessary.
Options:
-c, --chown Colon-separated list of files and directories to run
"chown USERNAME:GID" on before executing the
command; you can use this option multiple times
instead of using a colon-separated list; run-non-root
ignores this option if you are already running as a
non-root user; unlike -p this option is non-recursive.
-d, --debug Output debug information; using --quiet does not
silence debug output. Double up (-dd) for more output.
-f, --group GROUP_NAME The group name to use when executing the command; the
default group name is USERNAME or nonroot; this
option is ignored if we are already running as a
non-root user or if the GID already exists; this
option overrides the RUN_NON_ROOT_GROUP environment
variable.
-g, --gid GID The group ID to use when executing the command; the
default GID is UID or a new ID determined by
groupadd; this option is ignored if we are already
running as a non-root user; this option overrides the
RUN_NON_ROOT_GID environment variable.
-h, --help Output this help message and exit.
-i, --init Run an init (the tini command) that forwards signals
and reaps processes; this matches the docker run
option --init.
-p, --path Colon-separated list of directories to run
"chown -R USERNAME:GID" on before executing the
command; you can use this option multiple times
instead of using a colon-separated list; if a
directory does not exist, run-non-root attempts to
create it; run-non-root ignores this option if you
are already running as a non-root user; unlike -c
this option is recursive.
-q, --quiet Do not output "Running ( COMMAND ) as USER_INFO ..."
or warnings; this option does not silence --debug
output.
-t, --user USERNAME The username to use when executing the command; the
default is nonroot; this option is ignored if we are
already running as a non-root user or if the UID
already exists; this option overrides the
RUN_NON_ROOT_USER environment variable.
-u, --uid UID The user ID to use when executing the command; the
default UID is GID or a new ID determined by
useraddd; this option is ignored if we are already
running as a non-root user; this option overrides the
RUN_NON_ROOT_UID environment variable.
-v, --version Ouput the version number of run-non-root.
Environment Variables:
RUN_NON_ROOT_COMMAND The command to execute if a command is not given; the
default is bash; if bash does not exist, the default
is sh.
RUN_NON_ROOT_GID The group ID to use when executing the command; see
the --gid option for more info.
RUN_NON_ROOT_GROUP The group name to use when executing the command; see
the --group option for more info.
RUN_NON_ROOT_UID The user ID to use when executing the command; see
the --uid option for more info.
RUN_NON_ROOT_USER The username to use when executing the command; see
the --user option for more info.
Examples:
# Run bash or sh as a non-root user.
run-non-root
# Run id as a non-root user.
run-non-root -- id
# Run id as a non-root user using options and the given user specification.
run-non-root -f ec2-user -g 1000 -t ec2-user -u 1000 -- id
# Run id as a non-root user using environment variables
# and the given user specification.
export RUN_NON_ROOT_GID=1000
export RUN_NON_ROOT_GROUP=ec2-user
export RUN_NON_ROOT_UID=1000
export RUN_NON_ROOT_USER=ec2-user
run-non-root -- id
# Run sh as a non-root user.
docker run -it --rm creemama/run-non-root:latest
# Output: Running ( su-exec nonroot:1000 sh ) as uid=1000(nonroot) gid=1000(nonroot) groups=1000(nonroot) ...
# Run id as a non-root user.
docker run -it --rm creemama/run-non-root:latest --q -- id
# Output: uid=1000(nonroot) gid=1000(nonroot) groups=1000(nonroot)
# Run id as a non-root user using options and the given user specification.
docker run -it --rm creemama/run-non-root:latest \
-f ec2-user -g 1000 -q -t ec2-user -u 1000 -- id
# Output: uid=1000(ec2-user) gid=1000(ec2-user) groups=1000(ec2-user)
# Run id as a non-root user using environment variables
# and the given user specification.
docker run \
-e RUN_NON_ROOT_GID=1000 \
-e RUN_NON_ROOT_GROUP=ec2-user \
-e RUN_NON_ROOT_UID=1000 \
-e RUN_NON_ROOT_USER=ec2-user \
-it --rm creemama/run-non-root:latest \
-q -- id
# Output: uid=1000(ec2-user) gid=1000(ec2-user) groups=1000(ec2-user)
# Run as yourself.
docker run -it --rm creemama/run-non-root:latest \
-f $(id -gn) -g $(id -g) -t $(id -nu) -u $(id -u) \
-- id
# Run as root if you need to.
docker run -it --rm creemama/run-non-root:latest -qu 0 -- whoami
# Output: root
As we all know, processes in containers should not run as root.
There are several approaches to run as a non-root user.
Specify a USER
in your Dockerfile
One approach is to create a user via useradd
and specify a USER
in your Dockerfile.
FROM debian:stretch
RUN groupadd -g 999 appuser && \
useradd -r -u 999 -g appuser appuser
USER appuser
CMD ["cat", "/tmp/secrets.txt"]
The upside to this approach is that the container, by default, runs as appuser
instead of root.
The downside is that appuser
has a specific UID and GID that you cannot change without some work.
Specify a UID when starting your container
Another approach is to use the -u
or --user
option.
docker run -it --rm \
--user $(id -u):$(id -g) \ # Run as the given user.
--volume $(app):/app \ # Mount the source code.
--workdir /app \ # Set the working dir.
my-docker/my-build-environment:latest \ # The build image
make assets # The command
The upside to this approach is that you have control of the specific UID and GID of the user running the container.
The downside is that your user may be HOME-less and nameless. In other words,
the user might have no home directory and whoami
might not find a name for the user.
Basically, unless the UID you specified is in the getent passwd
list, your container does not know about the user you specified.
Specify run-non-root
as your ENTRYPOINT
Using run-non-root
as the ENTRYPOINT
of your container overcomes the downsides of the aforementioned approaches.
From a base image:
FROM alpine:3.9
...
ADD https://raw.githubusercontent.com/creemama/run-non-root/master/run-non-root.sh /usr/local/bin/run-non-root
RUN chmod +rx /usr/local/bin/run-non-root
ENTRYPOINT ["run-non-root"]
CMD ["--", "/your/program", "-and", "-its", "arguments"]
From one of run-non-root's images:
FROM creemama/run-non-root:1.5.1-alpine
...
CMD ["--", "/your/program", "-and", "-its", "arguments"]
With this approach, you do not have to specify USER
in your Dockerfile or use the --user
option when calling docker run
. Your container runs as a non-root user by default.
If run-non-root
creates the non-root user (which is nonroot by default), this user will have a home directory, and whoami
will return that user's name.
Use run-non-root
in conjunction with tini
to handle zombie reaping and signal forwarding by using the --init
option.
$ docker run -it --rm creemama/run-non-root:latest --init -q -- ps aux
PID USER TIME COMMAND
1 nonroot 0:00 tini -- ps aux
17 nonroot 0:00 ps aux