Project

General

Profile

DjangoTornadoNginxAuth » History » Version 3

« Previous - Version 3/7 (diff) - Next » - Current version
Mark Caglienzi, 12/11/2013 07:08 PM


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:

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;
    }
}

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, world")

application = tornado.web.Application([
#    (r"/", MainHandler),
    (r'/tornado', MainHandler),
])

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. 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'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

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 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.