Mod Selinux with Django

Django with mod_selinux

The mod_selinux module allows you to confine a spawned apache process into a specific selinux context. For example, you can do this via virtual hosts, or by LocationMatch directives.

Part of my curiosity wanted to see how this works. So I made up a small django application that would tell you the selinux context of an URL.

Install mod_selinux first

yum install mod_selinux mod_wsgi

Now we create a VirtualHost that we can use for the test application

NameVirtualHost *:80

<VirtualHost *:80>
    ServerAdmin william@firstyear.id.au
    DocumentRoot /var/empty
    ServerName 172.16.209.150

    <LocationMatch /selinux/test/c2>
    selinuxDomainVal        *:s0:c2
    </LocationMatch>
    <LocationMatch /selinux/test/c3>
    selinuxDomainVal        *:s0:c3
    </LocationMatch>

    #Alias /robots.txt /usr/local/wsgi/static/robots.txt
    #Alias /favicon.ico /usr/local/wsgi/static/favicon.ico

    AliasMatch ^/([^/]*\.css) /var/www/django_base/static/styles/$1

    Alias /media/ /var/www/django_base/media/
    Alias /static/ /var/www/django_base/static/

    <Directory /var/www/django_base/static>
    Order deny,allow
    Allow from all
    </Directory>

    <Directory /var/www/django_base/media>
    Order deny,allow
    Allow from all
    </Directory>

    WSGIScriptAlias / /var/www/django_base/django_base/wsgi.py

    <Directory /var/www/django_base/scripts>
    Order allow,deny
    Allow from all
    </Directory>
</VirtualHost>

We also need to alter /etc/httpd/conf.d/mod_selinux.conf to have MCS labels.

selinuxServerDomain     *:s0:c0.c100

And finally, download the (now sadly lost) tar ball, and unpack it to /var/www

cd /var/www
tar -xvzf django_selinux_test.tar.gz

Now, navigating to the right URL will show you the different SELinux contexts

http://localhost/selinux/test/test

Hello. Your processes context is [0, 'system_u:system_r:httpd_t:s0:c0.c100']

http://localhost/selinux/test/c2

Hello. Your processes context is [0, 'system_u:system_r:httpd_t:s0:c2']

http://localhost/selinux/test/c3

Hello. Your processes context is [0, 'system_u:system_r:httpd_t:s0:c3']

The best part about this is that this context is passed via the local unix socket to sepgsql - meaning that specific locations in your Django application can have different SELinux MCS labels, allowing mandatory access controls to tables and columns. Once I work out row-level permissions in sepgsql, these will also be available to django processes via this means.

Example of why you want this.

You have a shopping cart application. In your users profile page, you allow access to that URL to view / write to the credit card details of a user. In the main application, this column is in a different MCS - So exploitation of the django application, be it SQL injection, or remote shell execution - the credit cards remain in a separate domain, and thus inaccessible.

Additionally, these MCS labels are applied to files uploaded into /media for example, so you can use this to help restrict access to documents etc.