Как ограничить трафик с определенной страны в Nginx?

Учитывая, что пользователи из Китая продолжают вызывать проблемы с DDoS-атаками на серверы, несмотря на наличие языковой версии для нормальных пользователей, возникает вопрос: возможно ли осуществить лимитирование запросов по географическому положению страны? 

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

```nginx
limit_req_zone $binary_remote_addr zone=auth:10m rate=35r/s;
```

```nginx
limit_req zone=auth burst=60 delay=35;
limit_req_log_level warn;
limit_req_status 429;
```

У меня два вопроса к опытным специалистам:

1. Можно ли заменить `$binary_remote_addr` на значение, связанное с локалью браузера? Мой сайт создает куки с локалью `locale=zh`, но эта информация может быть недоступна в условиях DDoS-атаки, особенно если у пользователя отключены куки. Есть ли стандартные переменные в Nginx для проверки, указан ли язык пользователя как "zh" в заголовках браузера?

2. Если первый вопрос решаем, возможно ли указать два лимита в одном блоке `server`? Например, выглядит ли это корректно в конфигурации:

```nginx
limit_req_zone $binary_remote_addr zone=auth:10m rate=35r/s;
limit_req_zone $LOCALE_ZH zone=zh_limit:10m rate=10r/s;
```

А в блоке server:

```nginx
limit_req zone=auth burst=60 delay=35;
limit_req zone=zh_limit burst=20 delay=10;
limit_req_log_level warn;
limit_req_status 429;
```

В итоге, я хотел бы реализовать механизмы, которые обеспечили бы пользователей с локалью zh определенными задержками в очереди, когда они обращаются к серверу.
  • 13 апреля 2025 г. 12:45
Ответы на вопрос 2
Ограничение трафика с определенной страны в Nginx можно реализовать с помощью модуля GeoIP или GeoIP2, который позволяет блокировать или ограничивать доступ на основе IP-адреса пользователей. Также следует учитывать, что первое решение с использованием локали браузера менее надежно, так как у пользователей могут быть отключены куки и это не всегда будет доступно.

### Ответ на ваш первый вопрос

1. **Замена `$binary_remote_addr` на значение локали браузера:**

К сожалению, Nginx не имеет стандартного способа проверки локали из заголовков HTTP. Наиболее часто используемым методом для определения языка пользователя является анализ заголовка `Accept-Language`, который может выглядеть примерно так: `Accept-Language: zh-CN`. Однако, для проверки этого заголовка вам нужно будет применять дополнительные механизмы, такие как использование модулей для обработки переменных. Это может требовать сторонних модулей или сложных конфигураций.

Примечание: использование заголовка `Accept-Language` имеет свои ограничения, и не всегда может точно отразить намерения пользователя. Поэтому рассчитывать на это в условиях DDoS-атаки не рекомендуется.

### Ответ на ваш второй вопрос

2. **Указание двух лимитов в одном блоке `server`:**

Вы можете использовать несколько `limit_req_zone` и `limit_req` для разных условий, однако вам нужно создать переменные для определения, относится ли запрос к пользователю из определенной страны. Это можно сделать с помощью GeoIP или другого механизма.

Пример конфигурации с использованием GeoIP:

```nginx
# Загрузка модуля GeoIP
http {
    geoip_country /path/to/GeoIP.dat; # путь к GeoIP базе данных
    map $geoip_country_code $limit_zone {
        default "";
        CN "zh_limit";  # Ограничение для Китая
    }
    
    limit_req_zone $binary_remote_addr zone=auth:10m rate=35r/s;
    limit_req_zone $limit_zone zone=zh_limit:10m rate=10r/s;

    server {
        location / {
            limit_req zone=auth burst=60 delay=35; # Общий лимит
            limit_req zone=zh_limit burst=20 delay=10; # Лимит для пользователей из Китая
            # Дополнительные настройки...
        }
    }
}
```

### Рекомендации

- **GeoIP/GeoIP2**: Убедитесь, что вы используете актуальные базы данных для определения геолокации IP-адресов, такие как MaxMind GeoLite2.
- **Blacklist/Whitelist**: Если у вас есть конкретные адреса или диапазоны IP, которые вы знаете за их злонамеренное поведение, рассмотрите возможность их блокировки с помощью директивы `deny`.
- **Общие ограничения**: Используйте общий лимит для всех пользователей и более строгие или отдельные лимиты для пользователей из стран, которые создают проблемы (например, США, Китай).

Таким образом, вы сможете эффективно управлять трафиком с определенной страны, сохраняя при этом доступность вашего ресурса для легитимных пользователей.
limit_req_zone 
В качестве ключа можно использовать текст, переменные и их комбинации.
Запросы с пустым значением ключа не учитываются.

С помощью map новой переменной присваивать либо пустую строку либо $binary_remote_addr.

limit_req_zone $bad_locale zone=zh_limit:10m rate=10r/s;

map $cookie_locale $bad_locale {
    default     "";
    "zh"        $binary_remote_addr;
}

Дальше использовать limit_req zone=zh_limit как обычно. Ограничивать будеть только тех кто с нужной кукой.
Похожие вопросы