Я пытаюсь защитить Drupal, работающий на Nginx, от простого DDoS с помощью limit_conn и limit_req. Но я столкнулся с каким-то странным поведением с наследованием limit_conn
директива, которую я не могу объяснить.
Я сократил свою конфигурацию nginx (1.8.0) до этого минимума, что показывает проблему:
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;
}
}
Как видите, я хочу ограничить количество одновременных подключений до 10 для всех запросов и до 4 для бэкэнда php. (В этом примере я изменил соединение 4 на 1, чтобы его было легче запускать)
Документация Nginx по адресу http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn утверждает, что:
Эти директивы наследуются от предыдущего уровня тогда и только тогда, когда нет
limit_conn
директивы на текущем уровне.
Но в моих тестах происходит что-то странное, похоже, что nginx игнорирует директиву limit_conn
внутри location ~ \.php$
блок:
ab -n 100 -c
5 http://test.dev/
, блокировки не происходит. Как только я подниму-c 11
nginx начинает блокировать запросы. -c 6
) - похоже, директива в location ~ \.php$
блок игнорируется.conn_limit
директива на уровне сервера, директива в location ~ \.php$
блок, вдруг начинает работать!conn_limit
директива к location /
блок, он правильно перезаписывает глобальный!Может проблема в try_files
директива или множественные перенаправления? Буду очень благодарен, если кто-нибудь сможет объяснить, почему conn_limit
директива не перезаписывается, как ожидалось.
Спасибо @AlexeyTen, надеюсь, что смогу ответить на вопрос.
Важны два момента:
limit_conn
директива обрабатывается только один раз за запрос!limit_conn
директивы на текущем уровне. TL; DR: если есть limit_conn
директиве, nginx заглядывает в дочерний блок в цепочке выполнения, если нет limit_conn
, родительский обрабатывается. После этого все остальные limit_conn
директивы игнорируются.
Я пытаюсь объяснить это на своем примере:
В опубликованном мною примере, когда "http://test.dev/"запрос поступил, сначала nginx не обрабатывает limit_conn
в server
уровень, но ждет, чтобы найти подходящее место location /
. Но на этом уровне нет limit_conn
директива, поэтому nginx обрабатывает директиву server
уровень. После этого все limit_conn
директивы игнорируются, потому что он уже был обработан. Это объясняет мои тесты 1-3.
В тесте 4 nginx находит limit_conn
директива в месте location /
и обрабатывает его, но опять же на месте location @rewrite
нет limit_conn
найден, поэтому директива в location /
обрабатывается. Опять же, мы игнорируем location ~ \.php$
блок.