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 email@example.com 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.
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
Hello. Your processes context is [0, 'system_u:system_r:httpd_t:s0:c0.c100']
Hello. Your processes context is [0, 'system_u:system_r:httpd_t:s0:c2']
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.