Stop Disabling SELinux: A Real-World guide

31 Jan 2017
Be safe from software vulnerabilities AND run your webserver

It's 2017, and your New Year's resolution should be to stop disabling SELinux. SELinux does a great job of doing what it says on the tin - making your servers safer. It doesn't matter if a Docker, Samba or even Flash vulnerability hits, as SELinux can contain it.

But SELinux can't do anything if you disable it. In the first post in our SELinux series, we're going to look at just how easy it is to run nginx as a reverse proxy, all while keeping SELinux happy.

Setup

For this guide, I'm using a Fedora 25 setup. In writing this guide, I referred heavily to the RHEL/CentOS 7 and 6 documentation. SELinux is a very stable piece of software, so this guide will probably apply unmodified for other RedHat based systems.

As for the http server, we will be looking at using nginx. However the configuration in RedHat based systems is generic across all packaged servers, so you should be lucky if you use apache2.

Proxy Pass

So you have you web application server (eg. django) running on something like http:​//localhost:​8000. Then you setup nginx to proxy pass to the app server:

server {
    server_name www.coolsite.io;

    location / {
        proxy_pass http://127.0.0.1:8000;
    }
}

But now you get a 502 bad gateway error when you access it. First we need to follow the SELinux log, which is part of the systemd journal:

journalctl -f

After you request the page again, you should see an error from SELinux (also called audit) in the journal:

31 10:48:54 server audit[16067]: AVC avc: denied { name_connect } for pid=16067 comm="nginx" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:transproxy_port_t:s0 tclass=tcp_socket permissive=0

This is because of the default SELinux policy, which is secure and restrictive. Since using proxy passes is very common, this is a simple configurable boolean. Just run:

sudo setsebool -P httpd_can_network_connect true

The -P option writes this change to disk, meaning it persists across reboots. So just add this command to your provisioning script and your are good to go. If you use ansible, it is fully integrated:

- name: Allow nginx to proxy pass
  seboolean: name=httpd_can_network_connect state=yes persistent=yes

In future, you can check the list of all booleans but running semanage boolean --list.

Static files

But in the normal modern setup, nginx does more than just proxy. Commonly nginx is used to serve static files:

server {
    server_name www.coolsite.io;

    location / {
        proxy_pass http://127.0.0.1:8000;
    }

    location /static/ {
        alias /var/www/static/;
    }
}

But in your browser, you get a 403 Forbidden error. Again we will follow the systemd journal (journalctl -f) and request the file again. Then you should see an error message from SELinux:

Jan 31 20:28:46 server audit[9197]: AVC avc:  denied  { read } for  pid=9197 comm="nginx" name="test.txt" dev="vda1" ino=137247 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0

This is telling us that SELinux denied a read for a httpd_t (HTTPD type - probably nginx) process to read a var_t file. However, the var_t is used across the whole /var system, and it would be too insecure to give nginx access to all those files.

Enter httpd_sys_content_t. This is type that we can use for just this use case - files that the web server should have read access to.

SElinux file types work in 2 ways. Firstly, they are stored in the file metadata, like any normal permissions. But on creation, SELinux looks up the default for path based on the system rules (see semanage fcontext -i for all the rules on your system). We need to add a new rule for /var/www/:

semanage fcontext --add --type httpd_sys_content_t "/var/www(/.*)?"

That will set the default rule for /var/www and all descendants. Then we have to relabel the type of all the existing files:

restorecon -Rv /var/www

Now your webserver is good to go.

Conclusion

Keeping a webserver running with SELinux is as simple as 2 things:

  • Setting a config boolean to let it proxy pass
  • Telling SELinux where you are going to put the webroot files

It is seriously something your should do for your server's security.

Next in our SELinux series, we're going to be looking at how we can use SELinux to contain our own apps. By default, services run unconfined. However policy writing is easy and worthwhile for the extra security. Make sure to subscribe so that you get that in your inbox.