* Imagenes de instalación de las versiones estables para Mageia y OpenMandriva.

OpenMandriva: Mageia (Mageia 9) 20/Agosto/2023 - Anuncio, Descargas.

Blogdrake recomienda descargar las imágenes de instalación (iso) vía torrent para evitar corrupción de datos, aprovechar mejor su ancho de banda y mejorar la difusión de las distribuciones.

nginx, php-fpm, microcaché y otras judiadas.

Bueno, queridos. Como lo piden por ahí y por allá, he preparado este articulillo para explicaros como montar un stack LEMP con su micro caché, un par de afinadas para evitarnos sustos por parte de los jueinkers y los bots, y de paso, le clavamos un Drupal en toda la cresta para que trukulo esté contento.

Vamos a ello.

Hace mucho, igual me liaba aquí con comandos específicos de una distro, tipo Mandriva o Mageia. Pero como últimamente toco más distros que los de distrowatch, pues lo que haremos será asumir que todos somos mayorcitos y que sabemos usar el maldito gestor de paquetes de nuestra distro. Lo que sí haré será especificar un orden de instalación. Los nombres pueden variar, al igual que las rutas de los archivos de configuración. Pero nada insalvable para alguien con un par de dedos de frente que tenga instalado mlocate en su sistema.

Los primeros paquetes que instalaremos seran mysql-server y mysql-client. A continuación, nginx y php-fpm. Éste último, en funcion de la distro, puede llamarse php5-fpm.

Vamos con la configuración general de nginx. Ésta se almacena en el archivo /etc/nginx/nginx.conf. Lo abrimos con nuestro editor favorito y modificamos los valores de worker_processes worker_connections y keepalive_timeout fijándolos a 4, 768 y 10 respectivamente.
A continuación, y dentro del mismo archivo, añadimos las siguientes directivas justo antes de los dos includes que hay al final del archivo:

fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=microcache:5m inactive=5m max_size=500m;
log_format cache '$upstream_cache_status [$time_local] $remote_user "$request" $status "$http_referer" "$http_user_agent"';

Qué hacemos con ésto:

  • Definimos dónde creará nginx el directorio que usará para cacheo.
  • Limitaremos el arbol de directorio con la directiva "levels". La profundidad del árbol vendrá definida por la cantidad de parámetros que le pasemos tras el signo igual, en este caso, dos. Nginx aplica la función MD5 sobre la URL del objeto a cachear para asignarle un nombre. De ése nombre, el primer valor especifica cuántos caracteres por la derecha usará nginx para el nombrar el directorio de primer nivel y el segundo cuantos caracteres más para el directorio del segundo nivel. Más claro con un ejemplo:

  • suponiendo que aplicando md5sum a la url del objeto a cachear obtengamos:
    b7f54b2df7773722d382f4809d65029c nginx crearía bajo /var/cache/nginx/ el directorio c (1 caracter por la derecha) del que colgaría el directorio 29 (dos más) donde se almacenaría el objeto cacheado con el nombre b7f54b2df7773722d382f4809d65029c
    ¿Enrevesado? sí, pero no lo vamos a gestionar nosotros.

  • Definiremos la zona de caché (podemos tener más de una) y el tamaño máximo del índice de claves con "keys_zone"
  • Definiremos un tiempo de 5 minutos sin accesos antes de eliminar los objetos cacheados con "inactive"
  • Definiremos un tamaño máximo para el directorio de caché de 500MB con "max_size"
  • Finalmente, definimos el formato del archivo de logs. En éste caso, los campos serán:
    • Información de caché sobre el objeto servido (HIT/MISS/EXPIRED)
    • Fecha del sistema
    • Ip remota
    • Objeto solicitado al servidor
    • Estado de la solicitud (200, 404, 403...)
    • Recurso que enlaza al objeto (si está disponible)
    • User-agent del navegador visitante
    • TODO EN LA MISMA LÍNEA; QUE OS VEO VENIR

Vale. Ya tenemos definidos los parámetros generales del servidor web. Ahora vamos a por los detalles específicos.

Si os habeis fijado, al final del archivo /etc/nginx/nginx.conf tenemos éstas dos líneas:

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

Indican al programa que debe cargar los archivos especificados en las dos rutas. En /etc/nginx por lo general encontraremos éstos dos directorios, además de un tercer directorio llamado sites-available. El contenido de sites-enabled en realidad son enlaces que apuntan a los archivos de configuración de sites-available. Por defecto, encontramos el archivo default, que es con el que vamos a trabajar. Os dejo un ejemplo real y comentado de un archivo default:

#Definimos el servidor que gestionará el fastcgi_pass que indicaremos más
#adelante, para la gestión de php. En este caso, php-fpm
upstream php { server unix:/var/lib/php-fpm/php-fpm.sock; }
#Definimos nuestro servidor. En nginx una instancia "server"
#sería análoga a la de un vhost de apache o lighty
server{
        listen 80;                            #Puerto de escucha
        root /var/www/;                       # Directorio raíz del sitio
        index index.php index.html index.htm; # Posibles tipos de página índice
        server_name tusitio.com;              # Nombre del server (vhost)
        include /etc/nginx/seguridad.conf;    # Podemos incorporar contenidos de 
                                              # otros archivos
### las instancias "location" definen objetos y directorios dentro del "server"
### que deben tratarse de forma diferente a los objetos generales
        location = /favicon.ico {
                        log_not_found off;    # Desactivamos el registro de errores de archivo
                                              # no encontrado
                        access_log off;       # Desactivamos el registro de acceso al objeto
                }
        location = /robots.txt {
                        allow all;            # Permitimos el acceso a todo el mundo. Un poco chungo.
                        log_not_found off;    #Lo mismo que antes
                        access_log off;
                }
        location ~ \..*/.*\.php$ {            # Con esta regex, definimos qué pasa si alguien quiere
                        return 403;           # abrir un archivo .php que no sea el index
                }                             # en la raíz o superiores: 403 al canto.
                try_files $uri @rewrite;      # try_files mola. Busca en orden los archivos y directorios
                                              # especificados por orden. Y si no, 404 de la muerte.
                                              # OJOCUIDAO con confundir los 404 con las "not found" de drupal
                port_in_redirect off;         # Si desviamos (proxy) peticiones a otro servidor, no incluimos
                                              # en la url el puerto.
        }
        location @rewrite {                   # definimos el contenedor al que hacía referencia try_files
                rewrite ^/(.*)$ /index.php?q=$1; # cambiamos la url original por la de drupal sin clean url
                }
        location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                access_log off;               # Contenedor para el tratamiento de imágenes del server
                log_not_found off;            # Sin registro de acceso, o de not found
                expires max;                  # Tiempo de validez en la caché del navegador cliente
                root /var/www/directorio_de hostpedaje/;
        }
        location ~ .php$ {                    # La chicha. Contenedor para el manejo de php
                set $no_cache "";             # Oh, sí. Podemos definir variables
                if ($request_method !~ ^(GET|HEAD)$) { # OH,SÍ. CONDICIONALES TAMBIÉN
                        set $no_cache "1";    # Este conjunto de condicionales evalúan la petición entrante
                        }                     # para establecer si debe cachearse o no en el sistema
                if ($no_cache = "1") {
                        add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
                        add_header X-Microcachable "0";
                        }
                if ($http_cookie ~* "_mcnc") {# Teniendo cuidadito con las cookies.... Aunque falla a veces
                        set $no_cache "1";    # hay que revisarlo.
                        }
                fastcgi_no_cache $no_cache;   # En función de la evaluación, tiramos de caché o no
                fastcgi_cache_bypass $no_cache;# En función de la evaluación, nos saltamos el caché o no.
                fastcgi_cache microcache;     # Definimos qué caché mirar (keys_zone en nginx.conf)
                fastcgi_cache_key $server_name|$request_uri;
                fastcgi_cache_valid 404 30m;  # Parámetros varios del caché, duración de los objetos
                fastcgi_cache_valid 200 30s;  # cacheados
                fastcgi_max_temp_file_size 1M;# Tamaño máximo del archivo de intercambio temporal
                fastcgi_cache_use_stale updating;# Manejo de los datos cacheados en caso de timeouts y errores.
                #Línea super importante. Definimos cómo troceamos la petición entre el archivo php
                # y los parámetros para el mismo
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                fastcgi_pass unix:/var/lib/php-fpm/php-fpm.sock;# Ruta al socket php-fpm
                fastcgi_pass_header Set-Cookie;#
                fastcgi_pass_header Cookie;   # Headers que se le pasan a php-fpm
                fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
                fastcgi_index  index.php;     # Definimos qué nombre de archivo incluiremos en el valor de
                                              # SCRIPT_FILENAME en el caso de que la petición sea "/" o sea,
                                              # el nombre de nuestro dominio sin más.
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # Parámetros que le pasamos
                                              # a fastcgi indicando la ruta completa al objeto a tratar.
                include fastcgi_params;       # Incluímos /etc/nginx/fastcgi_params con parámetros extra.
                fastcgi_param  QUERY_STRING     $query_string; #
                fastcgi_param  REQUEST_METHOD   $request_method; #
                fastcgi_param  CONTENT_TYPE     $content_type; #
                fastcgi_param  CONTENT_LENGTH   $content_length; # Más parámetros extra para fastcgi 
                fastcgi_intercept_errors  on; # Usamos drupal, con sus propias páginas de error.
                fastcgi_ignore_client_abort off;# Si se solicita parar la ejecución del script, hacemos caso
                fastcgi_connect_timeout 60;   # Tiempo máximo para intentar conexiones con php-fpm
                fastcgi_send_timeout 60;      # Tiempo máximo que puede tardar una petición a php-fpm
                fastcgi_read_timeout 180;     # Tiempo máximo de php-fpm para llevar a cabo una petición
                                              # análogo en cierto modo al max_execution_time de php.
                fastcgi_buffer_size 128k;     # Tamaño máximo para las cabeceras de las respuestas de php-fpm
                fastcgi_buffers 4 256k;       # Número y tamaño máximos de buffers para cabeceras de respuesta
                fastcgi_busy_buffers_size 256k;# Tamaño máximo que puede emplear php-fpm para cabeceras por
                                               # petición individual
                fastcgi_temp_file_write_size 256k; # Almacenamiento en disco máximo si los buffers se llenan.
                include /etc/nginx/antibot.conf; # Directivas para manejo de spiders
                access_log /var/log/nginx/microcache.log cache; # Ruta al archivo log de actividad del caché
        }
        location ~ /.htaccess { deny all; log_not_found off; access_log off; } # Manejo de archivos apache,
        location ~ /.htpasswd { deny all; log_not_found off; access_log off; } # no usados aquí
}

Hemos visto que en el archivo de configuración podemos incluir referencias a otros archivos. En éste caso, tenemos referencias a seguridad.conf, fastcgi_params y antibot.conf. El archivo fastcgi_params viene con nginx, así que tampoco hay que meterle mano para una instalación medio básica como ésta. Por lo que respecta a los otros dos, los uso para evitar problemas de ancho de banda, exploits, inyecciones SQL y otros asuntos de seguridad en seguridad.conf y respuesta ante los distintos bots que van escaneando el sitio regularmente en antibot.txt. Os dejo el archivo seguridad.conf completo y una muestra de antibot.conf para que le echeis un ojo, lo adapteis y lo modifiqueis en función de vuestras necesidades:

seguridad.conf

#Seguridad: inyecciones SQL
    set $block_sql_injections 0;
    if ($query_string ~ "union.*select.*\(") {
        set $block_sql_injections 1;
    }
    if ($query_string ~ "union.*all.*select.*") {
        set $block_sql_injections 1;
    }
    if ($query_string ~ "concat.*\(") {
        set $block_sql_injections 1;
    }
    if ($block_sql_injections = 1) {
        return 403;
    }
#Seguridad: bloqueo de ficheros
    set $block_file_injections 0;
    if ($query_string ~ "[a-zA-Z0-9_]=http://") {
        set $block_file_injections 1;
    }
    if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") {
        set $block_file_injections 1;
    }
    if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") {
        set $block_file_injections 1;
    }
    if ($block_file_injections = 1) {
        return 403;
    }
# Seguriad: bloqueo de exploits
    set $block_common_exploits 0;
    if ($query_string ~ "(<|%3C).*script.*(>|%3E)") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "proc/self/environ") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "base64_(en|de)code\(.*\)") {
        set $block_common_exploits 1;
    }
    if ($block_common_exploits = 1) {
        return 403;
    }
# Bloqueo de mierdas traga BW
    set $block_user_agents 0;
    # wget
    if ($http_user_agent ~ "Wget") {
        set $block_user_agents 1;
    }
    # Akeeba Remote Control
    if ($http_user_agent ~ "Indy Library") {
        set $block_user_agents 1;
    }
    # Mandangas basadas en librerías perl. Muy comunes.
    if ($http_user_agent ~ "libwww-perl") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "GetRight") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "GetWeb!") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "Go!Zilla") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "Download Demon") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "Go-Ahead-Got-It") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "TurnitinBot") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "GrabNet") {
        set $block_user_agents 1;
    }
    if ($block_user_agents = 1) {
        return 403;
    }

Como veis, se trata básicamente de directivas if basadas en distintas variables que nginx almacena de cada petición entrante. Sólo les escupe un 403, aunque si se trata de un ataque con múltiples conexiones, tipo DDoS perpetrados por mexicanos iracundos, conviene también desactivar el log de errores y el general para no saturar al servidor a base de I/O al disco. Cualquier adición a éstas directivas será bienvenida.

En cuanto al archivo antibot.conf:

 if ($http_user_agent ~* "Googlebot" ) {
                access_log off;
                }

Son simples directivas if basadas en el user agent. Lo que hago es desactivar el registro en los logs de sus visitas, para no saturar el archivo log. Ésto es útil en sitios muy grandes, como un foro oficial de usuarios de Mandriva y Mageia o algo así, donde un bot puede tirarse días escaneando.

La chicha más grande está aquí, pero aún no hemos terminado. Debemos instalar los paquetes que den soporte a php para que pueda interactuar con mysql, unas cuantas funciones gráficas, de cifrado, etc. Básicamente serían php-mysql php-curl php-gd php-intl php-pear php-imagick php-imap php-mcrypt php-memcache php-ming php-ps php-pspell php-recode php-snmp php-sqlite php-tidy php-xmlrpc php-xsl

Algo que puede mejorar un poco el rendimiento es el uso de aceleradores tipo Xcache. A vuestro criterio. A mí me va la mar de bien, pero es un comentario bastante homeopático.

Antes de gritar "yavatodo" y ejecutar nuestra grotesca danza de la victoria, vamos a darnos cuenta de que si por un casual se nos ha ocurrido reiniciar nginx después de modificar el archivo /etc/nginx/sites-available/default habremos obtenido un hermoso casque. Se debe a que hemos definido que nginx se iba a comunicar con php-fpm mediante un socket unix. El problema es que, por defecto, php-fpm se instala de forma y modo que atenderá peticiones vía socket TCP. Por tanto, tocará modificar su configuración para que escuche por donde le vamos a hablar. Para ello tenemos que localizar su archivo de configuración, que puede ser /etc/php-fpm.conf en sistemas Mandriva, /etc/php5/fpm/pool.d/www.conf en sistemas basados en Debian... un poquito de grep no hace daño a nadie.

La directiva a cambiar es:
listen = 127.0.0.1:9000
Y debemos dejarla en:
listen = /var/lib/php-fpm/php-fpm.sock
Que, por cierto, es el valor del parámetro de configuración de nginx "fastcgi_pass".

Y ahora sí. Reiniciamos php-fpm y nginx y la cosa debería estar en marcha.

Para mañana, Drupal y el dios que lo menea.

P.D. Que se me olvidaba la bibliografía.

Si buscais más información podeis empezar por la legendaria howtoforge para instalación básica y seguridad inicial.

Para el tema de microcaché, yo me basé en ésto y ésto.

Y, sobre todo, la extraordinaria y completa documentación de la web de nginx.

Opciones de visualización de comentarios

Seleccione la forma que desee de mostrar los comentarios y haga clic en «Guardar opciones» para activar los cambios.


Gravatar de motitos

# 119904 Gracias mil

Mis respetos...

MOT



Gravatar de aioria

# 119914 Vaya tenes...

Vaya tenes muchos conocimientos deberías compartir mas ... has captado mi atención...

_______________________________________
Soy el Linuxero Registrado # 528862
----------------------------------



Gravatar de vfmBOFH

# 119919 Estoy ocupado

Entre otras muchas cosas, soy uno de los frikazos que mantiene ésto en pie y en línea a base de línea de comandos.
Ya me gustaría forear tanto como antaño.

Atentamente,

La voz ésa del interior de tu cabeza que oyes cuando lees algo.

DISCLAIMER: No tengo mucho tiempo para forear, así que voy al grano. Si crees que mi respuesta es ruda, seca, cortante o hiriente, no lo dudes: LO ES.

Opciones de visualización de comentarios

Seleccione la forma que desee de mostrar los comentarios y haga clic en «Guardar opciones» para activar los cambios.