¿Qué es un proxy inverso?
Un proxy inverso en este caso es, básicamente, un servidor web que se interpone como una capa entre el cliente y un backend, de manera de optimizar la conexión. Típicamente el proxy es un servidor muy liviano que funciona de frontend, atiende las peticiones de los clientes HTTP y deriva el procesamiento en un backend que podría ser un servidor Apache. Según la configuración que apliquemos, un proxy nos permite introducir mayor seguridad en nuestra red, hacer balanceo de carga, hacer cache, etc.
También optimiza el manejo de memoria. Pensemos que Apache lanza un thread o proceso por cada nuevo cliente, el cual se cierra recién cuando termina la transferencia de datos. Si el cliente tiene una conexión lenta, por más que Apache funcione rápido, el proceso queda corriendo hasta que se terminen de enviar los datos. Un frontend liviano como Nginx nos permite que el proceso que espere al cliente sea mucho más liviano que uno de Apache.
Por último, como indican en sysadmin.es, un proxy Nginx nos sirve para prevenir ataques de denegación de servicio utilizando slowloris.
Un proxy inverso en un servidor de hosting
Los proxies se suelen utilizar en arquitecturas para servir sitios de alta demanda. En esos casos es común, por ejemplo, hacer que Apache sirva el contenido dinámico y un servidor más liviano (lighttpd o nginx) sirva contenido estático. Pero en un servidor de hosting esto no es tan sencillo, pues al alojarse varios sitios en un mismo equipo nuestra configuración debe ser lo más genérica posible para que sirva a la mayor parte de nuestros clientes. Como veremos, podemos definir algún tipo de cache, pero también tiene que ser bastante genérico para no causar problemas. Además tenemos que pensar en la integración con el panel de control que estemos usando. Yo uso Directadmin y este panel no tiene (aún) una integración nativa con otro web server que no sea Apache.
Nginx + Apache + Directadmin
La opción que les presento es para utilizar Nginx como proxy inverso, manejando las conexiones de los clientes y haciendo un muy básico cache del contenido estático. La guía está pensada para CentOS, pero en otros sistemas operativos no debería ser muy distinto.
Primero instalamos Nginx. El proceso es muy sencillo.
# cd /usr/src
# wget http://nginx.org/download/nginx-0.7.67.tar.gz
# tar zxvf nginx-0.7.67.tar.gz
# cd nginx-0.7.67
# ./configure --prefix=/usr \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/run/nginx/nginx.lock \
--with-http_stub_status_module \
--with-openssl=/usr/lib/openssl
# make && make install
Creamos el directorio para guardar el cache de contenido estático:
# mkdir -p /var/tmp/nginx
# chown apache:apache /var/tmp/nginx
Lo más importante es configurar Nginx. Para ello modificaremos /etc/nginx/nginx.conf para que quede algo similar a esto:
Importante: reemplazar __SERVER_IP__ por la IP del servidor y __SERVER_HOSTNAME__ por el nombre del servidor.
user apache;
worker_processes 1;
events {
worker_connections 8192;
}
http {
server_tokens off;
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 75 20;
gzip on;
server_names_hash_bucket_size 64;
reset_timedout_connection on;
client_max_body_size 100m;
# Main cache data
proxy_cache_path /var/tmp/nginx/cache levels=1:2 keys_zone=staticfilecache:180m;
proxy_temp_path /var/tmp/nginx/proxy;
proxy_connect_timeout 30;
proxy_read_timeout 120;
proxy_send_timeout 120;
proxy_cache_key "$scheme$host$request_uri";
server {
listen __SERVER_IP__:81;
server_name __SERVER_HOSTNAME__ _;
#charset koi8-r;
charset off;
access_log off;
#access_log /var/log/nginx/access.log main;
# Main reverse proxy for most requests
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://__SERVER_IP__; # apache here
client_max_body_size 16m;
client_body_buffer_size 128k;
#proxy_buffering off;
proxy_buffering on;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 120;
proxy_buffer_size 8k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
error_page 502 503 /50x.html;
}
# Proxy cache for static files
location ~* \.(jpg|png|gif|jpeg|css|swf|mov|doc|pdf|xls)$ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://__SERVER_IP__; # apache here
client_max_body_size 16m;
client_body_buffer_size 128k;
#proxy_buffering off;
proxy_buffering on;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 120;
proxy_buffer_size 8k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
# Proxy cache data
proxy_cache_valid 200 120m;
expires 864000;
proxy_cache staticfilecache;
error_page 502 503 /50x.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/html;
}
}
}
Por supuesto esta es una configuración básica que debería adaptarse al caso específico. Es importante notar lo siguiente:
- Nginx escucha en el puerto 81 y Apache en el 80. Esto es importante para no tener que hacer cambios en la configuración de Directadmin.
- Se definen 3 Locations. Las primeras dos son proxies que le pasan requests al Apache esuchando en el puerto 80. La segunda aplica solamente a los requests de archivos estáticos y hace un cache en /var/tmp/nginx. Este cache es manejado siguiendo los headers HTTP correspondientes.
Ahora necesitamos instalar un módulo de Apache, mod_rpaf, para poder usar el header X-Real-IP.
# cd /usr/src
# wget http://stderr.net/apache/rpaf/download/mod_rpaf-0.6.tar.gz
# tar zxvf mod_rpaf-0.6.tar.gz
# cd mod_rpaf-0.6
# apxs -i -c -n mod_rpaf-2.0.so mod_rpaf-2.0.c
Y luego agregamos esto al httpd.conf
LoadModule rpaf_module /usr/lib/apache/mod_rpaf-2.0.so
RPAFenable On
RPAFsethostname On
RPAFproxy_ips 127.0.0.1 __SERVER_IP__
RPAFheader X-Forwarded-For
Reemplazando __SERVER_IP__ por la IP del servidor.
También vamos a necesitar un script para el init del Nginx. Como no encontré uno hecho, hice este:
#!/bin/bash
#
# Name: NginX, tsj5j
#
# Function: Start up NginX
#
# chkconfig: - 85 15
# description: NginX starter
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
prog="nginx"
nginx=/usr/sbin/nginx
start () {
echo -n $"Starting $prog: "
$nginx
RETVAL=$?
return $RETVAL
}
stop () {
echo -n $"Stopping $prog: "
killproc $nginx
RETVAL=$?
return $RETVAL
}
reload () {
echo -n $"Reloading $prog: "
killproc $nginx -HUP
RETVAL=$?
return $RETVAL
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
sleep 1
start
;;
reload)
reload
;;
graceful)
reload
;;
esac
exit $RETVAL;
Una vez ubicado ese contenido en un archivo /etc/init.d/nginx lo habilitamos
# chkconfig --add nginx
# chkconfig nginx on
# service nginx start
Y nos falta una única cosa. Tenemos Apache corriendo en el puerto 80 y Nginx en el 81. ¿Cómo hacemos que Nginx atienda las peticiones de nuestros clientes? Creamos una ruta en iptables para que redirija el tráfico del puerto 81 al 80:
# iptables -t nat -A PREROUTING -p tcp -s ! _SRV_IP_ --dport 80 -j REDIRECT --to-ports 81
# service iptables save
Reemplazando __SERVER_IP__ por la IP del servidor.
Y listo, ahora nuestro Nginx va a recibir todo el tráfico HTTP y negociar con el Apache para devolverlo a los clientes.
Verificar que atienda Nginx
Comprobar que Nginx esté atendiendo las peticiones en el puerto 80 es muy sencillo de hacer con curl. Por ejemplo, probándolo contra la URL de este blog.
# curl -I http://www.tail-f.com.ar
HTTP/1.1 200 OK
Server: nginx
Date: Sat, 18 Sep 2010 04:09:23 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Cookie,Accept-Encoding,User-Agent
X-Pingback: http://www.tail-f.com.ar/xmlrpc.php
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Pragma: no-cache
Como vemos el servidor que atiende es Nginx.
Créditos: