Reputation: 2030
I'm trying to share files within a Docker guest using the volume sharing. In order to get the same UID, and therefore interoperability with those files, I would like to create a user in the Docker guest with the same UID as my own user.
In order to test out the idea, I wrote the following simplistic Dockerfile:
FROM phusion/baseimage
RUN touch /root/uid-$UID
Testing it with docker build -t=docktest .
and then docker run docktest ls -al /root
reveals that the file is simply named uid-
.
Is there a means to share host environment variables with Docker during the guest build process?
Upvotes: 22
Views: 14061
Reputation: 181
Although there is an accepted answer it does not work for me. I needed the same uid:gid for all the files mounted (with bind) from the host file system in the docker container. It took me nearly 2 days to figure out why docker can't do this and how I can do this nevertheless. I leave it here if someone faces the same issues:
Docker volume/mounted binds have a significant downside: They statically change ownership: user(1000) becomes root(0) and www-data(33) becomes nobody(65534). And they do not allow to change ownership in the container. Thus access conflicts are mandatory.
This is known as user namespacing. It is a security feature to prevent privilege-escalation attacks. It can be disabled for containers via the --userns=host
. However:
There is a side effect when using this flag: user remapping will not be enabled for that container but, because the read-only (image) layers are shared between containers, ownership of the containers filesystem will still be remapped.
The wrong mapping occurs on docker desktop only (at least at docker desktop version 4.13.1 (90346)). If I use the docker daemon only, the mapping is always correct.
To circumvent this issue we can use battle proven linux server mounting programms like sshfs.
First we need to access our host machine from the container via ssh. Therefore, an ssh server has to be installed on the host (with passwd or ssh-keys configured) and an ssh client on the container.
Host:
apt-get update
apt-get install openssh-server -y
Container:
apt-get update
apt-get install ssh sshfs -y
First we check in the container if the ssh connection is working via ssh [email protected]
.
If it does we configure in the container a sshfs mount.
sshfs -o allow_other,IdentityFile=/home/$(whoami)/.ssh/id_rsa [email protected]:/host/source/path /container/target/path
If you do not want to supply your password you can leave the containers public ssh key on the host e.g. via ssh-copy-id
. Then:
sshfs -o allow_other,IdentityFile=/home/$(whoami)/.ssh/id_rsa [email protected]:/host/source/path /container/target/path
The sync is almost instant. And the permission mapping is correct even if you create files from within the container.
Hint: We need to run container with the --privileged
flag.
Since we have to run the container in the privileged mode, root
in the container is the same as root
on the host. The same applies for every other user.
Thus, for everything we run in that container we have to put up the same security measures as it would run directly on the host.
We have to run an ssh-server on the host, which has (of course) to be targeted by security measures.
Best practice: Use mounting of host directories for development only.
Upvotes: 0
Reputation: 2028
While researching a solution to this problem, I have found the following article to be a great resource: https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf
In my scripts, the solution boiled down to the following :
docker run --user $(id -u):$(id -g) -v /hostdirectory:/containerdirectory -v /etc/passwd:/etc/passwd myimage
Of course, id -u
can be replaced by other means of retrieving a user's gid, such as stat -c "%u" /somepath
Upvotes: 28
Reputation: 22680
The environment is not shared, you could use -e, --env options to set env variables in container.
I usually use this approach when I want to have the same owner of the mapped volume: I check uid & gid of directory in container and then create a corresponding user. Here my script (setuser.sh) which creates a user for a directory:
#!/bin/bash
setuser() {
if [ -z "$1" ]; then
echo "Usage: $0 <path>"
return
fi
CURRENT_UID=`id -u`
DEST_UID=`stat -c "%u" $1`
if [ $CURRENT_UID = $DEST_UID ]; then
return
fi
DEST_GID=`stat -c "%g" $1`
if [ -e /home/$DEST_UID ]; then
return
fi
groupadd -g $DEST_GID $DEST_GID
useradd -u $DEST_UID -g $DEST_GID $DEST_UID
mkdir -p /home/$DEST_UID
chown $DEST_UID:$DEST_GID /home/$DEST_UID
}
setuser $1
And this is the wrapper script which runs commands as the user, where the directory with permissions is specified either as $USER_DIR or in /etc/user_dir
#!/bin/bash
if [ -z "$USER_DIR" ]; then
if [ -e /etc/user_dir ]; then
export USER_DIR=`head -n 1 /etc/user_dir`
fi
fi
if [ -n "$USER_DIR" ]; then
if [ ! -d "$USER_DIR" ]; then
echo "Please mount $USER_DIR before running this script"
exit 1
fi
. `dirname $BASH_SOURCE`/setuser.sh $USER_DIR
fi
if [ -n "$USER_DIR" ]; then
cd $USER_DIR
fi
if [ -e /etc/user_script ]; then
. /etc/user_script
fi
if [ $CURRENT_UID = $DEST_UID ]; then
"$@"
else
su $DEST_UID -p -c "$@"
fi
P.S. Alleo suggested different approach: to map users and groups files into container and to specify uid and gid. So your container does not depend on built-in users/groups you could use it without additional scripts.
Upvotes: 11
Reputation: 25846
I slightly modified @ISanych answer:
#!/usr/bin/env bash
user_exists() {
id -u $1 > /dev/null 2>&1
}
group_exists() {
id -g $1 > /dev/null 2>&1
}
setuser() {
if [[ "$#" != 3 ]]; then
echo "Usage: $0 <path> <user> <group>"
return
fi
local dest_uid=$(stat -c "%u" $1)
local dest_gid=$(stat -c "%g" $1)
if user_exists $dest_uid; then
id -nu $dest_uid
return
fi
local dest_user=$2
local dest_group=$3
if user_exists $dest_user; then
userdel $dest_user
fi
if group_exists $dest_group; then
groupdel $dest_user
fi
groupadd -g $dest_gid $dest_group
useradd -u $dest_uid -g $dest_gid -s $DEFAULT_SHELL -d $DEFAULT_HOME -G root $dest_user
chown -R $dest_uid:$dest_gid $DEFAULT_HOME
id -nu $dest_user
}
REAL_USER=$(setuser $SRC_DIR $DEFAULT_USER $DEFAULT_GROUP)
setuser
function accepts user and group names that you want to assign to uid and gid of provided directory. Then if user with such uid exists then it simply returns login corresponding to this uid, otherwise it creates user and group and returns login originally passed to function.
So you get the login of user that owns destination directory.
Upvotes: 1
Reputation: 18669
This is not possible and will probably never be possible because of the design philosophy of keeping builds independent of machines. Issue 6822.
Upvotes: 1