## Exercise 6: Running as root or a non-root user ## ### Objective ### Learn how to manage running a container as a given user. ### Containers run as root by default ### As a normal user, you should not have access to certain files on the system, e.g. the **/etc/sudoers** file: ``` > wc /etc/sudoers wc: /etc/sudoers: Permission denied ``` However, running a container with a volume mount gives you complete access to the system, because the container runs as root: ``` > docker run --rm -it --volume /etc:/mnt alpine:3.5 wc /mnt/sudoers 97 496 3174 /mnt/sudoers ``` This means you can read, and potentially change, files on the host system that you should not have access to! To prove this point further, try creating a file on a volume-mounted directory, and see what user it shows as on the host system: ``` > mkdir tmp > cd tmp > touch a-file.txt > touch another-file.txt > ls -l total 0 -rw-rw-r--. 1 centos centos 0 Aug 5 16:12 a-file.txt -rw-rw-r--. 1 centos centos 0 Aug 5 16:12 another-file.txt > docker run --rm --volume `pwd`:/mnt alpine:3.5 touch /mnt/alpine-file.txt > ls -l total 0 -rw-rw-r--. 1 centos centos 0 Aug 5 16:12 a-file.txt -rw-r--r--. 1 root root 0 Aug 5 16:12 alpine-file.txt -rw-rw-r--. 1 centos centos 0 Aug 5 16:12 another-file.txt ``` Note that the file created within the alpine container is owned by root! Using volume-mounts into a container allows you not only to read and write as root, but also to mount directories that you cannot even see otherwise. The volume-mount is executed by the docker daemon, which runs as root, so it can fulfill the mount request even if you wouldn't be able to see that directory as a normal user. For example, in **/etc/selinux/final** there's a hierarchy of files that users should not be able to see. List the **/etc/selinux** directory and you'll see the **final** directory is protected. Attempt to list it's contents as a normal user, and you'll fail: ``` > ls -l /etc/selinux total 8 -rw-r--r--. 1 root root 542 Jan 28 2019 config drwx------. 3 root root 22 Aug 5 13:02 final -rw-r--r--. 1 root root 2321 Oct 30 2018 semanage.conf drwxr-xr-x. 8 root root 226 Aug 5 13:02 targeted drwxr-xr-x. 2 root root 6 Oct 30 2018 tmp > ls -l /etc/selinux/final ls: cannot open directory /etc/selinux/final: Permission denied ``` However, if you _know_ the path you want to examine under that tree, you can use a volume mount to access it in a container: ``` > docker run --rm -it --volume /etc/selinux/final/targeted/contexts:/mnt alpine:3.5 ls -l /mnt total 0 drwx------ 2 root root 6 Aug 5 13:02 files ``` If you don't find that scary, please go back and read this section again, until you do. This is why many HPC centres won't allow you to run docker images on their machines, which are shared among many users. There are alternatives, one of the best is [**singularity**](https://sylabs.io/docs/), which is essentially a drop-in replacement for docker which solves the security issues by only allowing you to run as the user you are on the host system. If you're only running on your own machines, and nobody else has access to them, there's not much to worry about. If, however, you're running a service of some sort (a web server or portal) then you need to consider that someone could exploit bugs in your service to gain access to the host via the container. Yes, it does happen! The solution, then, is to run your service as a non-root user in the container, and make sure that user cannot escalate their privilege within the container. ### Adding a non-root user to an image ### You can add users to containers in much the same way as you would with any linux distribution. How exactly you do this is specific to the particular linux flavour you're using. For the **alpine** distribution, you'll need to install the user-management packages. There's a dockerfile that does this, **Dockerfile.user**, in the **tsi-cc/ResOps/scripts/docker** directory of the tutorial repository, note that it creates both a _user_ and a _group_, because if you don't specify a group then the _root_ group is used by default: ``` > cat Dockerfile.user FROM alpine:3.5 RUN apk update && \ apk add shadow && \ groupadd muggles && \ useradd -ms /bin/sh -G muggles dudley USER dudley:muggles ``` Build an image from this, tag it as **user**. Now try running a container with that image and see if you can see the hidden files: ``` docker run --rm -it --volume /etc/selinux/final/targeted/contexts:/mnt user > ls -l /mnt ls: can't open '/mnt': Permission denied total 0 > ls -ld /mnt drwx------ 3 root root 19 Aug 5 13:02 /mnt > id uid=1000(dudley) gid=1000(muggles) ``` You can see that the volume-mount still succeeds, but you can't see the files, because you don't have permission. The **dudley** user has no way to become root within the container, so has no way to cheat. So far so good! But that's not the end of the story yet. Docker allows you to specify which user to run as in the container, from the command line. So a user can run the container as root by explicitly asking for it: ``` > docker run --rm -it --volume /etc/selinux/final/targeted/contexts:/mnt user ls -l /mnt ls: can't open '/mnt': Permission denied total 0 > docker run --rm -it --volume /etc/selinux/final/targeted/contexts:/mnt --user root user ls -l /mnt total 0 drwx------ 2 root root 6 Aug 5 13:02 files ``` The bottom line is that if the user is allowed to run containers for themselves then they can access pretty much anything on your system as if they are the root user. If they only have access to a service that you are providing, such as a web server, then you can protect your system more by running the service as a non-root user in the container. ### Conclusion ### Docker has some significant security issues. There are measures you can take to mitigate them, but if the user can run containers on your infrastructure for themselves, there's no foolproof method to prevent possible abuse. ### Best practices ### - consider who has access to the host system when you install docker on a machine. Can you limit the set of users to reduce security exposure? - if you're running a service, that users only access through the web or some other API, there are a few things you should do to protect it: - create an unprivileged user in the image, use that user to run the service - make sure your container only mounts volumes that you need from the host filesystem - be as restrictive as possible, don't mount more than you need, and never from the system directories - mount volumes read-only if you can, to prevent unwanted changes to files - if you allow users to upload data, provide separate input and output volumes, don't let them write in volumes where you store other files