Ozznotes

This is a blog with random OpenShift, Kubernetes, OpenStack and Linux related notes so I don't forget things. If you find something inaccurate or that could be fixed, please file a bug report here.

View on GitHub

Back to home

13 February 2018

SELinux and docker notes

by Juan Antonio Osorio Robles

SELinux and docker notes

Since the Pike release, we run most of the TripleO services on containers. As part of trying to harden the deployment, I’m investigating what it takes to run our containers with SELinux enabled.

Here are some of the things I learned.

Enabling SElinux for docker containers

Docker has the --selinux-enabled flag by default in CentOS 7.4.1708. However, in case your image or your configuration management tool is disabling it, as was the case for our puppet module verify this, you verify by running the following command:

$ docker info | grep 'Security Options'
Security Options: seccomp

To enable it, you need to modify the /etc/sysconfig/docker file, which you can use to enable SELinux for docker. In this file you’ll notice the $OPTIONS variable defined there, where you can append the relevant option as follows:

OPTIONS="--log-driver=journald --signature-verification=false --selinux-enabled"

After restarting docker:

$ systemctl restart docker

You’ll see SELinux is enabled as a security option:

$ docker info | grep 'Security Options'
Security Options: seccomp selinux

Note that for this to actually have any effect, SELinux must be enforcing in the host itself.

Docker containers can read /etc and /usr

SELinux blocks writes to files in /etc/ and /usr/, but it allows reading them.

Lets say we create a file in the /etc/ directory:

$ echo "Hello from the host" | sudo tee /etc/my-file.txt
Hello from the host
$ ls -lZ /etc/my-file.txt
-rw-r--r--. root root unconfined_u:object_r:etc_t:s0   /etc/my-file.txt

Now, lets mount the file in a container and attempt to read and write it.

$ docker run -ti -v /etc/my-file.txt:/tmp/my-file.txt alpine sh
(container)$ cat /tmp/my-file.txt
Hello from the host
(container)$ echo "Hello from the container" >> /tmp/my-file.txt
sh: can't create /tmp/my-file.txt: Permission denied

The same is possible if the file contains labeling more standard to the /etc/directory:

# ls -lZ /etc/my-file.txt
-rw-r--r--. root root system_u:object_r:etc_t:s0       /etc/my-file.txt
$ docker run -ti -v /etc/my-file.txt:/tmp/my-file.txt alpine sh
(container)$ cat /tmp/my-file.txt
Hello from the host
(container)$ echo "Hello from the container" >> /tmp/my-file.txt
sh: can't create /tmp/my-file.txt: Permission denied

This same behavior is not seen if we attempt it in another directory. Say, the user’s home directory:

$ pwd
/home/stack
$ mkdir test
$ echo "Hello from the host" >> test/my-file.txt
$ ls -lZ test/my-file.txt
-rw-rw-r--. stack stack unconfined_u:object_r:user_home_t:s0 test/my-file.txt
$ docker run -ti -v /home/stack/test/my-file.txt:/tmp/my-file.txt alpine sh
(container)$ cat /tmp/my-file.txt
cat: can't open '/tmp/my-file.txt': Permission denied
(container)$ ls /tmp/
ls: /tmp/my-file.txt: Permission denied

This might be useful if we want to mount a CA certificate for the container to trust, as it will effectively be read-only:

$ ls -lZ /etc/pki/ca-trust/source/anchors/cm-local-ca.pem
-rw-r--r--. root root unconfined_u:object_r:cert_t:s0  /etc/pki/ca-trust/source/anchors/cm-local-ca.pem
$ docker run -ti -v /etc/pki/ca-trust/source/anchors/cm-local-ca.pem:/tmp/ca.crt alpine sh
(container)$ cat /tmp/ca.crt
-----BEGIN CERTIFICATE-----
MIIDjTCCAnWgAwIBAgIQD6sfY0A+T7SHIG6yzfh//zANBgkqhkiG9w0BAQsFADBQ
MSAwHgYDVQQDDBdMb2NhbCBTaWduaW5nIEF1dGhvcml0eTEsMCoGA1UEAwwjMGZh
YjFmNjMtNDAzZTRmYjQtODcyMDZlYjItY2RmODdmZmYwHhcNMTgwMjAyMTUzNDI5
WhcNMTkwMjAyMTUzNDI5WjBQMSAwHgYDVQQDDBdMb2NhbCBTaWduaW5nIEF1dGhv
cml0eTEsMCoGA1UEAwwjMGZhYjFmNjMtNDAzZTRmYjQtODcyMDZlYjItY2RmODdm
ZmYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhEJJzGBkWNslk0iav
g1E2p39uYfTE6CCdeIRxFXpiKuPg/AO1lQXkUElGcakWJcJ7bWY/be6PGfp8EoRY
OCXtuggpVHXHdfOWhnPwhwdv51frFZwchL6jiaqDz+yEB9nTlhJ6cy4JQMcriZUP
6I/Djl1lzQQiBI/leA0ieNxTfGYifXHEGCDnNiyxIq32BzLcKUaMkl1sNmXjLZ1U
JW5ThPNs7IR/2zZgTyicDZTgLNUsn7oAQMXDffBOLOrx+MpX9k3o+XqBVcnb2+5Q
eQBxOAEjhbjel7GTTbkEajlCohcxvcycTot6hrd9xY3MTM3NHE/ysIs0zdnEkwLx
84v7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAQEwHQYDVR0OBBYEFDWB9zN+m0K6
xSauu4CUYdrcdtr/MB8GA1UdIwQYMBaAFDWB9zN+m0K6xSauu4CUYdrcdtr/MA4G
A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAlrTvxDBUNqx/nbF5DSkk
R1WqbfNLt07u3kqo+dBfYo4XTEfu2kQ2UzngzirAKokJfm7D8aNJqn6lLVpP0ffc
5VM+mW96tHFearImVZS3Z8gWe5MoD7hDziF3BKW1E0vBYqKOR773H4GpLkYcBLaP
sfujE/uxle2MpNn6i56AeiRwOVIejFSKFKA6rlUDuffu9NE9eKXmO5PW0KT/ojak
JoeC4LnDug+eOU3DrLCmBYEPU+JrHwtuPDCZgVoldVHbd/k+2vvOOvEWoSrTpmoH
3PH2UINW9t7cVxGipyPX3DYu1MrLJ+k73bny5pORgx0sqWh+RoWv8yKE92PP/O5r
Pw==
-----END CERTIFICATE-----
(container)$ echo "I'm trying to tamper with the CA" >> /tmp/ca.crt
sh: can't create /tmp/ca.crt: Permission denied

Just be careful that the files from /etc/ or /usr/ that you mount into the containers don’t contain any sensitive data that you don’t really want to share.

Enabling access to files protected by SELinux

In order to give a container access to files protected by SELinux, you need to use one of the following volume options: z or Z.

Lets show how the z(lower) flag works in practice:

$ ls -lZ test/my-file.txt
-rw-rw-r--. stack stack unconfined_u:object_r:user_home_t:s0 test/my-file.txt
$ docker run -ti -v /home/stack/test/my-file.txt:/tmp/my-file.txt:z alpine sh
(container)$ echo "Hello from container 1" >> /tmp/my-file.txt
(container)$ exit
$ cat test/my-file.txt
Hello from the host
Hello from container 1
$ ls -lZ test/my-file.txt
-rw-rw-r--. stack stack system_u:object_r:svirt_sandbox_file_t:s0 test/my-file.txt

Note that we were now able to append to the file. As we can see, from the host we could see the changes reflected in the file. Finally, checking the SELinux context, we will note that docker has changed the type to be svirt_sandbox_file_t, which makes it shareable between containers.

If we run another container and append to that file, we will be able to do so:

$ docker run -ti -v /home/stack/test/my-file.txt:/tmp/my-file.txt:z alpine sh
(container2)$ echo "Hello from container 2" >> /tmp/my-file.txt
(container2)$ exit
$ cat test/my-file.txt
Hello from the host
Hello from container 1
Hello from container 2

Now, lets try using the Z(upper) option. If we grab the same file and mount it in a container with that option we’ll see the following:

$ docker run -ti -v /home/stack/test/my-file.txt:/tmp/my-file.txt:Z alpine sh
(container3)$ echo "Hello from container 3" >> /tmp/my-file.txt

If we open another terminal, and try to append to that file, we won’t be able to:

$ docker run -ti -v /home/stack/test/my-file.txt:/tmp/my-file.txt:Z alpine sh
(container4)$ echo "Hello from container 4" >> /tmp/my-file.txt
sh: can't create /tmp/my-file.txt: Permission denied

We can verify the contents of the file:

$ cat test/my-file.txt
Hello from the host
Hello from container 1
Hello from container 2
Hello from container 3
$ ls -lZ test/my-file.txt
-rw-rw-r--. stack stack system_u:object_r:svirt_sandbox_file_t:s0:c829,c861 test/my-file.txt

Now we can see that the MCS label for the container changed and is specific to the container that first accessed it. Assuming the container that first mounted and accessed the file is named reverent_davinci, we can check the container’s label with the following command:

$ docker inspect -f '{{ .ProcessLabel }}' reverent_davinci

system_u:system_r:svirt_lxc_net_t:s0:c829,c861

And we can see that the container’s MCS label matches that of the file.

Disabling SELinux for a specific container

While this is not ideal, it is possible to do by using the --security-opt label:disable option:

$ docker run -ti -v /home/stack/test/my-file.txt:/tmp/my-file.txt --security-opt label:disable alpine sh
(container)$ cat /tmp/my-file.txt
Hello from the host
Hello from container 1
Hello from container 2
Hello from container 3

References

tags: docker - selinux

Back to home