Created an application image on top of ubuntu. The application requires a non-root user.
I am able to create the image and get it working but the RUN statement for creating new user significantly increases the size of this image.
Dockerfile snippet:
## create newuser and give correct permissions to home directory
RUN useradd newuser --create-home --shell /bin/bash && \
echo 'newuser:newpassword' | chpasswd && \
chown -R newuser:newuser /home/newuser && \
chmod 755 /home/newuser
USER newuser
WORKDIR /home/newuser
Is there a better way to create a new user?
Thinking about alternate approaches, wonder if one uses multi-stage build to create this new user and then use copy --from to get relevant files in the final build. Not sure what those files would be.
Don't chown the directory; leave root owning it. You also shouldn't need to set a shell, or a password, or create a home directory; none of these things will be used in normal operation.
I'd suggest creating the user towards the start of the Dockerfile (it is fairly fixed and so this step can be cached) but only switching USER at the very end of the file, when you're setting up the metadata for how to run the container.
A Node-based example:
FROM node:lts # debian-based
# Create the non-root user up front
RUN adduser --system --group --no-create-home newuser
# Copy and build the package as usual
WORKDIR /app
COPY package.json yarn.lock .
RUN yarn install
COPY . .
RUN yarn build
# Now the application is built
# And root owns all the files
# And that's fine
# Say how to run the container
EXPOSE 3000
USER newuser
CMD yarn start
Having root owning the files gives you a little extra protection in case something goes wrong. If there's a bug that allows files in the container to be overwritten, having a different user owning those files prevents the application code or static assets from being inadvertently modified.
If your application needs to read or write files then you could create a specific directory for that:
# Towards the end of the file, but before the USER
RUN mkdir data && chown newuser data
This will let the operator mount some storage over the otherwise-empty directory. This is the only thing that has the newly created user ID in it at all, so if the storage comes with its own owner it shouldn't be an operational problem; you need to also specify the matching user ID at container startup time.
docker run -u $(id -u) -v $PWD/data:/app/data ...
Related
I am building an image using Dockfile. I would like to set the Username of the container via the command line to avoid permission issues.
The Dockfile is shown below, I used the variables of USER_NAME, GROUP_ID. But when I build, the problem keeps appearing.
The error is: groupadd: option '--gid' requires an argument
I'm guessing that both ${GROUP_ID} and ${USER_NAME} are recognized as empty strings, but shouldn't they be assigned values when the container is created?
I've googled a few examples and based on the examples, I don't quite see where the problem is?
Please help me!
Thanks!
FROM matthewfeickert/docker-python3-ubuntu:latest
ARG USER_NAME
ARG USER_ID
ARG GROUP_ID
RUN groupadd -r --gid ${GROUP_ID} ${USER_NAME}
RUN useradd --no-log-init -r -g ${GROUP_ID} -u ${USER_ID} ${USER_NAME}
USER ${USER_NAME}
WORKDIR /usr/local/src
When you run the container, you can specify an arbitrary user ID with the docker run -u option.
docker run -u 1003 ... my-image
This doesn't require any special setup in the image. The user ID won't exist in the container's /etc/passwd file but there aren't really any consequences to this, beyond some cosmetic issues with prompts in interactive debugging shells.
A typical use of this is to give your container access to a bind-mounted data directory:
docker run \
-e DATA_DIR=/data \
-v "$PWD/app-data:/data" \
-u $(id -u) \
... \
my-image
I'd generally recommend not passing a specific user ID into your image build. This would make the user ID "baked in", and if someone with a different host uid wanted to run the image, they'd have to rebuild it.
It's often a good practice to set up some non-root user, but it doesn't matter what its user ID is so long as it's not zero. In turn, it's also typically a good practice to leave most of your application source code owned by the root user so that the application can't accidentally overwrite itself.
FROM matthewfeickert/docker-python3-ubuntu:latest
# Create an arbitrary non-root user; we don't care about its uid
# or other properties
RUN useradd --system user
# Still as root, do the normal steps to install and build the application
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY ./ ./
# Still as root, make sure the data directory exists
ENV DATA_DIR=/data
RUN mkdir "$DATA_DIR" && chown user "$DATA_DIR"
# VOLUME ["/data"]
# Normal metadata to run the container, only switching users now
EXPOSE 5000
USER user
CMD ["./app.py"]
This setup will still work with the extended docker run command shown initially: the docker run -v option will cause the container's /data directory to take on its numeric uid owner from the host, which (hopefully) matches the docker run -u uid.
You can pass the build args as shown below.
docker build --build-arg USER_NAME=test --build-arg USER_ID=805 --build-arg GROUP_ID=805 -t tag1 .
Also, as a best practice consider adding default vales to the args. So if the user doesn't specify the args the default values will be picked.
New here, was wondering if someone had experience with building images as non root user?
I am building Kotlin project, (2 step build) and my goal is now to build it as non root user. Here is what my Dockerfile looks like. Any help would be appreciated:
# Build
FROM openjdk:11-jdk-slim as builder
# Compile application
WORKDIR /root
COPY . .
RUN ./gradlew build
FROM openjdk:11-jre-slim
# Add application
COPY --from=builder /root/build/libs/*.jar ./app.jar
# Set the build version
ARG build_version
ENV BUILD_VERSION=$build_version
COPY docker-entrypoint.sh /
RUN chmod 777 /docker-entrypoint.sh
CMD /docker-entrypoint.sh
In order to use Docker, you don't need to be a root user, you just need to be inside of the docker user group.
On Linux:
If there is not already a docker group, you can create one using the command sudo groupadd docker.
Add yourself and any other users you would like to be able to access docker to this group using the command sudo usermod -aG docker [username of user].
Relog, so that Linux can re-evaluate user groups.
If you are not trying to run the command as root, but rather want to run the container as non-root, you can use the following DOCKERFILE contents (insert after FROM but before anything else.)
# Add a new user "john" with user id 8877
RUN useradd -u 8877 john
# Change to non-root privilege
USER john
As you know, for security reasons, isn't good to use root user execept if you need it. I have this Dockerfile that I use with multi-stage steps
FROM golang:latest AS base
WORKDIR /usr/src/app
# Create User and working dir
RUN addgroup --gid 42000 app
RUN useradd --create-home --uid 42000 --gid app app
RUN chown -R app:app /usr/src/app
RUN chmod 755 /usr/src/app
# Compile stage based on Debian
FROM base AS builder
USER app
# Copy form computer to current WORKDIR container
COPY . .
# Exit immediately if a command exits with a non-zero status
RUN set -xue && \
make go-build-linux
# Final stage
FROM debian:latest
USER app
EXPOSE 14001
RUN apt-get update && \
apt-get install -y ca-certificates
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/server .
CMD ["./server"]
The problem is that I'm trying to reuse the user in all steps but seems to be that the user scope is by stage and I don't know how to reuse it.
Do you know how I can reuse a user in a multi-stage Dockerfile and try to avoid to use root user from Dockerfile?
Thanks!
TL;DR;
It is not possible to re-use the same user in multiple stages of the docker build without re-creating the user (same UID and GID at least) in each stage as each FROM is starting from a clean slate FROM image in which a user UID=42000 and GID=42000 is unlikely to already exist.
I am not aware of any recommendation against building as the root user inside a container. It is recommended to run services as unprivileged users however certain containers processes must be run as the root user (i.e. sshd):
The best way to prevent privilege-escalation attacks from within a container is to configure your container’s applications to run as unprivileged users. For containers whose processes must run as the root user within the container, you can re-map this user to a less-privileged user on the Docker host. The mapped user is assigned a range of UIDs which function within the namespace as normal UIDs from 0 to 65536, but have no privileges on the host machine itself.
Tip: The Haskell Dockerfile Linter will complain if the last user is root which you can configure as a git pre-commit hook to catch things like that before committing teh codez.
Seems like a basic issue but couldnt find any answers so far ..
When using ADD / COPY in Dockerfile and running the image on linux, the default file permission of the file copied in the image is 644. The onwner of this file seems to be as 'root'
However, when running the image, a non-root user starts the container and any file thus copied with 644 permission cannot execute this copied/added file and if the file is executed at ENTRYPOINT it fails to start with permission denied error.
I read in one of the posts that COPY/ADD after Docker 1.17.0+ allows chown but in my case i dont know who will be the non-root user starting so i cannot set the permission as that user.
I also saw another work around to ADD/COPY files to a different location and use RUN to copy them from the temp location to actual folder like what am doing below. But this approach doesnt work as the final image doesnt have the files in /otp/scm
#Installing Bitbucket and setting variables
WORKDIR /tmp
ADD atlassian-bitbucket-${BITBUCKET_VERSION}.tar.gz .
COPY bbconfigupdater.sh .
#Copying Entrypoint script which will get executed when container starts
WORKDIR /tmp
COPY entrypoint.sh .
RUN ls -lrth /tmp
WORKDIR /opt/scm
RUN pwd && cp /tmp/bbconfigupdater.sh /opt/scm \
&& cp /tmp/entrypoint.sh /opt/scm \
&& cp -r /tmp/atlassian-bitbucket-${BITBUCKET_VERSION} /opt/scm \
&& chgrp -R 0 /opt/ \
&& chmod -R 755 /opt/ \
&& chgrp -R 0 /scm/bitbucket \
&& chmod -R 755 /scm/bitbucket \
&& ls -lrth /opt/scm && ls -lrth /scmdata
Any help is appreciated to figure out how i can get my entrypoint script copied to the desired path with execute permissions set.
The default file permission is whatever the file permission is in your build context from where you copy the file. If you control the source, then it's best to fix the permissions there to avoid a copy-on-write operation. Otherwise, if you cannot guarantee the system building the image will have the execute bit set on the files, a chmod after the copy operation will fix the permission. E.g.
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh
A better option with newer versions of docker (and which didn't exist when this answer was first posted) is to use the --chmod flag (the permissions must be specified in octal at last check):
COPY --chmod=0755 entrypoint.sh .
You do not need to know who will run the container. The user inside the container is typically configured by the image creator (using USER) and doesn't depend on the user running the container from the docker host. When the user runs the container, they send a request to the docker API which does not track the calling user id.
The only time I've seen the host user matter is if you have a host volume and want to avoid permission issues. If that's your scenario, I often start the entrypoint as root, run a script called fix-perms to align the container uid with the host volume uid, and then run gosu to switch from root back to the container user.
A --chmod flag was added to ADD and COPY instructions in Docker CE 20.10. So you can now do.
COPY --chmod=0755 entrypoint.sh .
To be able to use it you need to enable BuildKit.
# enable buildkit for docker
DOCKER_BUILDKIT=1
# enable buildkit for docker-compose
COMPOSE_DOCKER_CLI_BUILD=1
Note: It seems to not be documented at this time, see this issue.
While building a Docker image, how do I COPY a file into the image so that the resulting file is owned by a user other than root?
For versions v17.09.0-ce and newer
Use the optional flag --chown=<user>:<group> with either the ADD or COPY commands.
For example
COPY --chown=<user>:<group> <hostPath> <containerPath>
The documentation for the --chown flag is now live on the main Dockerfile Reference page.
Issue 34263 has been merged and is available in release v17.09.0-ce.
For versions older than v17.09.0-ce
Docker doesn't support COPY as a user other than root. You need to chown / chmod the file after the COPY command.
Example Dockerfile:
from centos:6
RUN groupadd -r myuser && adduser -r -g myuser myuser
USER myuser
#Install code, configure application, etc...
USER root
COPY run-my-app.sh /usr/local/bin/run-my-app.sh
RUN chown myuser:myuser /usr/local/bin/run-my-app.sh && \
chmod 744 /usr/local/bin/run-my-app.sh
USER myuser
ENTRYPOINT ["/usr/local/bin/run-my-app.sh"]
Previous to v17.09.0-ce, the Dockerfile Reference for the COPY command said:
All new files and directories are created with a UID and GID of 0.
History
This feature has been tracked through multiple GitHub issues: 6119, 9943, 13600, 27303, 28499, Issue 30110.
Issue 34263 is the issue that implemented the optional flag functionality and Issue 467 updated the documentation.