Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker Feature: "postCreateUser" to specify user during "postCreateCommand" #3283

Closed
diricxbart opened this issue Jun 30, 2020 · 12 comments
Closed
Labels
containers Issue in vscode-remote containers *question Issue represents a question, should be posted to StackOverflow (VS Code)

Comments

@diricxbart
Copy link

Feature request

Allow to specify a user, using a new "postCreateUser" setting, which is used to execute the "postCreateCommand".
This user may be a different user then the "remoteUser" or the "containerUser".

This would allow to connect to the container remotely using the "postCreateUser", execute the "postCreateCommand" and next connect to the container using the "remoteUser".

Example use case

The use case for this is the following:

  • Docker daemon running on a Linux server
  • VSCode running on a Windows PC
  • An SSH tunnel is established between both, by launching the following from the Windows PC: ssh remote_server_name -L localhost:2375:/var/run/docker.sock -N
  • The Docker image has a user 'custom_user' with UID and GID both set to 2000
  • A .devcontainer.json file based launching of the VSCode remote connection

In this setup we first want to map the 'custom_user' to the correct UID and GID of the user in the Linux server.
However changing the UID and GID requires 'root' permissions (or sudo). And editing files in VSCode as root, screws up the file permissions. Alternative might be changing UID and GID using 'custom_user' and sudo, but also here experiments have shown that changing the UID / GID of the active connected user also corrupts things.

As a fix for this, we would like to first execute a "postCreateCommand" which changes the UID and GID, while specifying the "postCreateUser" as "root". Next we would want the 'regular' VSCode connection to be made using the user 'custom_user', with the correct UID and GID.

We would specify the following in our devcontainer.json:

    "runArgs": [
        "-u",
        "custom_user"
    ],
    "postCreateCommand": "/user_mapping.sh ${localEnv:VSCODE_USER_ID} ${localEnv:VSCODE_GROUP_ID}",
    "postCreateUser": "root"

In the Docker image $USER would be set to "custom_user" and the following /user_mapping.sh would also be present:

#!/bin/bash
USER_ID=$1
USER_GID=$2

LINE=$(grep -F "${USER}" /etc/passwd)
# replace all ':' with a space and create array
array=( ${LINE//:/ } )

# home is 5th element
USER_HOME=${array[4]}

sed -i -e "s/^${USER}:\([^:]*\):[0-9]*:[0-9]*/${USER}:\1:${USER_ID}:${USER_GID}/"  /etc/passwd
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*/${USER}:\1:${USER_GID}/"  /etc/group

chown -R ${USER_ID}:${USER_GID} ${USER_HOME}

This was originally requested here, but has now been moved into a dedicated feature request upon request of @chrmarti .

@github-actions github-actions bot added the containers Issue in vscode-remote containers label Jun 30, 2020
@chrmarti chrmarti added the feature-request Request for new features or functionality label Jun 30, 2020
@diricxbart
Copy link
Author

@chrmarti Would you already know if this is added to the (short-term) roadmap?

@chrmarti
Copy link
Contributor

No, not currently. We have a fix for this when you run VS Code on Linux and connect to a local container ("updateRemoteUserUID": true is the default for Linux). I wonder if we could reuse that.

@diricxbart
Copy link
Author

Has the usage of updateRemoteUserUID been evaluated in the meantime as means to solve this issue? Any experiments I could help with?

@chrmarti
Copy link
Contributor

Unfortunately not, no.

Have you tried running the user_mapping.sh as part of the Dockerfile? You can use "build": { "args": { ... } } in the devcontainer.json to pass build arguments, that also supports substitutions like ${localEnv:VSCODE_USER_ID} in its values.

@diricxbart
Copy link
Author

Building our docker takes about 30 minutes on our, rather powerful, build server. This is controlled by a Jenkins pipeline and the result is pushed to JFrog artifactory. Our .devcontainer.json is pulling this image from artifactory. Building the docker locally is not really an option.

So what we need is 'some way' to start the container (as root) execute a command (to install the user) and from there on continue...

At some point I thought that the 'initializeCommand' could help us, but this is executed before the container is created... We would need something to be executed (as root) just after the point where the container is created.

@PavelSosin-320
Copy link

@diricxbart What do you mean when saying "Container is created"? Docker image create as a part of docker build or Docker container create as a part of the docker run command. I suppose, docker container create. At this moment entry point script or program is executed as it is defined in CMD and ENTRYPOINT directives of the dockerfile. VSCode has nothing to do with it because VSCode is neither container builder nor container runner. Docker container is not EXE or VM to execute a kind of user login. All its resources including users and access control it borrows from the host OS/VM on which the Docker engine runs. The creation of a new user during the container lifetime means a violation of the container isolation principle. Running a container with the default root user means that the container has unrestricted access to the host resources.
USER directive meaning is only the default user. --user option of the docker run command overrides this default.
This is predefined OCI container behavior.
The flexibility of Docker container execution is provided by CMD and ENTRYPOINT directives in the dockerfile.
The difference between CMD and ENTRYPOINT is that CMD can be overridden during docker run and ENTRYPOINT can't.
Both can be executed as root or another user but I afraid the creation of a new user is neither possible nor helpful.
ENTRYPOINT script has to do all the job which login does. It has both script and exec form. ENTRYPOINT accepts parameters provided to the docker run command.

@chrmarti
Copy link
Contributor

@diricxbart You can build the image on the build server and then pull it in with a Dockerfile that only runs your user_mapping.sh.

@PavelSosin-320
Copy link

@diricxbart All additional users except root have to be added to the image, not the container on the build server using useradd. The USER command doesn't create a user.
User _root (id = 0) is the default user within a container. The image developer can create additional users. Those users are accessible by name. When passing a numeric ID, the user does not have to exist in the container.

The developer can set a default user to run the first process with the Dockerfile USER instruction. When starting a container, the operator can override the USER instruction by passing the -u option with docker run command explicitly.

-u="", --user="": Sets the username or UID used and optionally the groupname or GID for the specified command.

The followings examples are all valid:
--user=[ user | user:group | uid | uid:gid | user:gid | uid:group ]
Note: if you pass a numeric uid, it must be in the range of 0-2147483647._
When the container runs locally on PC/laptop root user is OK in most cases.
When the container runs on the shared server User/Group ID(!) is used for Host machine resource access: files, Devices, Ports, etc.
All necessary environment variables can be passed to the container too from outside using --env option of the run command.
Are you still need post-create command?
I suppose that post-create is not taken into account in OCI standard because it assumes very fast container creation from the image and everything needed can be passed to the container from outside.

@diricxbart
Copy link
Author

The "custom_user" that I'm referring to above is added to the container image, indeed using useradd:

RUN groupadd --gid ${USER_GID} ${USER} && \
    useradd --uid ${USER_ID} --gid ${USER_GID} --create-home ${USER} && \
    echo "lumos ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
    cat /etc/sudoers

With USER_ID and USER_GID both set to 2000 and USER to "custom_user".

What we then want to accomplish is to be able to run this container as user "custom_user", but with the USER_ID and USER_GID set to the values of the user on the remote server (outside of the docker container) instead of 2000.

The reason for this is that we use volume mounts to access specific (sensitive) data, for which access permissions are linked to the user. If "custom_user" inside the docker container uses the uid/gid of the user outside of the container, than we can R/W the data with the permissions of this user.

I don't think that the -u flag supports running as a "custom_user" with the User/Group ID of the user on the shared server (id -u / id -g output), correct?

If there's a way to accomplish the same without the post-create command, then I of course want to explore this...

@chrmarti That could indeed be done locally, an option to consider...

@diricxbart
Copy link
Author

diricxbart commented Dec 14, 2020

@chrmarti I gave your suggestion a try and this is indeed working!

Some details:

I created a Dockerfile containing:

FROM <path_to_my_docker_base_image>
ARG HOST_USER_ID
ARG HOST_USER_GID

ENV HOST_USER_ID=$HOST_USER_ID
ENV HOST_USER_GID=$HOST_USER_GID
RUN /user_mapping.sh

Then I modified the .devcontainer.json:

  1. Remove the "image" entry and instead specify the "build" entry pointing to the above Dockerfile:
    "build": {
        "args": { "HOST_USER_ID": "${localEnv:VSCODE_USER_ID}", "HOST_USER_GID": "${localEnv:VSCODE_GROUP_ID}" },
        "dockerfile": "${localWorkspaceFolderBasename}/../../Dockerfile"
    },
  1. Use "custom_user" instead of "root" user:
    "runArgs": [
        "-u",
        "custom_user",

All seems to work as expected (id -u, id-g, file permission on volume mounts, ...), this is great...
Thank you very much!

Just an idea: if other people would need similar functionality, it might be interesting to have VSCode do all this for you. To add a command like "installUser": {"userName": ..., "userId": ..., "groupId": ...} which does the useradd, groupadd, user mapping, ...

For me this feature request can be closed (unless you prefer to keep it open for the "installUser" idea)...

@chrmarti
Copy link
Contributor

Thanks for the feedback @diricxbart! Closing, I suggest to open new issues for any remaining feature requests to keep the discussions separate.

@chrmarti chrmarti added *question Issue represents a question, should be posted to StackOverflow (VS Code) and removed feature-request Request for new features or functionality labels Dec 16, 2020
@PavelSosin-320
Copy link

Security consideration: be careful with USER in the dockerfile: the stolen using docker pull image will mean the stolen user. It can't belong to any powerful group. Such a container can be stored only locally or local registry. The "testuser" deafult value is common

@github-actions github-actions bot locked and limited conversation to collaborators Jan 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
containers Issue in vscode-remote containers *question Issue represents a question, should be posted to StackOverflow (VS Code)
Projects
None yet
Development

No branches or pull requests

3 participants