Autorización nginx a traves de node.js usando X-Accel-Redirect

Nginx es un servidor web potente y rico en características. En este artículo vamos a combinar el uso de los internals y la cabecera X-Accel-Redirect para gestionar recursos a usuarios que tengan una sesión en express.

Lo primero, si no tenéis instalado nginx será descargarlo y compilarlo.

wget http://nginx.org/download/nginx-1.2.3.tar.gz
tar zxvf nginx-1.2.3.tar.gz
cd nginx-1.2.2
./configure
sudo make install

Esto nos dejará nginx instalado en /usr/local/nginx. Vamos a crear un pequeño archivo de configuración para nuestro ejemplo:

# Sample nginx config
daemon  off;
worker_processes 1;

events {
  worker_connections  64;
}

http {
  include         mime.types;
  default_type    application/octet-stream;

  server {
    listen               4444;
    server_name          localhost;

    # proxy a nuestra aplicación express
    location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_set_header X-Forwarded-Proto https;
      proxy_pass       http://localhost:3000/;
      proxy_redirect   off;
    }

    # archivos protegidos
    location ~ /protected/(.*) {
      internal;
      root /usr/local/nginx/xaccel_protect/;
    }
  }
}

Lo copiamos a /usr/local/nginx/conf/nginx.xaccel.conf, también añadiremos unos archivos en /usr/local/nginx/xaccel_protect/protected que serán a los que demos acceso a través de nuestra aplicación express. Dales permiso de lectura para todo el mundo para que puedan ser servidos:

sudo chmod 777 /usr/local/nginx/xaccel_protect/protected/*

Arrancamos en nginx con un comando como:

sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.xaccel.conf

Si accedemos a http://localhost:4444 y veremos un bonito 502 indicandonos que todavía nos falta la parte de la aplicación. Si hemos puesto una imagen como prueba1.png en /usr/local/nginx/xaccel_protect/protected y probamos http://localhost:4444/protected/prueba1.png veremos que tampoco es accesible.

Vamos con la parte en node.js, estoy usando node.js 0.8.8 y express 3.0.0rc4. Podemos instalar esta versión de express usando un comando como:

sudo npm install -g express@3.0

Creamos la aplicación e instalamos las dependencias:

express --sessions xaccel
cd xaccel
npm install

La probamos:

node app.js

Y nos dirijimos a http://localhost:4444

Primero crearemos una mini autenticación para nuestras usuarios. Modificamos /views/index.jade y le añadimos un pequeño formulario:

extends layout

block content
  h1= title
  p Welcome to #{title}
  if !auth
    form(method="post")
      p
        | Say the magic word
        input(name="magic")
      p
        input(type="submit")
  else
    p You watch TLOTR!

Necesitamos una ruta nueva para gestionarla, vamos a routes/index.js y lo modificamos para que quede así

exports.index = function(req, res){
  res.render('index', { title: 'Express', auth: req.session.auth });
};

exports.auth = function(req, res){
  if( req.body.magic == "friend" ) {
    req.session.auth = true;
  }
  res.redirec("/");
};

Y se la añadimos a app.js

...
app.get('/', routes.index);
app.post('/', routes.auth);

http.createServer(app).listen(app.get('port'), function(){
...

Cerramos el server y volvemos a arrancarlo y probamos a autenticarnos:

Ahora nos falta permitir acceso a este usuario a nuestros archivos protegidos, esta es la parte importante, añadimos otra ruta a routes/index.js

...
exports.protect = function(req, res){
  if( req.session.auth ){
    res.header("X-Accel-Redirect", "/protected/"+req.params.filename);
    res.end();
  } else {
    res.send(404);
  }
}

y al app.js

...
app.post('/', routes.auth);
app.get('/protect/:filename', routes.protect);

Lo que estamos haciendo es hacer accesible a través de /protect/* lo que tenemos protegido en /protected/* simplemente enviándole la cabecera X-Accel-Redirect a nginx que se encargará del resto.

Volvemos a parar el server y podemos probar:

Nos autentificamos

y ahora nuestros archivos son accesibles!

Esta técnica puede resultar muy útil para hacer accesible todas la características que nos brinda nginx sólo a los usuarios que queramos y es realmente sencilla de aplicar.

Happy coding!.