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:
- 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
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.
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.
- Blog de vfmBOFH
- Entra a tu cuenta o crea una para poder comentar.
BOFH
# 119904 Gracias mil
Mis respetos...
MOT
Usuario
# 119914 Vaya tenes...
Vaya tenes muchos conocimientos deberías compartir mas ... has captado mi atención...
_______________________________________
Soy el Linuxero Registrado # 528862
----------------------------------
BOFH
# 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.