Я пытаюсь защитить 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$
блока:
- Когда я тестирую эту конфигурацию с 5 одновременными подключениями
ab -n 100 -c 5 http://test.dev/
, блокировки не происходит. Как только я увеличиваю лимит
до 11,-c 11
nginx начинает блокировать запросы. - Если я изменю глобальный лимит подключений с 10 до 5, nginx ограничит более 6 подключений (
-c 6
) - похоже, директива вlocation ~ \.php$
блоке игнорируется. - Если я удаляю
conn_limit
директиву на уровне сервера, директива вlocation ~ \.php$
блоке внезапно начинает работать! - Еще более запутанно: если я добавляю
conn_limit
директивуlocation /
block, она корректно перезаписывает глобальную!
Может проблема в try_files
директиве или множественных перенаправлениях? Я был бы очень благодарен, если бы кто-то мог объяснить, почему директива conn_limit
не перезаписывается, как ожидалось.
решение1
Спасибо @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 /
и обрабатывает ее, но снова на location location @rewrite
no не limit_conn
найдено, поэтому обрабатывается директива at location /
. Опять же, мы игнорируем location ~ \.php$
блок.