Today we will reveal the secret ingredients that make our technology unique. Our SRT chef Evgeny Khatsko will tell you how to cook Django Nginx Secure Link. Have fun reading.
We have made the first release of our package, it can be found here. The motivation was the problem of hiding files from public access for unauthorized users, or, going deeper, hiding the file from everyone altogether by creating temporary links for each individual file.
It is possible that someone will see the feedback from the object storage in this and say "there are AWS S3, Azure Blobs, etc.", but there are some problems and difficulties that prevent the rapid integration of this kind of storage. It is not always advisable to connect object storage to a working project, here are some such examples from our experience:
This is just a small list of examples in which it is much easier to take the Nginx module by closing public access to files, adding a component for generating private links to output files to the outside, thereby not changing the entire logic of the codebase working with files. Our package allows you to delegate file protection directly to the web server on simple and not only projects where Nginx acts as a frontend server, unloading our backend.
The solution is also suitable for solutions that are quite complex in architecture, where files can be stored on separate servers, which will be accessed via Nginx.
Our package allows you to take over the generation of temporary links to files, limiting public access, and Nginx itself already checks these links and decides whether to return the file. Nginx has an excellent ngx_http_secure_link_module module for this task, but we took it as a basis and wrote a layer between Django and this module. The solution turned out to be simple and convenient, we also took into account specific cases when it is necessary to hide only specific directories from the media storage or to make them public only, all this is configured at the package level and must be configured at the Nginx level with specific locations for media/...
The package also contains auxiliary manage.py commands aimed at helping you configure the required block for location.
The work is still in full swing and improvements and edits will be made. There are several open tasks that will soon be included in the next release. If you are interested in this project and want to improve it, then write and participate. We will be glad of any involvement, you can even tweak it in order to improve the packages.
Let's dive into the technical part a bit and look at an example of configuring a Django project and configuration for an Nginx virtual host.
pip install django-nginx-secure-linksDepending on the Django version, you need to specify the following settings:
Common settings for all Django versions
INSTALLED_APPS = (
...,
'nginx_secure_links',
)
SECURE_LINK_EXPIRATION_SECONDS = 100
SECURE_LINK_SECRET_KEY = '8SypVsPwf3PypUfdVmos9NdmQNCsMG'
SECURE_LINK_TOKEN_FIELD = 'token'
SECURE_LINK_EXPIRES_FIELD = 'exp'
SECURE_LINK_PRIVATE_PREFIXES = [
'private',
]
SECURE_LINK_PUBLIC_PREFIXES = []DEFAULT_FILE_STORAGE = 'nginx_secure_links.storages.FileStorage'STORAGES = {
"default": {
"BACKEND": "nginx_secure_links.storages.FileStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}Also, as an example, we will specify the MEDIA_URL and MEDIA_ROOT settings.:
MEDIA_URL = '/media/'
MEDIA_ROOT = '/var/www/html/media/'We have added the auxiliary secure_links_gnx_location command for manage.py , which is convenient to use to automatically generate the required location block, taking into account the current settings of the Django project:
python manage.py secure_links_nginx_locationAn example of a configuration block will be displayed in the console, which must be inserted into the site.conf file.:
location /media/private/ {
secure_link $arg_token,$arg_exp;
secure_link_md5 "$secure_link_expires$uri 8SypVsPwf3PypUfdVmos9NdmQNCsMG";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
alias /var/www/html/media/private/;
}
location /media/ {
alias /var/www/html/media/;
}1. Consider an example of a data model that saves files to a private directory specified in the SECURE_LINK_PRIVATE_PREFIXES setting.:
from django.db import models
class PrivateDocument(models.Model):
file = models.FileField(upload_to='private/documents')2. Create a new record in the database by uploading the file through the administrative panel for PrivateDocument.
3. To generate a secure link, just refer to the url property in the file -> obj.file.url field:
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from models import PrivateDocument
def private_document_view(request, pk):
obj = get_object_or_404(PrivateDocument, pk=pk)
private_url = obj.file.url
return HttpResponse(private_url)The private_url variable will store a private link containing two additional GET parameters.:
Link example: /media/private/documents/file1.txt?token=S9tBKKkXcAq77g8MrVe6LQ&exp=1721519555
The token GET parameter (see the SECURE_LINK_TOKEN_FIELD setting)
exp GET parameter (see the SECURE_LINK_EXPIRES_FIELD setting)It is worth considering that if the files are not in the private directories listed in the SECURE_LINK_PRIVATE_PREFIXES setting, then the private link generation will be skipped. As a result, we will get a public link without the additional GET parameters token and exp.
A full working example for different versions of Django can be found at this link. The example shows the simplest installation of the Nginx module via the nginx-extras package, but it is worth noting that if you build Nginx from source code, you can also add this module in the step./configure, avoiding installing nginx-extras on the system. We have designed a repository with an example by creating branches for specific versions of the Django framework. We hope this will simplify the integration of the package into your projects.