Existe uma maneira de fazer com que o Nginx me notifique se os hits de um referenciador ultrapassam um limite?
Por exemplo, se meu site é apresentado no Slashdot e, de repente, tenho 2 mil acessos em uma hora, quero ser notificado quando ultrapassar 1 mil acessos por hora.
Será possível fazer isso no Nginx? Possivelmente sem lua? (desde que meu prod não é lua compilado)
Respostas:
A solução mais eficaz poderia ser a de escrever um daemon que
tail -f
aaccess.log
, e manter o controle do$http_referer
campo.No entanto, uma solução rápida e suja seria adicionar um
access_log
arquivo extra , registrar apenas a$http_referer
variável com um costumelog_format
e girar o log automaticamente a cada X minutos.Isso pode ser realizado com a ajuda de scripts de logrotate padrão, que podem ser necessários para reiniciar normalmente o nginx para que os arquivos sejam reabertos (por exemplo, o procedimento padrão, consulte / a / 15183322 no SO por um simples período de tempo). script baseado)…
Ou, usando variáveis dentro
access_log
, possivelmente tirando a especificação minuciosa$time_iso8601
com a ajuda da diretivamap
ouif
(dependendo de onde você gostaria de colocar suaaccess_log
).Portanto, com o exposto, você pode ter 6 arquivos de log, cada um cobrindo um período de 10 minutos
http_referer.Txx{0,1,2,3,4,5}x.log
, por exemplo, obtendo o primeiro dígito do minuto para diferenciar cada arquivo.Agora, tudo o que você precisa fazer é ter um script de shell simples que possa ser executado a cada 10 minutos,
cat
todos os arquivos acima juntos, canalizá-losort
, canalizá-louniq -c
parasort -rn
, parahead -16
, e você tem uma lista das 16Referer
variações mais comuns - livre para decidir se alguma combinação de números e campos excede seus critérios e executar uma notificação.Posteriormente, após uma única notificação bem-sucedida, você pode remover todos esses 6 arquivos e, nas execuções subseqüentes, não emitir nenhuma notificação, A menos que todos os seis arquivos estejam presentes (e / ou outro número que desejar).
fonte
Eu acho que isso seria muito melhor feito com logtail e grep. Mesmo que seja possível fazer isso com lua inline, você não deseja essa sobrecarga para todas as solicitações e, principalmente , não deseja quando tiver um Slashdotted.
Aqui está uma versão de 5 segundos. Coloque-o em um script e coloque um texto mais legível em torno dele e você estará dourado.
Obviamente, isso ignora completamente o reddit.com e o facebook.com e todos os milhões de outros sites que podem enviar muito tráfego. Sem mencionar 100 sites diferentes, enviando 20 visitantes cada. Provavelmente, você deve ter apenas um limite de tráfego antigo simples que faça com que um email seja enviado a você, independentemente do referenciador.
fonte
-o
opção é para um arquivo offset, para que ele saiba por onde começar a ler da próxima vez.A diretiva nginx limit_req_zone pode basear suas zonas em qualquer variável, incluindo $ http_referrer.
Você também deseja fazer algo para limitar a quantidade de estado necessária no servidor da web, pois os cabeçalhos do referenciador podem ser bastante longos e variados e você pode ver uma variedade de informações. Você pode usar o recurso nginx split_clients para definir uma variável para todas as solicitações com base no hash do cabeçalho do referenciador. O exemplo abaixo usa apenas 10 buckes, mas você pode fazer isso com 1000 com a mesma facilidade. Portanto, se você receber um slashdotted, as pessoas cujo referenciador tiver hash no mesmo bloco que o URL do slashdot também serão bloqueadas, mas você poderá limitar isso a 0,1% dos visitantes usando 1000 buckets em split_clients.
Seria algo assim (totalmente não testado, mas direcionalmente correto):
fonte
split_clients
pode estar mal informado -limit_req
é baseado em um "depósito com vazamento", o que significa que o estado geral nunca deve exceder o tamanho da zona especificada.Sim, é claro que é possível no NGINX!
O que você pode fazer é implementar o seguinte DFA :
Implemente a limitação de taxa, com base em
$http_referer
, possivelmente usando alguma regex por meio de amap
para normalizar os valores. Quando o limite é excedido, uma página de erro interna é exibida, que você pode capturar através de umerror_page
manipulador de acordo com uma pergunta relacionada , indo para um novo local interno como redirecionamento interno (não visível para o cliente).No local acima, para limites excedidos, você executa uma solicitação de alerta, permitindo que a lógica externa execute a notificação; essa solicitação é armazenada em cache posteriormente, garantindo que você receba apenas uma solicitação única por um determinado período de tempo.
Capture o código de status HTTP da solicitação anterior (retornando um código de status ≥ 300 e usando
proxy_intercept_errors on
, ou, alternativamente, use o não construído por padrãoauth_request
ouadd_after_body
para fazer uma sub-solicitação "gratuita") e conclua a solicitação original como se o passo anterior não estava envolvido. Observe que precisamos habilitar oerror_page
tratamento recursivo para que isso funcione.Aqui está meu PoC e um MVP, também em https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf :
Observe que isso funciona conforme o esperado:
Você pode ver que a primeira solicitação resulta em um resultado de front-end e de back-end, conforme o esperado (eu tive que adicionar um back-end fictício ao local que possui
limit_req
, porque umreturn 200
teria precedência sobre os limites, um back-end real não é necessário para o restante do manuseio).A segunda solicitação está acima do limite, portanto, enviamos o alerta (obtendo
200
) e o armazenamos em cache, retornando429
(isso é necessário devido à limitação acima mencionada de que solicitações abaixo de 300 não podem ser capturadas), que são capturadas posteriormente pelo front-end , que agora é gratuito para fazer o que quiser.A terceira solicitação ainda está excedendo o limite, mas já enviamos o alerta, portanto, nenhum novo alerta é enviado.
Feito! Não se esqueça de bifurcar no GitHub!
fonte
limit_req
, e o outro é umlimit_conn
, basta usar olimit_req_status 429
acima (requer um nginx muito novo), e acho que você deveria ser de ouro; pode haver outras opções (uma a trabalhar com certeza é encadear o nginx comset_real_ip_from
, mas, dependendo do que exatamente você deseja fazer, pode haver opções mais eficientes).golang
, ou procurar nas opções de tempo limite para upstreams; Além disso, convém usarproxy_cache_lock on
também e, possivelmente, adicionar algum tratamento de erro para o que fazer se o script falhar (por exemplo, o usoerror_page
tambémproxy_intercept_errors
). Confio que meu POC é um bom começo. :)limit_req
/limit_conn
? Por exemplo, basta colocar a configuração acima na frente do seu servidor front-end atual. Você pode usarset_real_ip_from
no nginx upstream para garantir que os IPs sejam contabilizados corretamente na linha. Senão, se ainda não se encaixar, acho que você precisa articular suas restrições exatas e as especificações de forma mais vívida - de que níveis de tráfego estamos falando? Com que frequência o stat precisa ser executado (1min / 5min / 1h)? O que há de errado com alogtail
solução antiga ?