Защита от DDoS-атак на уровне Nginx: лимиты подключений - Компьюти
Logo КОМПЬЮТИ

Защита от DDoS-атак на уровне Nginx: лимиты подключений

Время на прочтение: 4 мин.

DDoS-атаки не всегда требуют сложной инфраструктуры. Иногда достаточно большого количества одновременных соединений или большого числа запросов к серверу, чтобы исчерпать доступные ресурсы. Один из способов снизить такой риск — настроить ограничения на уровне Nginx.

В этой статье разберём, как работают лимиты подключений в Nginx, какие директивы использовать и как настроить их так, чтобы защита не мешала обычным пользователям.

Зачем ограничивать подключения

Каждое соединение с сервером потребляет определённое количество ресурсов: память, файловые дескрипторы, сетевые ресурсы и время CPU. Если злоумышленник создаёт тысячи соединений одновременно, сервер может начать испытывать дефицит ресурсов ещё до того, как приложение получит существенную нагрузку.

Nginx позволяет ограничить количество одновременных соединений с одного IP-адреса. Это не является полноценной защитой от DDoS, но помогает отсеивать часть простых атак и ограничивать влияние одного источника трафика.

Основная директива для этой задачи — limit_conn.

Как работает limit_conn

Сначала нужно создать зону, в которой Nginx будет хранить информацию о клиентах:

http {
    limit_conn_zone $binary_remote_addr zone=perip:10m;

    server {
        ...
    }
}

Здесь:

  • $binary_remote_addr — IP-адрес клиента в компактном бинарном формате;
  • perip — имя зоны;
  • 10m — объём памяти, выделенный под эту зону.

Затем ограничение подключений применяется с помощью limit_conn:

server {
    limit_conn perip 20;

    location / {
        proxy_pass http://backend;
    }
}

В данном случае Nginx разрешает одному IP-адресу иметь не более 20 одновременно учитываемых подключений.

Если лимит превышен, Nginx отклоняет новые подключения с соответствующим статусом.

Почему используется $binary_remote_addr

Вместо $remote_addr обычно рекомендуется использовать $binary_remote_addr.

Оба варианта позволяют идентифицировать клиента по IP, но бинарное представление занимает меньше памяти. Это особенно важно, когда таблица клиентов должна хранить большое количество адресов.

Для IPv4 и IPv6 размер ключа остаётся небольшим, поэтому $binary_remote_addr хорошо подходит для зон ограничения.

Ограничение не равно rate limiting

Важно различать два механизма:

  • limit_conn ограничивает одновременные подключения;
  • limit_req ограничивает частоту обработки запросов.

Например, клиент может установить несколько соединений и отправлять через них большое количество HTTP-запросов. В такой ситуации одного limit_conn может быть недостаточно.

Для ограничения частоты запросов используется limit_req:

http {
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=5r/s;

    server {
        location / {
            limit_req zone=req_limit burst=10 nodelay;

            proxy_pass http://backend;
        }
    }
}

Здесь базовая скорость составляет 5 запросов в секунду на IP. Параметр burst позволяет временно принять всплеск запросов, не блокируя клиента сразу.

На практике limit_conn и limit_req часто применяют совместно.

Пример базовой конфигурации

Для обычного веб-сервера можно начать с умеренных ограничений:

http {
    limit_conn_zone $binary_remote_addr zone=perip:10m;
    limit_req_zone  $binary_remote_addr zone=req_limit:10m rate=10r/s;

    server {
        listen 80;
        server_name example.com;

        limit_conn perip 20;

        location / {
            limit_req zone=req_limit burst=20 nodelay;

            proxy_pass http://backend;
        }
    }
}

Такая конфигурация ограничивает одновременно открытые соединения и скорость запросов с одного IP.

Однако универсальных значений 20 или 10r/s не существует. Допустимые параметры зависят от приложения, характера трафика и количества пользователей.

Осторожно с NAT и прокси

Ограничение по IP имеет важный недостаток: несколько реальных пользователей могут выглядеть для сервера как один IP-адрес.

Например, сотрудники компании могут выходить в интернет через единый NAT. То же самое возможно в некоторых мобильных сетях и публичных Wi-Fi.

Если установить слишком маленький лимит, защита начнёт блокировать не атакующего, а обычных пользователей.

Поэтому значения нужно подбирать на основании реального трафика и проверять по логам.

А если перед Nginx стоит балансировщик?

Если Nginx находится за CDN, reverse proxy или балансировщиком, $remote_addr может содержать адрес промежуточного сервера, а не исходного клиента.

В такой архитектуре необходимо корректно настроить восстановление исходного IP, например через real_ip:

http {
    set_real_ip_from 10.0.0.0/8;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    limit_conn_zone $binary_remote_addr zone=perip:10m;
}

Но диапазоны в set_real_ip_from нельзя копировать бездумно. В них должны находиться только доверенные прокси или балансировщики. Иначе злоумышленник сможет подменять заголовок с IP-адресом.

Как подобрать лимит

Лучше не начинать с максимально жёстких значений. Сначала стоит определить нормальное поведение приложения.

Полезно посмотреть:

  1. сколько одновременных соединений обычно создаёт один клиент;
  2. какие URL получают наибольшую нагрузку;
  3. как часто пользователи отправляют запросы;
  4. сколько соединений выдерживает backend;
  5. какие пики наблюдаются во время обычной работы.

После этого можно установить ограничение с запасом и постепенно корректировать его.

Для публичного API лимиты обычно должны быть более продуманными, чем для обычного сайта. Для API также имеет смысл учитывать токены, пользователей или другие идентификаторы, а не только IP.

Что делать с ошибками лимита

При превышении ограничений Nginx может возвращать ошибку клиенту. При необходимости её можно обработать отдельно:

limit_conn_status 429;
limit_req_status 429;

Код 429 Too Many Requests хорошо подходит для ситуации, когда клиент превысил установленное ограничение.

При этом важно понимать, что изменение HTTP-кода само по себе не усиливает защиту. Оно лишь делает реакцию сервера более понятной для клиента и приложений.

Как проверить конфигурацию

Перед применением изменений стоит проверить синтаксис:

nginx -t

Если проверка прошла успешно, конфигурацию можно перечитать без полной остановки сервера:

nginx -s reload

После этого следует проверить логи и убедиться, что реальные пользователи не начали получать ошибки из-за слишком строгих ограничений.

Чего Nginx не сможет сделать в одиночку

Лимиты подключений — это защитный слой, а не полноценная DDoS-защита.

Если атака создаёт огромный объём трафика, сетевой канал или инфраструктура перед сервером могут быть перегружены ещё до того, как запросы попадут в Nginx. В таком случае локальные лимиты уже не спасут.

Для крупных атак обычно нужны дополнительные уровни защиты:

  • CDN и reverse proxy;
  • фильтрация трафика на уровне сети;
  • специализированные DDoS-защитные сервисы;
  • rate limiting на API Gateway или балансировщике;
  • мониторинг и автоматическое реагирование.

Главная задача Nginx в такой схеме — не заменить внешнюю DDoS-защиту, а уменьшить нагрузку на приложение и сделать инфраструктуру устойчивее к всплескам запросов.

Итог

limit_conn — простой и полезный механизм Nginx для ограничения количества одновременных подключений. В сочетании с limit_req он позволяет защитить backend от части чрезмерной активности на HTTP-уровне.

При настройке важно помнить о трёх вещах:

  • лимит подключений и лимит запросов решают разные задачи;
  • ограничение по IP может затронуть нескольких реальных пользователей за NAT;
  • Nginx не способен самостоятельно остановить крупную DDoS-атаку, если перегружается сетевой канал.

Поэтому оптимальная стратегия — использовать лимиты Nginx как один из уровней многоуровневой защиты, а значения подбирать на основе реальной статистики нагрузки.


Читайте также:

Приглашаем поучаствовать в обсуждении статьи:

Комментариев 0