DjangoTornadoNginxAuth » Cronologia » Versione 3
Versione 2 (Mark Caglienzi, 11-12-2013 19:06) → Versione 3/7 (Mark Caglienzi, 11-12-2013 19:08)
h1. 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). h2. Prerequisiti Servono (oltre a django e tornado, ovviamente) anche i pacchetti: @devscripts@, @mercurial@, @fakeroot@. h2. Compilazione di nginx Esiste "un plugin per nginx":https://github.com/perusio/nginx-auth-request-module (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. <pre> $ 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 </pre> Questo per scaricare il pacchetto sorgente di nginx 1.4.4 e averlo automaticamente scompattato. <pre> $ cd nginx-1.4.4/debian/modules $ hg clone http://mdounin.ru/hg/ngx_http_auth_request_module nginx-auth-request </pre> 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). <pre> # apt-get build-dep nginx </pre> 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: <pre> --add-module=$(MODULESDIR)/nginx-auth-request \ </pre> sotto alle altre direttive @--add-module@, in modo che il file si presenti così: <pre> [...] 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 $@ [...] </pre> A questo punto bisogna compilare i pacchetti binari: <pre> $ cd /home/utente/projects/nginx/nginx-1.4.4 $ fakeroot debian/rules binary </pre> Alla fine della compilazione nella directory @/home/utente/projects/nginx@ saranno presenti i pacchetti binari: <pre> 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 </pre> Si può procedere ad installare i pacchetti necessari direttamente da questa directory: <pre> # 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 </pre> h2. 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":http://www.tornadoweb.org/en/stable/#hello-world) e che le due applicazioni siano servite dai server di sviluppo: <pre> $ cd /home/utente/projects/django $ ./manage.py runserver </pre> <pre> $ cd /home/utente/projects/tornado $ ./hello-world.py </pre> In questo modo il server di sviluppo di django risponde a @127.0.0.1:8000@ e tornado a @127.0.0.1:8888@ h2. Creazione del file di configurazione di nginx Si crei il file @/etc/nginx/sites-available/django@: <pre> upstream tornado { server 127.0.0.1:8888; } 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://tornado; } } </pre> e si attivi con: <pre> # ln -s /etc/nginx/sites-available/django /etc/nginx/sites-enabled/django # /etc/init.d/nginx restart </pre> 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 <pre> 127.0.0.1 django </pre> al file @/etc/hosts@. h2. Modifica delle applicazioni Django e Tornado L'applicazione hello world di tornado va modificata così: <pre> #!/usr/bin/env python import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ # (r"/", MainHandler), (r'/tornado', MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start() </pre> in modo che @MainHandler@ risponda all'URL @/tornado@ anziché all'URL @/@. Nell'applicazione django va aggiunto in @urls.py@: <pre> url(r'^is_logged_in/$', 'myproject.views.is_logged_in', name='is_logged_in'), </pre> e una view di questo tipo: <pre> from django import http def is_logged_in(request): if request.user.is_anonymous(): return http.HttpResponseForbidden("NO") else: return http.HttpResponse("OK") </pre> che non fa altro che restituire un HTTP 403 in caso l'utente non sia loggato, e un HTTP 200 diversamente. Se un template dell'applicazione django ha un link di questo tipo (o se si tenta di scrivere a mano l'URL): <pre> <a href="/tornado" class="btn">Tornado</a> </pre> nginx si comporterà così: * Vede la richiesta a @http://django/tornado@ * La direttiva @auth_request@ passa il controllo all'URL @http://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 * Se la risposta è 403, nginx non prosegue e l'utente si vede impossibilitato a proseguire h2. h3. 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 decoratore @login_required@ nella view django. * Il "changelog di nginx":http://nginx.org/en/CHANGES 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.