Estou tentando proteger o Drupal em execução no Nginx contra DDoS simples com limit_conn e limit_req. Mas encontrei um comportamento estranho com herança de limit_conn
diretiva que não consigo explicar.
Reduzi minha configuração do nginx (1.8.0) a este mínimo, o que mostra o problema:
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
server_name test.dev;
root /var/nginx/drupal; ## <-- Your only path reference.
#Allow not more than 10 simultaneous connections from one address.
limit_conn perip 10;
location / {
#limit_conn perip 1;
# This is cool because no php is touched for static content
try_files $uri @rewrite;
}
location @rewrite {
# Clean URLs are handled in drupal_environment_initialize().
rewrite ^ /index.php;
}
location ~ \.php$ {
#Allow not more than 1 simultaneous *connection_to_PHP* from one address.
limit_conn perip 1;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi.conf;
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
}
Como você pode ver, quero limitar o número de conexões simultâneas a 10 para todas as solicitações e a 4 para o backend php.(Neste exemplo modifiquei a conexão 4 para 1, assim seria mais fácil acionar)
A documentação do Nginx emhttp://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_connafirma que:
Estas directivas são herdadas do nível anterior se e só se não existirem
limit_conn
directivas no nível actual.
Mas algo estranho acontece nos meus testes, parece que o nginx está ignorando a diretiva limit_conn
dentro do location ~ \.php$
bloco:
- Quando testo essa configuração com 5 conexões simultâneas
ab -n 100 -c 5 http://test.dev/
, nenhum bloqueio acontece. Assim que aumento o
limite para 11,-c 11
o nginx começa a bloquear solicitações. - Se eu modificar o limite global de conexões de 10 para 5, o nginx limitará mais de 6 conexões (
-c 6
) - parece que a diretiva nolocation ~ \.php$
bloco é ignorada. - Se eu excluir a
conn_limit
diretiva no nível do servidor, a diretiva emlocation ~ \.php$
bloco de repente começa a funcionar! - Ainda mais confuso: se eu adicionar a
conn_limit
diretiva aolocation /
bloco, ela substituirá corretamente a global!
Talvez o problema esteja na try_files
diretiva ou em múltiplos redirecionamentos? Ficaria muito grato se alguém pudesse explicar por que razão a conn_limit
directiva não está a ser substituída, como esperado.
Responder1
Graças a @AlexeyTen, espero poder responder à pergunta.
Importantes são dois pontos:
- A
limit_conn
diretiva é processada apenas uma vez por solicitação! - Estas directivas são herdadas do nível anterior se e só se não existirem
limit_conn
directivas no nível actual.
DR: se houver uma limit_conn
diretiva, o nginx espia o bloco filho na cadeia de execução, se não houver limit_conn
, o pai é processado. Depois disso, todas as outras limit_conn
diretivas serão ignoradas.
Tento explicar com meu exemplo:
No exemplo que postei, quando um "http://test.dev/"A solicitação chega, a princípio, o nginx não está processando limit_conn
no server
nível, mas espera para verificar o local correspondente location /
. Mas neste nível não há limit_conn
diretiva, então o nginx processa aquela no server
nível. Depois disso, todas limit_conn
as diretivas são ignoradas, porque já foi processado. Isso explica meus testes 1-3.
No teste 4, o nginx encontra uma limit_conn
diretiva em location location /
e a processa, mas novamente, em location location @rewrite
no limit_conn
é encontrado, então a diretiva at location /
é processada. Novamente, estamos ignorando o location ~ \.php$
bloco.