Posted by Paul on 5th February 2014
Static File Access Management
Django documentation advises against using an application to directly serve static files when in a production environment. A dedicated HTTP server, optimised for serving static files, should be used for this purpose. That’s fine for serving publicly accessible files. But serving static files which require user access permission checking necessarily involves routing the download request via application code.
It’s possible to get the best of both Django view processing, where access permissions can be managed and dedicated HTTP server download handling, by using a HTTP server extension that processes response headers applied by the web application. The mod_xsendfile Apache module is one such module. Used with django-sendfile, application management of file downloads can be cleanly separated into interface and implementation.
django-xsendfile and Apache mod_xsendfile Installation
Install the mod_xsendfile module:
$ sudo apt-get install libapache2-mod-xsendfile
The mod_xsendfile module is usually enabled by the install scripts. Use a2enmod
to further manage its enabled status.
Enable X-SENDFILE header processing within the application’s Apache virtual host. For instance, a virtual host for for a Django application to serve example.com, /etc/apache2/sites-available/example.com
:
<VirtualHost *:80>
ServerName example.com
# mod_wsgi settings
WSGIDaemonProcess example python-path=/var/www/example.com/app
WSGIProcessGroup example
WSGIScriptAlias / /var/www/example.com/app/example/wsgi.py
# Publicly available static files directly available via Apache.
Alias /static /var/www/example.com/app/static
Alias /pub-uploads /var/www/example.com/pub-uploads
# Restricted access files via Apache mod_xsendfile.
XSendFile On
XSendFilePath /var/www/example.com/priv-uploads
# Apache <directory/> directives here...
</VirtualHost>
From the Django application:
$ pip install django-xsendfile
Within the Django application’s settings.py
:
SENDFILE_BACKEND = 'sendfile.backends.xsendfile'
Conventionally this can be overridden in a local_settings.py
and imported by settings.py
for a development environments:
SENDFILE_BACKEND = 'sendfile.backends.development'
The Django view used to manage download access permissions may then look something like the following:
from django.views.generic.base import View
from sendfile import sendfile
class DownloadFile(View):
def get(self, request):
# Get user access rights and the file's file-system path.
# ...
# If access denied return HttpResponseForbidden(), else:
return sendfile(request, file_path)
The sendfile API also provides response header management, such as Content-Disposition
header content:
def sendfile(request, filename,
attachment=False, attachment_filename=None,
mimetype=None, encoding=None)
Posted by Paul on 2nd July 2009
Background
These notes relate to Debian-based systems running Apache 2.2, so you’ll have to make the appropriate changes to paths, and possibly commands, for your operating system or Linux distro.
The example setup that I’ve provided here allows users with an operational Apache user directory (mod_userdir
) to set their own user access permissions, rather than a system-wide approach.
System-wide Settings
The Apache2 configuration files can be found in /etc/apache2/
. Update the file /etc/apache2/apache2.conf
to include the following directives:
<Directory /home/*/public_html>
AllowOverride FileInfo AuthConfig Limit
Options Indexes SymLinksIfOwnerMatch IncludesNoExec
</Directory>
You might simply be able to uncomment existing text within the config file. Among other things, this permits users to enable authentication checking in their public_html
directories, or whatever you set the directory name to. You’ll also have to enable Apache’s mod_userdir
if it isn’t already enabled:
$ sudo a2enmod userdir
Support for digest authentication is also provided in an Apache module. The digest authentication module is not enabled by default, but can also be enabled using a2enmod
:
$ sudo a2enmod auth_digest
If a2enmod
isn’t available on your distribution, then you may wish to enable Apache modules by providing a sym link to the appropriate module in the following manner:
ln -s /etc/apache2/mods-available/auth_digest.load /etc/apache2/mods-enable
Password Generation
Passwords are generated using the htdigest
tool that ships with the Apache2 distribution. The file created using this tool places username, realm and hashed password together on a colon-delimited line. This file should be placed in a location where Apache cannot serve it up to a client (e.g. don’t place it in /var/www
).
In order to add an entry to the password file run the htdigest
tool as follows:
$ htdigest -c /directory/path/digest.htpasswd myrealm username
Caution: the -c
flag forces htdigest
to delete the existing digest password file, if it already exists. Drop its use of you need to add new entries into the file. You should also replace the values myrealm
and username
with values appropriate for your system. The realm value is a security context that should be recognisable to the user in order to allow them to provide the correct username and password.
Directory-Level Configuration
You can now create a .htaccess
file within each of those directories and subdirectories that you would like to be maintain access control to using digest authentication. Here’s an example .htaccess
file that may, for example, be placed immediately within a user’s public_html
directory:
AuthType Digest
AuthName "myrealm"
AuthDigestDomain / http://subdomain.mydomain.com/
AuthUserFile /directory/path/digest.htpasswd
Require valid-user
Here’s an explanation of each of the above Apache directives.
- The AuthName value is the same value that was given when using the htdigest program (see details above).
- AuthDigestDomain provides the list of URIs that are in the protection space. These URIs can be absolute or relative and sub-directories of those given are matched also.
- The value of AuthDigestFile points to the location of the file that was created using the
htdigest
tool.
- Require takes two values here, but can take more so that extra requirements are imposed. The values used are used to indicate that the user-level authentication mechanism is being used (rather than group-level) and that only valid users (created using
htdigest
, as shown above) are granted access.
Now Try It!
Restart apache
$ sudo /etc/init.d/apache2 restart
If all has gone well, you should be challenged for your credentials when you try to browse your protected directories.