Running Singularity container without root as different user inside - linux

I am trying to run a Docker container with Singularity that executes a Windows program (msconvert from proteowizard) via Wine. This image: https://hub.docker.com/r/chambm/pwiz-skyline-i-agree-to-the-vendor-licenses
The Wine installation inside the Docker container is owned by root. Running with Singularity on a standard user account with
singularity run --env WINEDEBUG=-all pwiz.sif wine msconvert
results in:
wine: '/wineprefix64' is not owned by you
So I must use the --fakeroot option to overcome this.
HOWEVER, on the remote HPC system I wish to run the container I cannot use --fakeroot because it is not allowed by the system admins:
FATAL: while extracting pwiz.sif: root filesystem extraction failed: extract command failed: FATAL: configuration disallows users from running sandbox based containers
My workaround is to add a %post to build the container and change the ownership of the directory to the user of the remote system, then rebuild the Singularity image on a machine I have root access. Simply:
Bootstrap: docker
From: chambm/pwiz-skyline-i-agree-to-the-vendor-licenses
%post
useradd -u 1234 user
chown -Rf --no-preserve-root user /wineprefix64
This works for me. But my question is, is there a better way to generalise this so any unprivileged user can run this without having to manually re-build it for their username?

Related

Docker Bind Mount: error while creating mount source path, permission denied

I am trying to run the NVIDIA PyTorch container nvcr.io/nvidia/pytorch:22.01-py3 on a Linux system, and I need to mount a directory of the host system (that I have R/W access to) in the container. I know that I need to use bind mounts, and here's what I'm trying:
I'm in a directory /home/<user>/test, which has the directory dir-to-mount. (The <user> account is mine).
docker run -it -v $(pwd)/dir-to-mount:/workspace/target nvcr.io/nvidia/pytorch:22.01-py3
Here's the error output:
docker: Error response from daemon: error while creating mount source path '/home/<user>/test/dir-to-mount': mkdir /home/<user>/test: permission denied.
ERRO[0000] error waiting for container: context canceled
As far as I know, docker will only need to create the directory to be mounted if it doesn't exist already. Docker docs:
The file or directory does not need to exist on the Docker host already. It is created on demand if it does not yet exist.
I suspected that maybe the docker process does not have access; I tried chmod 777 with dir-to-mount as well as with test, but that made no difference.
So what's going wrong?
[Edit 1]
I am able to mount my user's entire home directory with the same command, but cannot mount other directories inside the home directory.
[Edit 2]
Here are the permissions:
home directory: drwx------
test: drwxrwxrwx
dir-to-mount: drwxrwxrwx
Run the command with sudo as:
sudo docker run -it -v $(pwd)/dir-to-mount:/workspace/target nvcr.io/nvidia/pytorch:22.01-py3
It appears that I can mount my home directory as a home directory (inside of /home/<username>), and this just works.
docker run -it -v $HOME:$HOME nvcr.io/nvidia/pytorch:22.01-py3
I don't know why the /home/<username> path is special, I've tried looking through the docs but I could not find anything relevant.

How do I add the local users to my docker container?

I want to know how I can add the local users of my server to a docker container. I don't need to import their files, I just need a username/password/privileges with new home directory in the docker container for every user in my system. For example, suppose my docker container contains the following users:
Host System:
admin: who has root access and rw access to all
bob: a regular non-sudo user
joe: another regular non-sudo user
Then the Docker Container must have users:
admin: who has root access and rw access to all
bob: a regular non-sudo user
joe: another regular non-sudo user
The Docker container and the system are both running linux, though the system is red hat and the container is ubuntu.
EDIT: I don't want to mount /etc/ files if possible, as this can create a two way security vulnerability as pointed out by #caveman
You would have to mount all relevant linux files using -v like /etc/passwd, /etc/shadow, /ect/group, and /etc/sudoers. Though I can't recommend this due to the security risks, if anyone gets root access in the container they can add users on the host or change passwords since he mount works both ways.
The list of files is not exhaustive, for example, you have to also make sure the shell exacutables exist within the container. When testing this I had to make a symbolic link from /usr/bin/zsh to /bin/bash for example since my user has the zsh shell configured which was not present in the docker image.
If you want to use these users to interact with mounted files, you also have to make sure that user namespace remapping is disabled, or specify that you want to use the same user namespace as the host with the --userns=host flag. Again, not recommended since it is a security feature, so use with care.
Note: Once you have done all this you can use su - {username} to switch to all your existing users. The -u options doesn't work since docker checks the /etc/passwd file before mounting and will give an error.

Running containerized application as non-root - Sanity check

I read this article about why you shouldn't run containerized applications as root user,
and I'd like someone to confirm my understanding:
Article brief
The article is basically saying that just as you won't run binaries on your machine
as root, but rather as a least-privileged-required user, you won't run containerized applications as root either.
The recommendation of the author is to create a user with a known uid in the Dockerfile
and run the process as that user.
The start of the dockerfile should look like
this:
FROM <base image>
RUN groupadd -g 999 appuser && \
useradd -r -u 999 -g appuser appuser
<br>USER appuser
... <rest of Dockerfile> ..
Validating my understanding, and some questions:
1. Why bother?
Ok, I understand that it's not good to run a container process as root,
just like it's not good to run any process as root.
That's why we should create a user in the Dockerfile and run the application process as that user.
But, if it's possible to run:
~$ docker run -u 0 some_docker_image
then why bother adding a user to my Dockerfile and switch to that user?
The question boils down to the question: What are we "afraid" of? What is the threat?
If the answer is that we're afraid of some untrusted user connected to the system (who is not a sudoer),
then this user can't even run docker containers, unless he is a member of the "docker" group,
in which case - again - he could run the container with -u 0?
So I guess that we're not concerned about the user. We're concerned about the binary itself.
In that case, two possible options exist:
a. The binary is of our creation. In that case: why are we concerned?
b. The binary is of someone else's creation. In that case i can understand why we would like to
switch user.
Am I missing something?
2. Why "Known uid"?
Why is that important to specify the uid of the newly created user, and not just name it something?
3. Why in the start of the Dockerfile?
Is it important to create+switch to the new user in the beginning of the Dockerfile?
Seemingly, this is an approach that's hard to implement, since usually during the docker build process
you need to run a lot of tasks that require root privileges, such as apt-get install etc.
4. What about adding a user and adding it to sudoers?
I have a case in which I need to create a docker image, which when the container runs,
it'll run a ssh server. In order to run the ssh server, you need root privileges.
Is there a point creating a user, adding it to sudoers, and then run the ssh server as root?
Running as root in Docker is dangerous for most of the same reasons as running as root directly on the host. The container has limited Linux privileges so there are some things it can't do (reconfigure the network, reboot the host), but it can do things like overwrite the application code inside the container.
Nobody's code is absolutely perfect, so one of the big reasons to run as non-root is to minimize the damage possible when a mistake does happen.
It doesn't matter what the user ID is, just that it's not 0. There's an argument to make it different from any uid the host might be using, but since your image could run on any host, it's just a guess.
You should create the user at the start of the Dockerfile, since that setup will change infrequently and Docker layer caching can skip it. But, you should use the USER directive and switch to the user at the end of the Dockerfile, after COPYing code in and RUNning the build. Do not RUN chown ... to make the non-root user own the code: you want most files to be owned by root, so that the non-root user can't overwrite them.
(In a compiled language, with a multi-stage build, you can consider the Dockerfile equivalent of the ./configure; make; sudo make install sequence, switching to a non-privileged user to do the build. I haven't seen this pattern in many Dockerfiles but I'd recognize it if I saw it.)
Do not add a user to /etc/sudoers. There are a couple of good reasons for this. The most basic one is that a container only runs a single process, and as already discussed we probably don't want it to be root. You can either configure it with no password (in which case you might as well be root) or hard-code a password in plain text in your Dockerfile (also a bad idea). You also usually don't want to run sudo inside a script (its behaviors of putting up random password prompts and hiding environment variables can cause trouble), and so correspondingly don't want to RUN sudo ... in a Dockerfile.
If you need to break into a container to debug it, you can always docker exec -u root ... to get a root shell there.

inside container container file permission issue for non root user

I am extending a docker image of a program from here and I want to change some configs and create my own docker image. I have written a Dockerfile as follows and replaced the server.xml file in this image:
FROM exoplatform/exo-community
COPY server.xml /opt/exo/conf
RUN chmod 777 /opt/exo/conf/server.xml
When I created the docker image and run an instance from the image, the running program of the container cannot access the file server.xml because its owner is the root user and I see the permission denied error. I tried to change the ownership in the Dockerfile by chmod command but I see the Operation not permitted error. The user of the running container is not the root user and it cannot access the server.xml file that is owned by the root user. How can I resolve this issue?
If this is actually just a config file, I wouldn't build a custom image around it. Instead, use the docker run -v option to inject it at runtime
docker run \
-v $PWD/server.xml:/opt/exo/conf/server.xml \
... \
exoplatform/exo-community
(You might still hit the same permission issues.)
In your Dockerfile approach, the base image runs as an alternate USER but a COPY instruction by default makes files owned by root. As of relatively recent Docker (18.03; if you're using Docker 1.13 on CentOS/RHEL 7 this won't work) you should be able to
COPY --chown=exo server.xml /opt/exo/conf
Or if that won't work, you can explicitly switch to the root user and back
COPY server.xml /opt/exo/conf
USER root
RUN chown exo /opt/exo/conf/server.xml
USER exo

Using useradd in a Dockerfile

What happens when a RUN useradd ... command is found in a Dockerfile?
Where is that user created? Where does it live? Is it added to the UID pool managed by the kernel?
What happens to it when the build context container exits?
A "user" in Linux is a numeric user ID, plus a mapping to some specific user name and other properties in /etc/passwd (optional). There's no such thing as a "uid pool"; if I happen to have user ID 12345 right now then things like filesystem ownership checks do a numeric comparison, and that's kind of the end of it.
So if a Dockerfile does a RUN useradd, it runs the useradd command, which (among other things) adds an entry to /etc/passwd inside the image's filesystem space. Docker knows to look things up in the image's /etc/passwd file for a Dockerfile USER directive or the docker run -u option. (But you can also use a numeric uid there too.)
Depending on the host OS and Docker daemon configuration, if you map a host directory into a container with the docker run -v option, either the container process must run as root or with the same numeric user ID as the directory owner to be able to access its files. The name of the respective host and container users is irrelevant.
The usual best practice I've seen is to do all of the installation as root, then create a single non-root user (its user ID is irrelevant, so long as it's not 0) and make that be the default user when executing the container.
FROM ubuntu
# Defaults to root; root will own all installed files
RUN ... && make install
# Set up a non-root user
RUN useradd myapp
USER myapp
# Say how to run the container (as user "myapp")
CMD ["myapp"]

Resources