How to disallow the Docker Daemon to mount host's root file system into the container

I have the following Container Setup.

On a bare metal server two Docker Daemons are installed and running.

  1. Main Docker Daemon Runs my application containers exposing 80/443 to the outside world.
  2. Plugin Docker Daemon Runs some containers provided by the customer that communicate with my application via 80/443.

I would like give the customer access to the API (2376) of the Plugin Docker Daemon so that the customer can deploy/start/stop his own containers. The customer will only have access to the API not to the Host (SSH).

The problem I currently face is, what if the customers runs a container that does something insecure like docker run -v /:/host/root Ubuntu rm -rf /host/root.

My question is what can I do to prevent the Plugin Docker Daemon from mounting root / or any other directory outside /home/user/,

  • Is it an option to start the Docker Daemon in /home/user/?
  • Can I use some LSM (Linux Security Modules SELinux/Apparmor) magic to prevent the docker daemon to mount some or all host paths except users home or var/docker/libs?
  • Can --userns-remap help me to achieve my goal?
  • Are they any other options available except VMs?

The server belongs entirely to a single customer. So security or data leakage is not my primary concern. What I really want to prevent is that someone in Plugin Daemon is doing something stupid, that influences my containers that run in Main Docker Daemon. I would like to keep lean and stick to docker only workflow and don't won't to set up an extra workflow for VM creation.

SELinux will prevent anything not correctly labelled to be mounted as a volume inside a docker container, as proof, here using a single file, same policy applies to directories:

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Max kernel policy version:      30

Using the sample file:

$ cat 
echo 'Hello, world'

Its default security context is:

$ ls -lrtZ 
-rw-------. 1 david david unconfined_u:object_r:user_home_t:s0 20 Oct  3 17:18

Trying to use this file inside a container fails as expected:

$ docker run -v /home/david/ --rm ubuntu bash
bash: Permission denied

And an AVC denial will be logged:

$ sudo ausearch -m avc -ts recent
time->Mon Oct  3 17:39:28 2016
type=AVC msg=audit(1475512768.444:784): avc:  denied  { read } for  pid=28720 comm="bash" name="" dev="dm-13" ino=101062112 scontext=system_u:system_r:svirt_lxc_net_t:s0:c457,c992 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

After changing the security context to one Docker can use:

$ sudo chcon -Rt svirt_sandbox_file_t
$ ls -lrtZ 
-rw-------. 1 david david unconfined_u:object_r:svirt_sandbox_file_t:s0 20 Oct  3 17:18 

The container now has access to the file:

$ docker run -v /home/david/ --rm ubuntu bash
Hello, world

More information about Docker and SELinux in the official Red Hat documentation and this article by Dan Walsh.

Yes, SELinux prevents this attack. Showing that it doesn’t work makes for a nice demo. Use Docker on a Red Hat-based distro to gain access to SELinux protection. On Debian-based distros, SELinux has historically been not well maintained.

What about Apparmor is it possible there as well?

I don’t believe so. I’ve never heard of AppArmor being able to do anything like this. It’s a rather different and much less secure technology, you know.