Skip to content

creemama/docker-run-non-root

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

run-non-root

Run Docker containers with a non-root user by default.

Travis CI Build Status run-non-root Version run-non-root on Docker Hub

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.

Supported tags and respective Dockerfile links

Examples

run-non-root

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

Examples

# 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

Docker and run-non-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.

tini

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