DjangoTornadoNginxAuth » Cronologia » Versione 5
« Precedente |
Versione 5/7
(diff)
| Successivo »
Mark Caglienzi, 12-12-2013 15:12
Syntax highlighting
Django Tornado Nginx Auth¶
Si suppone che si abbia un progetto django nella directory /home/utente/projects/django/
e un progetto tornado nella directory /home/utente/projects/tornado/
. Si vuole configurare nginx in modo che django venga servito all'url http://example.com/
e tornado all'url http://example.com/tornado/
, facendo in modo che l'accesso a tornado sia inibito se non si è autenticati a django.
La guida è condotta su un sistema Debian Jessie, al momento in testing (per altre versioni probabilmente andrà adattato qualcosa).
Prerequisiti¶
Servono (oltre a django e tornado, ovviamente) anche i pacchetti: devscripts
, mercurial
, fakeroot
.
Compilazione di nginx¶
Esiste un plugin per nginx (scritto da uno dei core developer di nginx stesso) che nella versione 1.4.x del server (quella attualmente in debian) non è incluso. Quindi bisogna scaricare il plugin e ricompilare nginx. Per comodità si useranno il più possibile i sorgenti e il sistema di build di debian.
$ mkdir /home/utente/projects/nginx $ cd /home/utente/projects/nginx $ dget http://ftp.de.debian.org/debian/pool/main/n/nginx/nginx_1.4.4-1.dsc
Questo per scaricare il pacchetto sorgente di nginx 1.4.4 e averlo automaticamente scompattato.
$ cd nginx-1.4.4/debian/modules $ hg clone http://mdounin.ru/hg/ngx_http_auth_request_module nginx-auth-request
A questo punto nella sotto-directory debian/modules/nginx-auth-request
c'è il codice del plugin richiesto (preso direttamente dal repository mercurial dove viene sviluppato, dato che il repository su github è meno aggiornato).
# apt-get build-dep nginx
In questo modo verranno installati i pacchetti necessari a compilare nginx. Ora bisogna modificare il file debian/rules
nella sezione riguardante il pacchetto binario nginx-full
(solo in questa, nelle altre non è necessario), aggiungendo la riga:
--add-module=$(MODULESDIR)/nginx-auth-request \
sotto alle altre direttive --add-module
, in modo che il file si presenti così:
[...] config.status.full: config.env.full ### In questa sezione... cd $(BUILDDIR_full) && CFLAGS="$(CFLAGS)" CORE_LINK="$(LDFLAGS)" ./configure \ --prefix=/usr/share/nginx \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --http-client-body-temp-path=/var/lib/nginx/body \ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ --http-log-path=/var/log/nginx/access.log \ --http-proxy-temp-path=/var/lib/nginx/proxy \ --http-scgi-temp-path=/var/lib/nginx/scgi \ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ --lock-path=/var/lock/nginx.lock \ --pid-path=/run/nginx.pid \ --with-pcre-jit \ --with-debug \ --with-http_addition_module \ --with-http_dav_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-ipv6 \ --with-mail \ --with-mail_ssl_module \ --add-module=$(MODULESDIR)/nginx-auth-pam \ --add-module=$(MODULESDIR)/nginx-dav-ext-module \ --add-module=$(MODULESDIR)/nginx-echo \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ --add-module=$(MODULESDIR)/ngx_http_substitutions_filter_module \ --add-module=$(MODULESDIR)/nginx-auth-request \ ### ...aggiungere questa riga $(CONFIGURE_OPTS) >$@ touch $@ [...]
A questo punto bisogna compilare i pacchetti binari:
$ cd /home/utente/projects/nginx/nginx-1.4.4 $ fakeroot debian/rules binary
Alla fine della compilazione nella directory /home/utente/projects/nginx
saranno presenti i pacchetti binari:
nginx_1.4.4-1_all.deb nginx-common_1.4.4-1_all.deb nginx-doc_1.4.4-1_all.deb nginx-extras_1.4.4-1_amd64.deb nginx-extras-dbg_1.4.4-1_amd64.deb nginx-full_1.4.4-1_amd64.deb nginx-full-dbg_1.4.4-1_amd64.deb nginx-light_1.4.4-1_amd64.deb nginx-light-dbg_1.4.4-1_amd64.deb nginx-naxsi_1.4.4-1_amd64.deb nginx-naxsi-dbg_1.4.4-1_amd64.deb nginx-naxsi-ui_1.4.4-1_all.deb
Si può procedere ad installare i pacchetti necessari direttamente da questa directory:
# dpkg -i nginx_1.4.4-1_all.deb nginx-common_1.4.4-1_all.deb nginx-full_1.4.4-1_amd64.deb
Avvio delle applicazioni¶
Dato che la guida si concentra sulla gestione dell'autenticazione tra django e tornado, si assume per semplicità che l'applicazione tornado sia hello-world (link) e che le due applicazioni siano servite dai server di sviluppo:
$ cd /home/utente/projects/django $ ./manage.py runserver
$ cd /home/utente/projects/tornado $ ./hello-world.py
In questo modo il server di sviluppo di django risponde a 127.0.0.1:8000
e tornado a 127.0.0.1:8888
Creazione del file di configurazione di nginx¶
Si crei il file /etc/nginx/sites-available/django
:
server { listen 80; root /home/utente/projects/django; server_name django; access_log /home/utente/projects/django/access.log; error_log /home/utente/projects/django/error.log; location / { proxy_pass http://127.0.0.1:8000; } location /tornado { auth_request /is_logged_in/; proxy_pass http://127.0.0.1:8888; } }
e si attivi con:
# ln -s /etc/nginx/sites-available/django /etc/nginx/sites-enabled/django # /etc/init.d/nginx restart
In questo modo si dice a nginx che per URL sotto a /tornado
deve fare una request a /is_logged_in/
e accettare di passare il controllo a tornado solo se riceve una risposta HTTP 200.
Aggiungere la riga
127.0.0.1 django
al file /etc/hosts
.
Modifica delle applicazioni Django e Tornado¶
L'applicazione hello world di tornado va modificata così:
#!/usr/bin/env python
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, %s!" % self.get_cookie('user')) ### 'Hello, world!' diventa 'Hello, username!'
### Il setup del cookie viene fatto nella view di login di django (v. sotto)
application = tornado.web.Application([
# (r"/", MainHandler), ### Commentare/cancellare questa riga...
(r'/tornado', MainHandler), ### ...e aggiungere questa
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
in modo che MainHandler
risponda all'URL /tornado
anziché all'URL /
. Nell'applicazione django va aggiunto in urls.py
:
url(r'^is_logged_in/$', 'myproject.views.is_logged_in', name='is_logged_in'),
e una view di questo tipo:
from django import http
def is_logged_in(request):
if request.user.is_anonymous():
return http.HttpResponseForbidden("NO")
else:
return http.HttpResponse("OK")
che non fa altro che restituire un HTTP 403 in caso l'utente non sia loggato, e un HTTP 200 diversamente.
Bisogna poi settare un cookie con i dati che si vuole che passino da django a tornado nella view di login (e cancellarlo in quella di logout), ad esempio in questo modo:
from django import http
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
def login_view(request):
username = password = ''
_next = request.GET.get("next", None)
if request.POST:
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
messages.info(request, _('Welcome, %s!' % username))
if _next:
response = http.HttpResponseRedirect(_next)
else:
response = http.HttpResponseRedirect(reverse('home'))
response.set_cookie(
key='user',
value=request.user.username,
expires=request.session.get_expiry_age(),
)
return response
else:
messages.error(request, _('Account %s is disabled' % username))
else:
messages.error(request,
_("The credentials you supplied were not correct or did not grant access to this resource."))
return render(request, 'login.html')
@login_required
def logout_view(request):
logout(request)
response = http.HttpResponseRedirect(reverse('home'))
response.delete_cookie('user')
return response
In questo esempio il cookie user
contiene lo username dell'utente appena loggato, ma si possono definire cookie per qualsiasi dato sia necessario passare tra django e tornado. Come si vede il valore di expires
del cookie è settato uguale all'expiry_age
della sessione attuale.
Se un template dell'applicazione django ha un link di questo tipo (o se si tenta di scrivere a mano l'URL):
<a href="/tornado" class="btn">Tornado</a>
nginx si comporterà così:
- Vede la richiesta a
http://django/tornado
- La direttiva
auth_request
passa il controllo all'URLhttp://django/is_logged_in/
- Django controlla se l'utente è loggato o meno, e dà una response di tipo 200 o 403
- Se la risposta è 200, allora nginx prosegue e passa il controllo a
http://127.0.0.1:8888
dove risponde tornado, che avrà accesso ai cookie eventualmente settati dalla view di login di django - Se la risposta è 403, nginx non prosegue e l'utente si vede impossibilitato a proseguire
Dubbi/problemi¶
- Compilare un plugin (seppur proveniente da una fonte autorevole, come un core dev di nginx) non mi piace troppo, specialmente se in un pacchetto così fondamentale come il server web/proxy
- Non ho ancora risolto il problema per cui l'utente non loggato vede un errore 403 anziché un più educato redirect alla view di login. Purtroppo il plugin non supporta i redirect, ma soltanto 403/200, quindi perché funzioni la direttiva
auth_request
non ho potuto usare il decoratorelogin_required
nella view django. - Il changelog di nginx dice che dalla versione 1.5.4 del 27 agosto 2013 il modulo
ngx_http_auth_request_module
è incluso, quindi ci si può aspettare che anche i futuri pacchetti debian lo includano.
Aggiornato da Mark Caglienzi quasi 11 anni fa · 5 revisions