1. Caching
El caching permite ahorrar llamadas a los servidores, hay varios tipos de caching:
- Nginx Proxy Cache : que cachea las respuestas de la granjua de servidores a los que llama (upstream servers). Se preferirá este método para las llamadas a los servidores
- Lua Shared Dictionary :(lua_shared_dict) : que guarda pares de claves y valores. En principio este método es mejor cuando solo se utilizan llamadas tipo clave-valor
- FastCGI Cache : Para respuestas FastCGI como PHP u otras peticiones FastCGI
Para nuestro caso utilizaremos la primer opción, que tiene estops parámetros:
Para la parte http de ngonx.conf se utiliza:
- proxy_cache_path :La carpeta donde se guardan los datos cacheados
- levels=1:2 : Estructura de las carpetas que guardan los datos
- keys_zone=my_cache:10m Define una zona de memoria compartida de metadatos de 10MB
- max_size=100m Define un tamaño máximo de 100MB
- inactive=60m Elimina los datos que no se han usado durante 60 minutos
- use_temp_path=off No permite usar almacenamiento temporal
Para la parte de rutas concretas a cachear (location) y se utilizan estos parámetros.
- proxy_cache my_cache Permite cachear utilizando la zona my_cache.
- proxy_cache_valid – Especifica durante cuanto tiempo se cachearan diferentes respuestas.
- proxy_cache_bypass – Permite hacer un bypass del cache en determinadas condiciones
- add_header X-Cache-Status $upstream_cache_status – Añade un caché adaptado para verificar el estado del caché (HIT, MISS, BYPASS).
Para ficheros estáticos, podemos optar para que el caché lo suministre el navegador, pero en este caso, los ficheros tienen estáticos tienen que estar dentro del servidor openresty (nginx) y no dentro de pasándole estos 2 parámetros:
- expires max que le pide al navegador que guarde el fichero lo máximo posible
- add_header Cache-Control "public, max-age=31536000, immutable"
- public → Permite el caching tanto para el browser como los proxys
- max-age=31536000 → Cachea el fichero para 1 año (in segundos).
- immutable → Indica que el el fichero nunca cambia, por tanto no lo revalida nunca
Veamos el varios casos:
1.1 Cacheo de ficheros estáticos que se encuentran dento de una carpeta del servidor openresty (nginx)
Puede servir si el servidor nginx es el mismo que el servidor web
En este caso, lo cacheamos al browser (navegador)
worker_processes auto; error_log /var/log/openresty/error.log notice; events { worker_connections 1024; } http { 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"'; error_log /usr/local/openresty/nginx/logs/error.log info; access_log /var/log/openresty/access.log main; sendfile on; keepalive_timeout 65; # Enable Lua shared dictionary for sessions lua_shared_dict sessions 10m; lua_package_path '/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/openresty/nginx/conf/?.lua;;'; lua_package_cpath '/usr/local/lib/lua/5.1/?.so;;'; # Enable cache proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=60m use_temp_path=off; # Creamos una bateria de servidores para softprop con un solo servidor upstream softpropsrv { server 0.0.0.0:5001; #server 127.0.0.1:5001; #server el_que_sea.com; } server { #->listen 8001; listen 8449 ssl; #->New server_name localhost; #server_name edu.tavernes.es; #-> Begin new # SSL certificate configuration ssl_certificate /usr/local/openresty/nginx/conf/wildcard2023Nginx.crt; ssl_certificate_key /usr/local/openresty/nginx/conf/wildcard2023Nginx.rsa; # Optional SSL settings for security and performance ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # Common proxy settings (optional) proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; # Location of static files location /static/ { #1. Solo ficheros dentro del servidor openresty (nginx) # Are good both root and alias, but in alias you must append "static/" to the path #root /home/eduard/MyPython/11.softprop-01; # Path where static files are stored alias /home/eduard/MyPython/11.softprop-01/static/; # Path where static files are stored # Use browser cache !!! expires max; access_log off; gzip on; gzip_types text/css application/javascript image/svg+xml; } location /softprop { include nginx.conf.softprop; } # Redirect root to login page location = / { return 301 /login; } # Serve the login form location = /login { include nginx.conf.login; } location = /initial { include nginx.conf.initial; } # Protected proxy location location /protected { include nginx.conf.protected; } #location = /success { # include nginx.conf.success; #} location = /error { include nginx.conf.error; } location = /logout { include nginx.conf.logout; } } }
1.2 Cacheo de ficheros estáticos que se encuentran dentro de una carpeta del servidor web (uvicorn)
Par ello tenemos que modificar el program menu_main.py para definbir la ruta a la carpeta a ficheros estáticos con:
app.mount("/static", fh.StaticFiles(directory="/home/eduard/MyPython/11.softprop-01/static"), name="static")
#!/home/eduard/MyPython/11.softprop-01/venv_softprop/bin/python3 #1. Imports #------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path from sqlalchemy import Table path_root1 = Path(__file__).parents[1] # Damos un salto de directorio hacia arriba-> ximoutilsmod sys.path.append(str(path_root1)) path_root0 = Path(__file__).parents[0] # El mismo directorio sys.path.append(str(path_root0)) from menus.mnu_fh import fh, app #OJO: No eliminar las dependencias marcadas en gris (routes01mnu, routes02form, routes03grid,app) pues, sinó falla el programa from menus import routes00comp, routes01mnu, routes02form, routes03grid # No eliminar !!! from menus.mnu_fh import fh, app # No eliminar app !!! from basicutils import xmdb from session.xmsessionmodels import SessionStoreAbst, SessionStore, SessionStoreHst # No eliminar !!! # ------Fin imprescindible from basicutils import xmdb # Create database tables xmdb.Base.metadata.create_all(bind=xmdb.engine) # Execute the web server, but now not using fh.serve but uvicorm.run instead #fh.serve() if __name__ == "__main__": import uvicorn app.mount("/static", fh.StaticFiles(directory="/home/eduard/MyPython/11.softprop-01/static"), name="static") uvicorn.run(app, host="0.0.0.0", port=5001, ssl_keyfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.rsa", ssl_certfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.crt")
Y para cachear el servidor web nesa ruta , cambiamos la configuración del fichero /usr/local/openresty/nginx/conf/nginx.conf
Y utilizamos una caché tipo nginx-proxy
worker_processes auto; error_log /var/log/openresty/error.log notice; events { worker_connections 1024; } http { 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"'; error_log /usr/local/openresty/nginx/logs/error.log info; access_log /var/log/openresty/access.log main; sendfile on; keepalive_timeout 65; # Enable Lua shared dictionary for sessions lua_shared_dict sessions 10m; lua_package_path '/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/openresty/nginx/conf/?.lua;;'; lua_package_cpath '/usr/local/lib/lua/5.1/?.so;;'; # Enable cache proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=60m use_temp_path=off; # Creamos una bateria de servidores para softprop con un solo servidor upstream softpropsrv { server 0.0.0.0:5001; #server 127.0.0.1:5001; #server el_que_sea.com; } server { #->listen 8001; listen 8449 ssl; #->New server_name localhost; #server_name edu.tavernes.es; #-> Begin new # SSL certificate configuration ssl_certificate /usr/local/openresty/nginx/conf/wildcard2023Nginx.crt; ssl_certificate_key /usr/local/openresty/nginx/conf/wildcard2023Nginx.rsa; # Optional SSL settings for security and performance ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # Common proxy settings (optional) proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; # Location of static files location /static/ { #2. Solo ficheros dentro del uno de los servidores que actuan como web servers del upstream "softpropsrv" proxy_pass https://softpropsrv/static/; proxy_set_header Host $host; proxy_ssl_verify off; # If using SSL proxy_cache my_cache; proxy_cache_valid 200 30d; proxy_cache_use_stale error timeout updating; add_header X-Cache-Status $upstream_cache_status; } location /softprop { include nginx.conf.softprop; } # Redirect root to login page location = / { return 301 /login; } # Serve the login form location = /login { include nginx.conf.login; } location = /initial { include nginx.conf.initial; } # Protected proxy location location /protected { include nginx.conf.protected; } #location = /success { # include nginx.conf.success; #} location = /error { include nginx.conf.error; } location = /logout { include nginx.conf.logout; } } }
2. Logging
El logging ya lo deifinimos inicialmente en nginx.conf con el siguiente código parcial:
http { 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"'; error_log /usr/local/openresty/nginx/logs/error.log info; access_log /var/log/openresty/access.log main;
3. Rate limiting
Mediante el "rate limiting" se controla el número de peticiones que un cliente puede hacer en un instante. En principio sirve para prevenir ataques DDoS (distributed denial of service) o de fuerza bruta o un uso excesivo de la API.
Se utilizan estas 2 directivas que contienen parámetros:
- limit_rq_zone: se define en el apartado http {..} y se le da un nombre (en este caso api_limit) con el parámetro zone, un tamaño (10 MB)y una tasa de peticiones (request) por segundo con rate, si se quiere hacer un seguimiento sobre la IP del cliente se usará $binary_remote_addr, y si se quiere hacer por usuario se usará $http_authorization : Se pueden definir varios limit_req_zone, teniendo la precaución de asignar diferentes nombres.
http { # Define rate limit (10 requests per second, with a burst of 20) limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
- limit_req: se define en el apartado del endpoint (direccion) afectado y tiene 2 parámetros, el primero zone identifica el nombre del limit_rq_zone anterior, busrt que indica el número de peticiones temporales a modo de ráfaga que puede admitir, y nodelay que rechaza el exceso de request inmediatamente si sobrepasan los umbrales indicados. En vez de nodelay se puede indicar delay con ún número de request a retrasar, y sobrepasado este límite se rechazan. Este ejemplo no es el real !!!
http { limit_req_zone $binary_remote_addr zone=low_limit:10m rate=5r/s; limit_req_zone $http_authorization zone=high_limit:10m rate=20r/s; server { listen 80; location /api/slow/ { limit_req zone=low_limit burst=10 nodelay; proxy_pass http://backend; } location /api/fast/ { limit_req zone=high_limit burst=30 delay=10; proxy_pass http://backend; } } }
Si queremos comprobar si va bien o no el rate limiting, se puede ejecutar este shell:
ab -n 50 -c 1 http://yourserver/api/
Veamos que problemas pueden surgir según el ChatGPT y como solucionarlo:
Error | Cause | Fix |
---|---|---|
503 Service Unavailable | Requests exceed rate and burst limit. | Increase burst or use delay . |
Rejected requests even when under limit | Requests exceed rate per microsecond. | Use a higher burst value. |
IP is blocked too long | Requests accumulate in the zone. | Use a smaller zone size (5m instead of 10m ). |
4. Filtrado por paises NO VA?????
4.1 Instalar ip2location-resty
sudo opm get ip2location/ip2location-resty
y se instala en /usr/local/openresty/site/lualib
y en concreto copia estas 2 librerías: ip2location.lua y ip2locationwebservice.lua
4.2 Indicar la ruta de las librerías "lua" instaladas en el nginx.conf
La variable lua_package_path debe incluir "/usr/local/openresty/site/lualib/?.lua" que es donde se ha instalado.
En mi caso es:
lua_package_path '/usr/local/openresty/site/lualib/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/openresty/nginx/conf/?.lua;/usr/local/openresty/nginx/conf/?.lua;;'
4.3 Descargar la BD "gratis" desde https://lite.ip2location.com/
Seleccionar o "Sign Up" en la parte superior dercha o "Sign Up Free Account"
Nos pide seleccionar una cuenta
A continuación nos pide que hagamos login y después usamos otra vez la cuenta de google
Ahora podemos descargar la base de datos. En principio hay que descargar el formato BIN.
Para detectar fraudes solo me hace falta la de IP-COUNTRY
Ahora se crea una carpeta, en mi caso /usr/local/openresty/ip2location y descargamos la base datos ahí y descomprimimos el fichero zip y luego copiamos el contenido a la carpeta padre /usr/local/openresty/ip2location para no tener tantas subcarpetas.
Supongamos que queremos que solamente puedan acceder los paises de la Unión Europea, veamos los pasos a seguir:
4.1 Instalar el módulo GeoIP2 Module
sudo apt update
sudo apt install libmaxminddb-dev
4.2 Descargar la BD GeoIP2
mkdir -p /usr/share/GeoIP
cd /usr/share/GeoIP
wget https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-Country.mmdb
4.3 Configurar OpenResty para usar GeoIP2
Veamos como queda esta parte en nginx.conf. Se plantea crear un map para definir los paises qu tienen permiso (esto es solo un ejemplo)
http {
#Definimos la dependencia GeoIP
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
$geoip2_data_country_code country iso_code;
}
#Definimos los paises que pueden acceder EU (European Union)
map $geoip2_data_country_code $allowed_country {
default 0;
# European Union countries
~^(AT|BE|BG|HR|CY|CZ|DK|EE|FI|FR|DE|GR|HU|IE|IT|LV|LT|LU|MT|NL|PL|PT|RO|SK|SI|ES|SE)$ 1;
}
server {
listen 443 ssl;
server_name yourdomain.com;
location / {
# Only allowed countries
if ($allowed_country = 0) {
# Allow access or perform specific logic
return 403 "Access Denied";
}
proxy_pass http://backend;
}
}
}
5. Configuración final
Veamos los ficheros afectados de la carpeta /usr/local/openresty/nginx/conf.
Se indica en estos colores:
Amarillo: filtrado de paises
Naranja: Rate limiting
Azul: Caché
nginx.conf
worker_processes auto; error_log /var/log/openresty/error.log notice; events { worker_connections 1024; } http { 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"'; error_log /usr/local/openresty/nginx/logs/error.log info; access_log /var/log/openresty/access.log main; sendfile on; keepalive_timeout 65; # Enable Lua shared dictionary for sessions lua_shared_dict sessions 10m; lua_package_path '/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/openresty/nginx/conf/?.lua;;'; lua_package_cpath '/usr/local/lib/lua/5.1/?.so;;'; # Enable cache proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=60m use_temp_path=off; # Creamos una bateria de servidores para softprop con un solo servidor upstream softpropsrv { server 0.0.0.0:5001; #server 127.0.0.1:5001; #server el_que_sea.com; } # Hacemos un "rate limiting" para evitar un número de peticiones excesivas # Define rate limit (10 requests per second, with a burst of 20) por IP limit_req_zone $binary_remote_addr zone=my_api_limit:10m rate=10r/s; #Definimos la dependencia GeoIP geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip2_data_country_code country iso_code; } #Definimos los paises que pueden acceder EU (European Union) map $geoip2_data_country_code $allowed_country { default 0; # European Union countries ~^(AT|BE|BG|HR|CY|CZ|DK|EE|FI|FR|DE|GR|HU|IE|IT|LV|LT|LU|MT|NL|PL|PT|RO|SK|SI|ES|SE)$ 1; } server { #->listen 8001; listen 8449 ssl; #->New server_name localhost; #server_name edu.tavernes.es; #-> Begin new # SSL certificate configuration ssl_certificate /usr/local/openresty/nginx/conf/wildcard2023Nginx.crt; ssl_certificate_key /usr/local/openresty/nginx/conf/wildcard2023Nginx.rsa; # Optional SSL settings for security and performance ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # Common proxy settings (optional) proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; # Location of static files location /static/ { #1. Solo ficheros dentro del servidor openresty (nginx) ## Are good both root and alias, but in alias you must append "static/" to the path ##root /home/eduard/MyPython/11.softprop-01; # Path where static files are stored #alias /home/eduard/MyPython/11.softprop-01/static/; # Path where static files are stored ## Use browser cache !!! #expires max; #access_log off; #gzip on; #gzip_types text/css application/javascript image/svg+xml; #2. Solo ficheros dentro del uno de los servidores que actuan como web servers del upstream "softpropsrv" proxy_pass https://softpropsrv/static/; proxy_set_header Host $host; proxy_ssl_verify off; # If using SSL proxy_cache my_cache; proxy_cache_valid 200 30d; proxy_cache_use_stale error timeout updating; add_header X-Cache-Status $upstream_cache_status; } location /softprop { include nginx.conf.softprop; } # Redirect root to login page location = / { # Aplicamos el rate limiting a esta ruta limit_req zone=my_api_limit burst=30 delay=10; # Only allowed countries if ($allowed_country = 0) { # Allow access or perform specific logic return 403 "Access Denied"; } return 301 /login; } # Serve the login form location = /login { include nginx.conf.login; } location = /initial { include nginx.conf.initial; } # Protected proxy location location /protected { include nginx.conf.protected; } #location = /success { # include nginx.conf.success; #} location = /error { include nginx.conf.error; } location = /logout { include nginx.conf.logout; } } }
PROBLEMAS:
1. nginx[983041]: nginx: [emerg] unknown directive "geoip2" in /usr/local/openresty/nginx/conf/nginx.conf:44
La sentencia en cuestión es:
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip2_data_country_code country iso_code; }
Hacemos las siguientes averiguaciones
No hay comentarios :
Publicar un comentario