¿Hay alguna manera de hacer que Nginx me notifique si los hits de un referente exceden un umbral?
Por ejemplo, si mi sitio web aparece en Slashdot y, de repente, recibo 2K visitas en una hora, quiero recibir una notificación cuando supere 1K visitas por hora.
¿Será posible hacer esto en Nginx? Posiblemente sin lua? (ya que mi producto no está compilado por lua)
Respuestas:
La solución más eficiente podría ser escribir un demonio que sería
tail -f
elaccess.log
, y realizar un seguimiento del$http_referer
campo.Sin embargo, una solución rápida y sucia sería agregar un
access_log
archivo adicional , registrar solo la$http_referer
variable con una personalizadalog_format
y rotar automáticamente el registro cada X minutos.Esto se puede lograr con la ayuda de scripts de logrotate estándar, que pueden necesitar reinicios elegantes de nginx para volver a abrir los archivos (por ejemplo, el procedimiento estándar, eche un vistazo a / a / 15183322 en SO por un tiempo simple) guión basado) ...
O, mediante el uso de variables internas
access_log
, posiblemente obteniendo la especificación de minutos$time_iso8601
con la ayuda de la directivamap
o unaif
(dependiendo de dónde le gustaría poner suaccess_log
).Entonces, con lo anterior, puede tener 6 archivos de registro, cada uno de los cuales cubre un período de 10 minutos
http_referer.Txx{0,1,2,3,4,5}x.log
, por ejemplo, obteniendo el primer dígito del minuto para diferenciar cada archivo.Ahora, todo lo que tiene que hacer es tener un script de shell simple que podría ejecutarse cada 10 minutos,
cat
todos los archivos anteriores juntos, canalizarlossort
, canalizarlos auniq -c
, asort -rn
, ahead -16
, y tiene una lista de las 16Referer
variaciones más comunes - libre de decidir si alguna combinación de números y campos excede sus criterios, y realizar una notificación.Posteriormente, después de una sola notificación exitosa, puede eliminar todos estos 6 archivos y, en ejecuciones posteriores, no emitir ninguna notificación A MENOS QUE los seis archivos estén presentes (y / o un cierto otro número como mejor le parezca).
fuente
Creo que esto sería mucho mejor con logtail y grep. Incluso si es posible hacerlo con lua en línea, no desea esa sobrecarga para cada solicitud y, especialmente , no la quiere cuando se le ha asignado Slashdotted.
Aquí hay una versión de 5 segundos. Péguelo en un script y ponga un texto más legible a su alrededor y estará dorado.
Por supuesto, eso ignora por completo reddit.com y facebook.com y todos los millones de otros sitios que podrían enviarle mucho tráfico. Sin mencionar 100 sitios diferentes que le envían 20 visitantes cada uno. Probablemente debería tener un umbral de tráfico antiguo que hace que se le envíe un correo electrónico, independientemente del remitente.
fuente
-o
opción es para un archivo de compensación para que sepa dónde comenzar a leer la próxima vez.La directiva nginx limit_req_zone puede basar sus zonas en cualquier variable, incluyendo $ http_referrer.
Sin embargo, también querrá hacer algo para limitar la cantidad de estado requerida en el servidor web, ya que los encabezados de referencia pueden ser bastante largos y variados y puede ver una variedad infinita. Puede usar la función nginx split_clients para establecer una variable para todas las solicitudes que se base en el hash del encabezado de referencia. El siguiente ejemplo usa solo 10 dólares, pero podría hacerlo con 1000 con la misma facilidad. Por lo tanto, si obtiene una barra oblicua, las personas cuyo referenciador se mezcló en el mismo cubo que la URL de barra oblicua también se bloquearían, pero podría limitar eso al 0.1% de los visitantes al usar 1000 cubos en clientes divididos.
Se vería algo así (totalmente no probado, pero direccionalmente correcto):
fuente
split_clients
puede estar mal informado:limit_req
se basa en un "cubo con fugas", lo que significa que el estado general nunca debe exceder el tamaño de la zona especificada.¡Sí, por supuesto que es posible en NGINX!
Lo que podría hacer es implementar el siguiente DFA :
Implemente la limitación de velocidad, basada en
$http_referer
, posiblemente, utilizando algunas expresiones regulares a través de amap
para normalizar los valores. Cuando se excede el límite, se abre una página de error interno, que puede atrapar a través de unerror_page
controlador según una pregunta relacionada , yendo a una nueva ubicación interna como una redirección interna (no visible para el cliente).En la ubicación anterior para los límites excedidos, realiza una solicitud de alerta, permitiendo que la lógica externa realice la notificación; Esta solicitud se almacena en caché posteriormente, lo que garantiza que solo obtendrá 1 solicitud única por una ventana de tiempo determinada.
Capture el código de estado HTTP de la solicitud anterior (devolviendo un código de estado ≥ 300 y usando
proxy_intercept_errors on
, o, alternativamente, use el no construido por defectoauth_request
oadd_after_body
para hacer una subrequest "libre"), y complete la solicitud original como si el paso anterior no estuvo involucrado. Tenga en cuenta que debemos habilitar elerror_page
manejo recursivo para que esto funcione.Aquí está mi PoC y un MVP, también en https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf :
Tenga en cuenta que esto funciona como se esperaba:
Puede ver que la primera solicitud da como resultado un hit de front-end y un backend, como se esperaba (tuve que agregar un backend ficticio a la ubicación que tiene
limit_req
, porquereturn 200
tendría prioridad sobre los límites, un backend real no es necesario para el resto del manejo).La segunda solicitud está por encima del límite, por lo tanto, enviamos la alerta (obteniendo
200
) y la almacenamos en caché, regresando429
(esto es necesario debido a la limitación mencionada anteriormente de que las solicitudes por debajo de 300 no pueden ser capturadas), que posteriormente es capturado por el front-end , que ahora es libre de hacer lo que quiera.La tercera solicitud aún excede el límite, pero ya hemos enviado la alerta, por lo que no se envía ninguna nueva.
¡Hecho! ¡No olvides bifurcarlo en GitHub!
fuente
limit_req
, y el otro es unlimit_conn
, entonces solo usa ellimit_req_status 429
anterior (requiere un nginx muy nuevo), y creo que deberías ser dorado; puede haber otras opciones (una de las cuales debe estar seguro es encadenar nginx w /set_real_ip_from
, pero, dependiendo de lo que quiera hacer exactamente, puede haber opciones más eficientes).golang
, o buscar en las opciones de tiempo de espera para las secuencias ascendentes; Además, es posible que desee usarproxy_cache_lock on
también, y posiblemente agregue algún manejo de errores sobre qué hacer si el script falla (por ejemplo, usarloerror_page
tan bien como deproxy_intercept_errors
nuevo). Confío en que mi POC es un buen comienzo. :)limit_req
/limit_conn
? Por ejemplo, simplemente coloque la configuración anterior frente a su servidor front-end actual. Puede usarset_real_ip_from
nginx ascendente para garantizar que las IP se contabilicen correctamente en el futuro. De lo contrario, si aún no encaja, creo que debe articular sus restricciones exactas y las especificaciones de manera más vívida: ¿de qué niveles de tráfico estamos hablando? ¿Con qué frecuencia debe ejecutarse la estadística (1 min / 5 min / 1 h)? ¿Qué hay de malo con la viejalogtail
solución?