This question already has answers here:
Share files between host system and docker container using specific UID
(4 answers)
Closed 7 years ago.
I'm using Docker (1.3.1) to build RPMs inside a container:
docker run -v /home/matt/build:/build build-rpm /build/build-pkg.sh
This works fine (my user is in the docker group, so I don't need to sudo) and drops a completed .rpm file in the current directory. The problem is that the file is created as owned by root.
How can I arrange it so that the file is created owned by the same user as I run docker with?
You could try to create (in the Dockerfile of a custom image) a user and set it as the one used by the container
RUN adduser --system --group --shell /bin/sh auser \
&& mkdir /home/auser/bin
USER auser
Then check if a docker run -v /home/matt/build:/build build-rpm mounts the shared folder in /build as auser.
Another option mentioned in issue 2259:
If you chown the volume (on the host side) before bind-mounting it, it will work.
In that case, you could do:
mkdir /tmp/www
chown 101:101 /tmp/www
docker run -v /tmp/www:/var/www ubuntu stat -c "%U %G" /var/www
(Assuming that 101:101 is the UID:GID of the www-data user in your container.)
Docker runs as root and has no idea what your user is inside its virtual environment (even if you're in the sudoers group). But you can create a non-root user while building your docker image that can be called whatever you like.
# create a non-root user named tester,
# give them the password "tester" put them in the sudo group
RUN useradd -d /home/tester -m -s /bin/bash tester && echo "tester:tester" | chpasswd && adduser tester sudo
# start working in the "tester" home directory
WORKDIR /home/tester
COPY ./src
# Make the files owned by tester
RUN chown -R tester:tester /home/tester
# Switch to your new user in the docker image
USER tester
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
I am trying to build a Kafka-Connect image in Docker:
FROM confluentinc/cp-kafka-connect
RUN confluent-hub install --no-prompt wepay/kafka-connect-bigquery:1.6.1
RUN confluent-hub install --no-prompt confluentinc/connect-transforms:latest
RUN mkdir -p /usr/share/landoop-plugins
COPY kafka-connect-redis-1.2.2-2.1.0-all.jar /usr/share/landoop-plugins/
but it runs as appuser
Step 4/4 : RUN id
---> Running in d2094f6336a7
uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)
so if I want for example
RUN mkdir -p /usr/share/landoop-plugins
it stops because of root privilages:
mkdir: cannot create directory '/usr/share/landoop-plugins': Permission denied
The command '/bin/sh -c mkdir -p /usr/share/landoop-plugins' returned a non-zero code: 1
I can add USER root at the beginning of Dockerfile:
Step 3/15 : RUN id
---> Running in 6255e2e7ff81
uid=0(root) gid=0(root) groups=0(root)
but then if I ran the container, I am logged as appuser which causes problems with permissions:
[appuser#connect ~]$.
Actually, in source image
confluentinc/cp-kafka-connect:6.0.0
there is a layer USER appuser so my question is how can I build my image as root and then login as root and do not use appuser user. I've tried and USER root does not help.
Is it somehow connected with groups?
groups
gignac sudo docker
I tried docker build and sudo docker build as well
I do something similar, when I need to create my own plugin to Kafka Connect but I don't exactly do it as root.
Simply I put my jars in a place I have permission to write and just configure the plugins Environment Setting
something like this:
FROM confluentinc/cp-kafka-connect:5.5.2
ENV CONNECT_PLUGIN_PATH='/usr/share/java,/usr/share/confluent-hub-components'
COPY converter/* /usr/share/java/kafka-serde-tools/
COPY format/* /usr/share/java/kafka-connect-storage-common/
would this not work?
For installing packages and troubleshooting, we would require root user. Using version 5.5.3 does the trick wherein the APPUSER is not created and root is loaded by default.
The version can be verified by
http://localhost:8083/connectors
which provides the version details.
I'm trying a simple workflow without success and it take me a loooooot of time to test many solutions on SO and github. Permission for named folder and more generaly permissions volume in docker is a nightmare link1 link2 imho.
So i restart from scratch, trying to create a simple proof of concept for my use case.
I want this general workflow :
user on windows and/or linux build the Dockerfile
user run the container (if possible not as root)
the container launch a crontab which run a script writing in the data volume each minute
users (on linux or windows) get the results from the data volume (not root) because permissions are correctly mapped
I use supercronic because it runs crontab in container without root permission.
The Dockerfile :
FROM artemklevtsov/r-alpine:latest as baseImage
RUN mkdir -p /usr/local/src/myscript/
RUN mkdir -p /usr/local/src/myscript/result
COPY . /usr/local/src/myscript/
WORKDIR /usr/local/src/myscript/
RUN echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
RUN apk --no-cache add busybox-suid curl
ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.$
SUPERCRONIC=supercronic-linux-amd64 \
SUPERCRONIC_SHA1SUM=9aeb41e00cc7b71d30d33c57a2333f2c2581a201
RUN curl -fsSLO "$SUPERCRONIC_URL" \
&& echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \
&& chmod +x "$SUPERCRONIC" \
&& mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
&& ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
CMD ["supercronic", "crontab"]
The crontab file :
* * * * * sh /usr/local/src/myscript/run.sh > /proc/1/fd/1 2>&1
The run.sh script
#!/bin/bash
name=$(date '+%Y-%m-%d-%s')
echo "some data for the file" >> ./result/fileName$name
The commands :
# create the volume for result, uid/gid option are not possible for windows
docker volume create --name myTestVolume
docker run --mount type=volume,source=myTestVolume,destination=/usr/local/src/myscript/result test
docker run --rm -v myTestVolume:/alpine_data -v $(pwd)/local_backup:/alpine_backup alpine:latest tar cvf /alpine_backup/scrap_data_"$(date '+%y-%m-%d')".tar /alpine_data
When i do this the result folder local_backup and files it contains has root:root permissions, so user who launch this container cannot access the files.
Is there a solution which works, which permits windows/linux/mac users who launch the same script to access easily the files into volume without problem of permissions ?
EDIT 1 :
The strategy first described here only work with binded volume, and not named volume. We use an entrypoint.sh to chown uid/gid of folders of container based on information given by docker run.
I copy paste the modified Dockerfile :
FROM artemklevtsov/r-alpine:latest as baseImage
RUN mkdir -p /usr/local/src/myscript/
RUN mkdir -p /usr/local/src/myscript/result
COPY . /usr/local/src/myscript/
ENTRYPOINT [ "/usr/local/src/myscript/entrypoint.sh" ]
WORKDIR /usr/local/src/myscript/
RUN echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
RUN apk --no-cache add busybox-suid curl su-exec
ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.$
SUPERCRONIC=supercronic-linux-amd64 \
SUPERCRONIC_SHA1SUM=9aeb41e00cc7b71d30d33c57a2333f2c2581a201
RUN curl -fsSLO "$SUPERCRONIC_URL" \
&& echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \
&& chmod +x "$SUPERCRONIC" \
&& mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
&& ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
CMD ["supercronic", "crontab"]
The entrypoint.sh
#!/bin/sh
set -e
addgroup -g $GID scrap && adduser -s /bin/sh -D -G scrap -u $UID scrap
if [ "$(whoami)" == "root" ]; then
chown -R scrap:scrap /usr/local/src/myscript/
chown --dereference scrap "/proc/$$/fd/1" "/proc/$$/fd/2" || :
exec su-exec scrap "$#"
fi
The procedure to build,launch, export:
docker build . --tag=test
docker run -e UID=1000 -e GID=1000 --mount type=volume,source=myTestVolume,destination=/usr/local/src/myscript/result test
docker run --rm -v myTestVolume:/alpine_data -v $(pwd)/local_backup:/alpine_backup alpine:latest tar cvf /alpine_backup/scrap_data_"$(date '+%y-%m-%d')".tar /alpine_data
EDIT 2 :
For Windows, using docker toolbox and binded volume, i found the answer on SO. I use the c:/Users/MyUsers folder for binding, it's more simple.
docker run --name test -d -e UID=1000 -e GID=1000 --mount type=bind,source=/c/Users/myusers/localbackup,destination=/usr/local/src/myscript/result dockertest --name rflightscraps
Result of investigation
crontab run with scrap user [OK]
UID/GID of local user are mapped to container user scrap [OK]
Exported data continue to be root [NOT OK].
Windows / Linux [HALF OK]
If i use bind volume and not a named volume, it works. But this is not the desired behavior, how can i use the named volume with correct permission on Win/Linux ...
Let me divide the answer into two parts Linux Part and Docker part. You need to understand both in order to solve this problem.
Linux Part
It is easy to run cronjobs as user other than root in Linux.
This can be achieved by creating a user in docker container with the same UID as of that in the host machine and copying the crontab file as /var/spool/cron/crontabs/user_name.
From man crontab
crontab is the program used to install, deinstall or list the
tables used to drive the cron(8) daemon in Vixie Cron. Each user can
have their own crontab, and though these are files in
/var/spool/cron/crontabs, they are not intended to be edited directly.
Since Linux identifies users by User Id, inside docker the UID will be bound to the newly created user whereas in host machine the same will be binded with host user.
So, You don't have any permission issue as the files is owned by the host_user. Now you would have understood why I mentioned creating user with same UID as of that in host machine.
Docker Part
Docker considers all the directories(or layers) to be UNION FILE SYSTEM. Whenever you build an image each instruction creates a layer and the layer is marked as read-only. This is the reason Docker containers doesn't persist data. So you have to explicitly tell docker that some directories need to persist data by using VOLUME keyword.
You can run containers without mentioning volume explicitly. If you do so, docker daemon considers them to be UFS and resets the permissions.
In order to preserve the changes to a file/directory including ownership. The respective file should be declared as Volume in Dockerfile.
From UNION FILE SYSTEM
Indeed, when a container has booted, it is moved into memory, and the boot filesystem is unmounted to free up the RAM used by the initrd disk image. So far this looks pretty much like a typical Linux virtualization stack. Indeed, Docker next layers a root filesystem, rootfs, on top of the boot filesystem. This rootfs can be one or more operating systems (e.g., a Debian or Ubuntu filesystem).
Docker calls each of these filesystems images. Images can be layered on top of one another. The image below is called the parent image and you can traverse each layer until you reach the bottom of the image stack where the final image is called the base image. Finally, when a container is launched from an image, Docker mounts a read-write filesystem on top of any layers below. This is where whatever processes we want our Docker container to run will execute. When Docker first starts a container, the initial read-write layer is empty. As changes occur, they are applied to this layer; for example, if you want to change a file, then that file will be copied from the read-only layer below into the read-write layer. The read-only version of the file will still exist but is now hidden underneath the copy.
Example:
Let us assume that we have a user called host_user. The UID of host_user is 1000. Now we are going to create a user called docker_user in Docker container. So I'll assign him UID as 1000. Now whatever files that are owned by docker_user in Docker container is also owned by host_user if those files are accessible by host_user from host(i.e through volumes).
Now you can share the binded directory with others without any permission issues. You can even give 777 permission on the corresponding directory which allows others to edit the data. Else, You can leave 755 permissions which allows others to copy but only the owner to edit the data.
I've declared the directory to persist changes as a volume. This preserves all changes. Be careful as once you declare a directory as volume further changes made to that directory while building the will be ignored as those changes will be in separate layers. Hence do all your changes in the directory and then declare it as volume.
Here is the Docker file.
FROM alpine:latest
ARG ID=1000
#UID as arg so we can also pass custom user_id
ARG CRON_USER=docker_user
#same goes for username
COPY crontab /var/spool/cron/crontabs/$CRON_USER
RUN adduser -g "Custom Cron User" -DH -u $ID $CRON_USER && \
chmod 0600 /var/spool/cron/crontabs/$CRON_USER && \
mkdir /temp && \
chown -R $ID:$ID /temp && \
chmod 777 /temp
VOLUME /temp
#Specify the dir to be preserved as Volume else docker considers it as Union File System
ENTRYPOINT ["crond", "-f", "-l", "2"]
Here is the crontab
* * * * * /usr/bin/whoami >> /temp/cron.log
Building the image
docker build . -t test
Create new volume
docker volume create --name myTestVolume
Run with Data volume
docker run --rm --name test -d -v myTestVolume:/usr/local/src/myscript/result test:latest
Whenever you mount myTestVolume to other container you can see the
data under /usr/local/src/myscript/result is owned by UID 1000
if no user exist with that UID in that container or the username of
corresponding UID.
Run with Bind volume
docker run --rm --name test - -dv $PWD:/usr/local/src/myscript/result test:latest
When you do an ls -al /home/host_user/temp You will see that file called cron.log is created and is owned by **host_user**.
The same will be owned by docker_user in docker container when you do an ls -al /temp. The contents of cron.log will be docker_user.
So, Your effective Dockerfile should be
FROM artemklevtsov/r-alpine:latest as baseImage
ARG ID=1000
ARG CRON_USER=docker_user
RUN adduser -g "Custom Cron User" -DH -u $ID $CRON_USER && \
chmod 0600 /var/spool/cron/crontabs/$CRON_USER && \
echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories && \
apk --no-cache add busybox-suid curl && \
mkdir -p /usr/local/src/myscript/result && \
chown -R $ID:$ID /usr/local/src/myscript/result && \
chmod 777 /usr/local/src/myscript/result
COPY crontab /var/spool/cron/crontabs/$CRON_USER
COPY . /usr/local/src/myscript/
VOLUME /usr/local/src/myscript/result
#This preserves chown and chmod changes.
WORKDIR /usr/local/src/myscript/
ENTRYPOINT ["crond", "-f", "-l", "2"]
Now whenever you attach a Data/bind volume to /usr/local/src/myscript/result it will be owned by user having UID 1000 and the same is persistent across all the containers whichever has mounted the same volume with their corresponding user with 1000 as file owners.
Please Note: I've given 777 permissions in order to share with every one. You can skip that step in your Dockerfle based on your convinence.
References:
Crontab manual.
User identiier - Wiki.
User ID Definition.
About storage drivers.
UNION FILE SYSTEM.
I have Dockerfile as shown here.
A script in the entrypoint creates a directory and places few artifacts.
# from base image
FROM ......
RUN mkdir -p /home/myuser
RUN groupadd -g 999 myuser &&\
useradd -r -u 999 -g myuser myuser
ENV HOME=/home/myuser
ENV APP_HOME=/home/myuser/workspace
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
RUN chown -R myuser:myuser $APP_HOME
USER myuser
ENTRYPOINT ......
I start a container for the above image as shown here
sudo docker run -v ${WORKSPACE}/output:/home/myuser/workspace/output image
I could not get the artifacts in the host machine. ${WORKSPACE}/output created with permission drwxr_xr_x
What is the process to get the container files into the host machine?
Additional Info:
My host username is kit
container user is myuser
container works perfectly fine - at the time of creating output file it throws an error that Permission denied
I tried to give full permission drwxrwxrwx to ${WORKSPACE}/output. then i could see the output files.
The permission denied error is because you are running a container with uid 999, but trying to write to a host directory that is owned by uid 1000 and only configured to allow writes by the user. You can:
chmod the directory to allow anyone to write (not recommended, but quick and easy)
update your image to match the uid/gid of your user on the host
switch to using a named volume
use an entrypoint to align the container uid/gid to that of a volume mount before starting your app
I go into a bit more detail on these in my slides here. There are also some speaker notes in there (I believe either P or S will bring them up).