Назад | Перейти на главную страницу

Nginx limit_conn наследование

Я пытаюсь защитить 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$ блок:

  1. Когда я тестирую этот конфиг с 5 одновременными подключениями ab -n 100 -c 5 http://test.dev/, блокировки не происходит. Как только я подниму
    ограничение до 11 -c 11 nginx начинает блокировать запросы.
  2. Если я изменю глобальный лимит подключений с 10 до 5, nginx ограничит более 6 подключений (-c 6) - похоже, директива в location ~ \.php$ блок игнорируется.
  3. Если я удалю conn_limit директива на уровне сервера, директива в location ~ \.php$ блок, вдруг начинает работать!
  4. Еще больше сбивает с толку: если я добавлю 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$ блок.