docker build and running as root

I’m working on increasing the security of our Docker builds of Orthanc. One step involves running the Docker container as a user other than root with the --user option at the “docker run” step.

However, I find that my Orthanc will not launch when run as a non-root user. As near as I can tell from the few error messages, it might be encountering a problem locking down the ports necessary for communication with the postgres server.

Before I dig deeper to try to find specifically where Orthanc is failing to launch, I wanted to ask the simple question: Does Orthanc need to run as root?

Conceivably, I could set up a sudoer file to let the non-root user execute just Orthanc and no other root commands. Alternatively, I could engage Docker’s user namespace to keep the process as root inside the container but some higher mapped UID externally. The namespace approach is a bit more complicated than the “run as” like --user option.

Ultimately, I would like to have the process running Orthanc (and the Mongoose server) be something other than root on the off chance that someone manages to break out of the web server into the host Docker container (as root!) and then cause problems there or conceivably break out of the container onto the host as root.

John.

Dear John,

Orthanc can be run be any non-root user, of course provided you have read-write access to the storage folders, and provided you don’t use privileged TCP ports (this is actually the reason why Orthanc uses ports 4242 instead of 104, the default port for DICOM).

However, the official Docker package uses the “root” user and the “/var/lib/orthanc” folder to store its database:
https://github.com/jodogne/OrthancDocker/blob/master/orthanc/build.sh

Note that as far as Docker is concerned, you should run each separate service (for instance Orthanc, nginx, and PostgreSQL) in separate containers, linking them through the “–link” argument of Docker. It is not a good approach to run each of those services inside a single Docker container. Separating the containers makes the different services run in full isolation, hereby improving the security (the “root” users are not shared between the containers) and the stability (a crash in one service does not impact the other containers). Furthermore, note that the “root” user of one container cannot log into the host computer running the container.

As a consequence, it is not a security problem to run Orthanc as root within Docker, and I would suggest you to use the official Docker images as such.

HTH,
Sébastien-

Thanks for the quick response, Sebastien.

We do indeed run our various processes (Orthanc, Postgres, web server, etc.) as separate Docker containers.

I don’t think low ports are a problem, but I’ll take a look.

At least I know Orthanc should be able to be run as non-root, so that I can dig a little deeper into why that it is not working for me.

As near as I can tell, without the user id namespace feature turned on with the Docker daemon, the root user within a container has the same uid and therefore power as the root user of the host on any disk volumes mapped into the container. Docker has a moderate amount of documentation on security, but the amount of criticism I find on the net regarding docker and security makes me anxious, so I try to take as much precaution as possible.

If I figure out why my Orthanc won’t run as non-root, I’ll post back here.

John.

You are right about the problem. However, I've always found the
solution of creating a user within the container to be a bit of a
kludge. Indeed, user IDs are usually allocated sequentially by most
tools based on state they can observe (e.g. /etc/passwd), and in a
container tools won't be able to observe the user database of the host.
As a result, creating a user in a container might still clash with an
existing user on the host or other containers, making the UID->name
resolution on the host very confusing when bind-mounting volumes (and
of course it is still dangerous in the event of a container breakout).

I fear the only real solution is user namespacing/remapping. I haven't
had a chance to play with it much but I do hear the current
implementation in Docker makes it a bit of a pain to use, though it
should improve in the future.

That said, creating users inside containers has been popular anyway as
it's better than nothing. If you have strong security requirements I
recommend you look at SELinux and the :Z flag for Docker bind-mounts.
This does guarantee proper isolation for Docker volumes. Beware of
relabeling when recreating containers however, they can be very costly
(multiple minutes) on file trees with a lot of files (which is often
the case in Orthanc servers).

As it turns out, my problem was a matter of file permissions on shared folders and files with the host system. I forgot to switch permissions on the host to match the uid:gid of the container user. Once I did, things ran fine.

I can confirm that Orthanc can run as non-root within the container.

John.

I’ve returned to this issue of running as root as I start to work on a docker-compose recipe to assemble my Orthanc/PostGRE pairs with a single recipe using Osimis/Orthanc docker images.

In my previous setup, I used an all-in-one configuration file that I manually edited outside the container and bound to the container as it came up. I also created a special image with everything ready to run as non-root. I’m hoping to have the configuration be part of the spin-up process now.

I’m making a lot of use of the environmental variable hooks to “personalize” each Orthanc container when spinning up. However, I immediately ran into this root vs non-root user problem.

It seems the framework for turning on/off plugins and for writing configuration json files in the Osimis/orthanc Docker image depends on having root privilege to write to the plugin and configuration folders.

I’m going to look at deriving an image where I set the permissions so that a limited user should be able to write to these folders. As another approach, I’m going to dig into the documentation to determine whether I can specify other folder locations that the non-root user would be able to write to.

I’d be interested in how others have run as non-root.

John.

My first approach to this seems to have worked. During my docker-compose initiation, I use a Dockerfile that specifically changes ownership on /usr/share/orthanc, /etc/orthanc and /usr/lib/orthanc so that a limited non-root user can read/write to/from them.

Rather than being owned by root, those folders and contents are now owned by my limited user that runs the Orthanc process within the container.

Hi John,

Could you share that derived Dockerfile? This might be of interest for many people and eventually for us to include in our images.

Best regards,

Alain.

Hi, John!
Hi, Alain!

If understood your query correctly, 've been working on this kind of Dockerfile.

Specifically my requirements were twofold:

  1. Run Orthanc as an ordinary user
  2. Run my Orthanc builder as an ordinary user

Maybe (2) was more critical at the time. Because since I develop Orthanc with a custom image (courtesy of switching to openSUSE whilst still wanting to build and run it on Debian!)… Well, long story made short: root builds Orthanc, permissions get messed up with, then I can’t read my own files. Well, “my” files.

The only gotcha is that its installation dependant. More specifically, it depends on the user building it.

UNLESS

You standardise a, say, orthanc.adm’s UID. Then you could make it more “generalist-ic”.

I’m omw to work. Once I get to my computer I’ll fetch it from my personal Google drive.

HTH
Luiz

[now on the proper thread]

Hi, John!
Hi, Alain!

There you go!

To build this particular image and see it in action:

$ cd
$ docker build --build-arg USER=$(whoami) --build-arg UID=$UID -t : .
$ docker run --rm -it :

You’ll notice the image kicks off with the user that built the image, instead of root. Also, you’ll want to download Java 12 to build particualr image because it depends on it. Nothing related to Orthanc, it’s because of the project I’ve been working on.

Notice that “gosu” is used before most commands. It’s this program that switches the UID’s. Side-note-worth-mentioning: since the ENTRYPOINT is a bash script that runs another program, it is imperative that it ends with the “exec” instruction; otherwise shutting down running containers with SIGTERM will not function.

Should you need any help, feel free to reach out.

@Alain Mazy feel free to use these images as you see fit on a contribution basis! =)

HTH

configure-docker-user.sh (343 Bytes)

Dockerfile (1.65 KB)

docker-run.sh (89 Bytes)

My Dockerfile is a bit more complicated because it refers to a local Orthanc image that I build on top of Ubuntu 18.04 using the Osimis/Orthanc docker build files (and substituting the base Ubuntu 18.04 for 16.04). I also add installation of some Lua support libraries, since I make extensive use of the Lua scripting engine.

That said, for purposes of ultimately running as a limited user, the relevant additions to the Dockerfile are not dependent on Ubuntu 18.04 and are:

ARG PROCESS_UID
ARG PROCESS_GID
RUN chown -R $PROCESS_UID:$PROCESS_GID /usr/share/orthanc /etc/orthanc /usr/lib/orthanc

where PROCESS_UID/PROCESS_GID are provided either

  1. docker build --build-arg PROCESS_UID=a_value --build-arg PROCESS_GID=another_value .
    
  2. OR, within docker-compose.yml with "args" as part of the build context
    Once the image is built, I run it as the limited user by either specifying the UID:GID at the docker run command line (--user UID:GID) or within docker-compose.

As Luiz suggests in a later post of this thread, when you run a container this way, you have to make sure that the limited user has read/write access to any files and directories that you might bind to the container at run time. Otherwise, you can run into problems where the limited user tries to access files either inside the container or outside (bind mounts) that it has no permission to access.

So, for example, when I bind in my complicated Lua scripts, I have to make sure my limited user has permission to read those script files. I do that on the host even before I launch the container.

Similarly, I bind in the data directory where the DICOM are to be stored. I prepare the permissions ahead of time. Note that I didn't change permissions on the internal DICOM dicectory because, in my case, I replace that directory with my external bind. If you don't bind in your own directory, it might be necessary to add the internal storage area of the container to the list of directories to be chowned to the limited UID:GID.
``

Note that this is all about preparing to run the container as a limited UID:GID. In my approach, I still assume root or at least root-like ability to build Docker images.

John.

Thanks, Luiz. I read about gosu in the past as one possible solution to switching UID:GID within the running container. At the time, I was just learning about Docker and gosu was a bit far up the learning curve for me. It looks like you got it working nicely.

Did you ever look into the UID mapping features of Docker? This let you run the internal container process still as root, but externally that process would be mapped to a non-root UID. I never could make it work well, though that might have been because it was a relatively new feature for Docker at the time. I also didn’t like the idea that the internal process seemed to retain root access to the internals of the container.

Regarding building images as non-root, apparently Singularity can do that now. It used to be that Singularity, like Docker, required root permission to build images, but Singularity did not require root to run containers. I have considered on occasion porting my Orthanc build over to Singularity so that, at least, a non-root user could launch it.

  • John

Thanks for pointing out the UID mapping features of Docker. It’s news to me.

I agree with you. I guess it beats the purpose of running a container as a non-root user. Still, I reckon it’s a nice option to consider and I will take a look at it.

Best!