Django chroot, securing your web application hosting
Filed under tips & tricks, security, Python on jun 03, 2010
For me the best way to run Django applications has proven to be mod_wsgi and Apache. This setup allows for scalable application pools to run as a seperate process, featuring threading and extra privilege separation, which is great.
To make your application even more hardened, you can use the mod_wsgi chroot feature, as discussed in this thread. This will make mod_wsgi do a chroot to the application jail before spawning the Python application. This ensures your application doesn’t have access to other parts of the filesystem but its own jail.
If you wish to still be able to use uploads, make sure you have a (world) writable /tmp directory in your chroot jail. You can use techniques like bind mounts if you want to expose parts of the file system in your jail.
Setting up the chroot
Your application has to be able to access the Python libraries from within the jail. There are several ways to setup your chroot, I just used ldd and some guessing. Note that copying the Python binary by itself is not mandatory, however I do find it convenient to be able to access the Python interpreter from within the jail.
~# mkdir /home/chroot/hosting{,etc,dev,usr,usr/bin,usr/lib,lib,tmp}
~# export CHROOT=/home/chroot/hosting
~# cd $CHROOT
hosting# alias CP='rsync -a'
hosting# $CP /usr/bin/python $CHROOT/usr/bin/
hosting# ldd $CHROOT/usr/bin/python | grep '=> /' | awk '{print $3}' | while read LIB; do
> mkdir $CHROOT/$(dirname $LIB)
> $CP $LIB $CHROOT/$LIB
> done
hosting# mknod $CHROOT/dev/null c 1 3
hosting# mknod $CHROOT/dev/zero c 1 5
hosting# echo "hosting" > $CHROOT/etc/hostname
hosting# grep ^demo /etc/passwd > $CHROOT/etc/passwd
hosting# grep ^demo /etc/group > $CHROOT/etc/group
hosting# chmod --reference=/tmp $CHROOT/tmp
Installing Python
Next we have to copy the Python library files:
hosting# mkdir -p $CHROOT/usr/lib hosting# $CP /usr/lib/python2.6 $CHROOT/usr/lib/
Finally, install Django in your chroot. In my Ubuntu environment, the site-packages directory is called dist-packages, you can find out what directory to use by asking the distutils package:
hosting# python -c 'from distutils import sysconfig; print sysconfig.get_python_lib(0)' /usr/lib/python2.6/dist-packages
Now extract Django in the appropriate path:
hosting# curl http://href.be/3 | tar -C /tmp -xf - hosting# mv /tmp/Django-1.2.1/django $CHROOT/usr/lib/python2.6/dist-packages hosting# rm -rf /tmp/Django-1.2.1
Setting up the application
Now you can setup your application as you normally would, except you will be using /home/chroot/hosting as base directory. To tell mod_wsgi to use the chroot jail, you can use a configuration like this:
<VirtualHost *:80>
ServerName demo1.example.org
WSGIDaemonProcess demo-1 user=demo1 group=demo1 display-name=%{GROUP} chroot=/home/chroot/hosting
WSGIScriptAlias / /home/chroot/hosting/demo1/application/apache.py
Alias /media/ /home/chroot/hosting/demo1/media/
</VirtualHost>
Python in the chroot
You can use the chroot(8) utility to switch to Python within the chroot environment. This can be of great use when debugging.
~$ sudo chroot /home/chroot/hosting /usr/bin/python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> print os.listdir('/')
['tmp', 'usr', 'bin', 'dev', 'demo1', 'demo2', 'etc']
Nota Bene
Make sure you have set up the permissions correctly in your jail. If the project is owner by user demo1, you can make a seperate account demo1-www that runs the application in mod_wsgi. In this way, the application can not modify parts of the code.
Post your feedback
You can use this form to leave your feedback. Your insights are always appreciated.