Nginx como proxy HTTP

De regalo de Reyes os traigo un post bastante sencillo sobre Nginx. Se trata de configurar Nginx para que funcione como un proxy HTTP, pero antes de nada...

¿Qué es un proxy?

Un proxy no es más que un intermediario, que es el significado que tiene la palabra proxy en inglés, en la comunicación que se realiza entre dos puntos. Por ejemplo, entre un cliente, que puede ser un navegador web, petición Ajax, etc; y un servidor. Hay muchos tipos o aplicaciones distintas para un proxy como pueden ser proxy inverso (reverse proxy), proxy transparente, proxy cache; y todas ellas se pueden combinar en una misma configuración.

Por ejemplo, podríamos configurar un proxy HTTP inverso con cache para acelerar el tiempo de respuesta de éste a medida que se va utilizando. En este caso vamos a configurar un proxy HTTP inverso, pero...

¿Que nos ofrece un proxy HTTP inverso?

Antes de nada, nuestro proxy, como su propio nombre indica, va a estar
orientado al servicio HTTP o HTTPS (HTTP Secure), es decir, sólo va a trabajar con peticiones HTTP. Aunque Nginx como tal, también podría actuar como IMAP Proxy, un proxy para el protocolo IMAP (Internet Message Access Protocol) de correo, pero no va a ser este el caso. Como proxy inverso nos va a permitir:

  • Añadir seguridad, protegiendo al resto de servidores web del ataque directo de los usuarios.
  • Reescribir las URLs según nuestras necesidades.
  • Securizar el acceso a nuestras aplicaciones web con HTTPS, es decir, podremos enrutar la petición HTTP hacia HTTPS y securizar la comunicación entre los dos puntos.

Imaginemos que en nuestra red corporativa o doméstica, tenemos varios
servidores web en nuestra DMZ publicando diferentes aplicaciones web, pero
queremos controlar la publicación de cada una de éstas al exterior. En ese
caso, podríamos redirigir todo el tráfico HTTP entrante desde el firewall hacia el proxy HTTP y controlar la publicación de las aplicaciones web al exterior. Como he comentado anteriormente, podríamos añadir HTTPS obligatoriamente al acceder a una aplicación web, añadir autenticación básica (usuario y contraseña), etc.

Configuración

Vamos a suponer que Nginx ya se encuentra instalado en nuestro sistema. Si no fuera el caso, es posible consultar mi anterior articulo sobre la instalación de Nginx. La configuración que obtengo, eliminando los comentarios, tras haber instalado Nginx desde los repositorios de Debian 7 (Wheezy) es la siguiente:

root@nginx:/# grep -v "#" /etc/nginx/nginx.conf |uniq
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

events {
  worker_connections 768;
}

http {

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;

  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  gzip on;
  gzip_disable "msie6";

  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

NOTA: la configuración suele ser diferente dependiendo del método de
instalación: utilizando los repositorios de la distribución o compilando desde las fuentes.

¿Qué significa ésta configuración?

Podemos apreciar varias directivas que son globales: user,
worker_processes y pid; y varios bloques como events, que configura el número de conexiones para cada worker. El número de conexiones totales es:

total_connections = worker_processes * worker_connections

Y el bloque http que define algnas directivas como:

  • keepalive_timeout : tiempo que se va a mantener una conexión viva.
  • include : permite incluir ficheros que contienen más configuración como en este caso los tipos MIME y los ficheros de configuración en el directorio sites-enabled.
  • access_log : define el fichero de acceso donde se registrarán las conexiones al proxy http.
  • error_log : igual que access_log pero solo registrará los intentos fallidos de conexión.
  • gzip : permite comprimir los datos enviados con gzip, consumiendo menos ancho de banda.

Es posible que dentro del bloque http podamos encontrar otro bloque
llamado server y que contenga algo como lo siguiente:

server {
    listen       80;
    server_name  localhost;

    location / {
        root   html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

Es necesario eliminar este bloque de configuración en el fichero de
configuración principal nginx.conf ya que el bloque server lo definiremos
para cada uno de los sitios a publicar, dentro del directorio sites- available.

sites-available y sites-enabled

Normalmente, y sobretodo si se instala Nginx utilizando los repositorios del sistema, durante la instalación se crean dos directorios llamados sites-available y sites-enabled, pero ¿para qué función tienen y para qué se usan? Muy fácil.

  • sites-available : se utiliza para almacenar la configuración de cada sitio o aplicación web. Siguiendo las buenas prácticas, se debe crear un fichero de configuración por cada sitio, para evitar tener la configuración de todos los sitios en un solo fichero.
  • sites-enabled : directorio que utiliza Nginx para saber qué sitios están activados. El contenido de este directorio deben ser enlaces simbólicos que apuntan a los ficheros de configuración del directorio sites-available.

Nota: la creación de los directorios sites-available y sites-enabled son una práctica muy común realizada por la paquetería del sistema, es decir, es una acción que realiza el paquete descargado de los repositorios durante la instalación. Pero es muy probable que dichos directorios no aparezcan si se instala Nginx desde las fuentes. En ese caso, solamente habría que crear dichos directorios e incluir el futuro contenido de estos mediante la directiva include en la configuración principal de Nginx.

Publicando un sitio web

Ya estamos casi a punto. Sólo nos falta configurar una redirección en el
directorio sites-available y enlazarla con un enlace simbólico en sites- enabled, así que vamos a ello!

  1. Creamos el fichero de configuración test.manuelviera.es.conf en el
    directorio /etc/nginx/sites-available/ con una configuración como la
    siguiente:

       server {
           listen 80;
           server_name     test.manuelviera.es;
           location / {
               proxy_pass http://192.168.1.200:8080;
               proxy_set_header  X-Real-IP  $remote_addr;
               proxy_set_header  Host  $http_host;
           }
       }
    

Nota: creo que es buena práctica establecer como nombre de fichero el mismo que el dominio que estamos publicando, es decir, el valor de la directivaserver_name. De esta forma, le indicando nuestro Nginx que cuando reciba una petición del dominio "test.manuelviera.es" por el puerto 80, debe redirigir la petición HTTP al host 192.168.1.200 al puerto 8080, que es donde se encuentra nuestra aplicación web desplegada.

El ingrediente estrella en esta configuración es el uso del módulo proxy_pass incluido en el Core de Nginx, y es la directiva que nos permite pasar la petición que nos llega hacia otro destino, en este caso, el servidor web donde se aloja nuestra supuesta aplicación.

Como podéis observar, también hemos hecho uso de otra directiva llamada proxy_set_header que nos permite añadir o modificar cabeceras, en este caso hemos editado dos cabeceras:

  • X-Real-IP : contiene la IP del cliente que inicia la petición, y se ha establecido el valor de la variable $remote_addr con la idea de que al servidor destino le llegue la IP del cliente y no la del proxy HTTP. Si no se hubiese modificado esta cabecera (header) la IP que recibiría el servidor web objetivo siempre sería la del proxy HTTP.

  • Host : Al igual que la anterior cabecera, establecemos el valor con el contenido de la variable $http_host, es decir, el nombre de host que especificó el cliente.

    1. Una vez configurado nuestra primera redirección, sólo nos falta activarla, es decir, crear un enlace simbólico hacia esta en el directorio
      sites-enabled:

         root@nginx:~# cd /etc/nginx/sites-enabled/
         root@nginx:/etc/nginx/sites-enabled# ln -s ../sites-available/test.manuelviera.es.conf
         root@nginx:/etc/nginx/sites-enabled# ls -l
         total 0
         
         lrwxrwxrwx 1 root root 43 Jan  6 12:05 test.manuelviera.es.conf -> ../sites-available/test.manuelviera.es.conf
      
    2. Una vez enlazada el fichero de configuración, debemos obligar a Nginx a recargar la configuración con la siguiente instrucción:

         root@nginx:~# service nginx reload
         Reloading nginx configuration: nginx.
      

¡Perfecto! Pero aún nos queda el último paso, y no por ello menos importante...

Comprobar el funcionamiento del proxy HTTP

Siempre debemos comprobar que lo que hemos hecho realmente funciona, ya que de no ser así, es como si no hubiésemos hecho nada y daremos mala imagen como profesionales. Si lo que tenemos es un entorno de prueba, que aún no se encuentra implantado en producción, una prueba muy sencilla sería utilizar el fichero /etc/hosts añadiendo la IP de nuestro proxy HTTP y el dominio especificado en la directiva server_name, de la siguiente forma:

$ sudo sh -c "echo 192.168.1.200 test.manuelviera.es >> /etc/hosts"

Nota: en mi caso, el proxy HTTP se encuentra en la IP 192.168.1.200. Si
todo ha ido bien, nuestro proxy HTTP, tras realizar la petición, deber habernos redirigido al host especificado en la directiva proxy_pass :-)

Otra prueba sencilla para comprobar que el proxy HTTP funciona es especificar un sitio externo como terra.es, google.es, etc; en la directiva proxy_pass, si aún no se dispone de un servidor web interno que sirva una aplicación web.

Y esto ha sido todo amigos! Espero que os sea de utilidad y Feliz día de Reyes!

Un saludo.