devcontainers / spec

Development Containers: Use a container as a full-featured development environment.
https://containers.dev
Creative Commons Attribution 4.0 International
3.56k stars 228 forks source link

UID/GID sync scripts #25

Open Chuxel opened 2 years ago

Chuxel commented 2 years ago

Using bind mounts on Linux (inclusive of WSL) can be confusing since the actual file descriptors from the host are used - inclusive of all permissions. The problem is that the user in the container - whether root or otherwise, can have different UID/GIDs than the host user - which means that files in the source folder can be unreadable or unwritable on either the host or the container depending on the situation. While you can use a volume to house source code instead of bind mounts, its useful to be able to use local source code mounts as well and can be manditory in CI/Automation scenarios as #9 is released.

The spec and reference CLI includes an updateRemoteUserUID property that defaults to true to deal with this problem which automatically updates a specified non-root user (in containerUser or rootUser).

However, while this does automatically update permissions in that user's $HOME directory in the container, it does not update other locations. This is problematic in a number of cases, but particularly when packages or other content the user needs to work with is not located there.

Generally, the needed update is a chown -R on a directory, but this is not always the case - and more subtle updates can often be better (e.g. using find /location -uid 1000 -execdir chown username-here {} \+). To provide a nuanced way to handle this, I'd propose a model similar to #19 where either Dockerfiles or dev container features can add a script to a known location. When a processor like the dev container CLI then executes a UID/GID update, these bash scripts are fired to make the needed updates. These would run as root and should be owned by root as a part of final image prep much like the usermod/groupmod commands when this step is needed.

bhack commented 2 years ago

We could consider also https://github.com/moby/moby/issues/42134#issuecomment-1004811749

Chuxel commented 2 years ago

@bhack Namespace remapping can actually make things worse since you have to run the container as root for bind mounting to work. At that point, any tool that dislikes running as root will fail.

Cloning into a volume instead avoids that particular problem, but it's also less convenient for quick actions and work when the source is local. I haven't found a slam dunk for how to resolve that particular issue. Namespace mapping also breaks certain things like docker-in-docker that are quite common.

If you are running namespace mapped, you can disable UID sync'ing via the updateRemoteUserUID in devcontainer.json property already.

I wish there was an optional remapping on bind mounts but there isn't right now. 🤷‍♂️ Doesn't seem to be a perfect answer near as I can tell yet. Love to know if anyone finds one.

bhack commented 2 years ago

@bhack Namespace remapping can actually make things worse since you have to run the container as root for bind mounting to work. At that point, any tool that dislikes running as root will fail.

Are you talking about the idmapping part of the mentioned thread?

This could (probably) be made easier if support for idmapped mounts would be added, but this requires very recent kernel versions (see https://github.com/moby/moby/issues/2259 and https://github.com/moby/moby/issues/2259#issuecomment-785147028 for a longer discussion)

P.s. See also https://github.com/moby/moby/issues/28593#issuecomment-1153701031

Chuxel commented 2 years ago

@bhack Namespace remapping can actually make things worse since you have to run the container as root for bind mounting to work. At that point, any tool that dislikes running as root will fail.

Are you talking about the idmapping part of the mentioned thread?

This could (probably) be made easier if support for idmapped mounts would be added, but this requires very recent kernel versions (see moby/moby#2259 and moby/moby#2259 (comment) for a longer discussion)

Yeah the referenced comment is making a statement about the design for the docker daemon running as something other than root. Running as non-root actually makes wanting to run as non-root inside the container harder - and that makes this overall situation worse. If you're cool running your container as root and you don't need elevated container privs it works fine. One thing I haven't tried is to see if you can run the daemon as some other user than your user to see if the sync'ing can kick in correctly in that case.

Or did I misunderstand the reference in your comment?

bhack commented 2 years ago

Running as non-root actually makes wanting to run as non-root inside the container harder - and that makes this overall situation worse.

Yes this is the current case of docker rootless case with non-root user in the image.

But with future idmppaing mount support (https://github.com/moby/moby/issues/28593#issuecomment-1153701031) cannot we map the mount in the container with the user we want?

bhack commented 2 years ago

I don't know if you could test it with podman as it seems mereged there https://github.com/containers/podman/pull/12298

Chuxel commented 2 years ago

Yes this is the current case of docker rootless case with non-root user in the image.

But with future idmppaing mount support (moby/moby#28593 (comment)) cannot we map the mount in the container with the user we want?

Got it, yeah, once that is there the situation probably changes, I agree. Some of the comments from May in 2259 are encouraging. Fingers crossed that lands. We'd still need a fallback for kernels < 5.12, but that would narrow the problem and speed the whole process up.

chrmarti commented 2 years ago

On using rootless: To use a non-root user (containeruser) inside the container in rootless mode we need two users (root and the non-root user) inside the container mapped to two users on the host (at least that is my understanding). Can we use a secondary host user (hostuser-root) belonging to the primary host user (hostuser)? The mapping would be hostuser-root > root and hostuser > containeruser. Files would be owned by hostuser and containeruser. We would need sudo set up for hostuser to run processes as hostuser-root (e.g., to start the container). This might avoid the need for updating the UID/GID completely.

bhack commented 2 years ago

I don't know if this is still valid and/or the same on Docker:

https://www.redhat.com/sysadmin/rootless-podman-makes-sense

chrmarti commented 2 years ago

Reading the article, it looks like there is only one regular host user per container, all others map to >100000 host UIDs.

felipecrs commented 2 years ago

I have a suggestion for now: allow users to provide their own updateRemoteUserUID scripts through custom features (in this case we are only missing the exposure of some additional ARGs/ENVs like LOCAL_USER, LOCAL_UID, LOCAL_GID, and LOCAL_HOME).

For example, I use docker-on/from-docker in all my devcontainers, and I would like to perform some additional remappings to the user within the container instead of only changing its UID/GID. For example, I'd like to change the username from vscode (example) to $LOCAL_USER and the /home/vscode to $LOCAL_HOME as well.

This would be super nice for me. :)