Перейти к содержанию

ipset-access: Модуль доступа к ipset для NGINX

Установка на Debian/Ubuntu

Эти документы относятся к APT-пакету nginx-module-ipset-access, предоставляемому репозиторием GetPageSpeed Extras.

  1. Настройте APT-репозиторий, как описано в настройке APT-репозитория.
  2. Установите модуль:
sudo apt-get update
sudo apt-get install nginx-module-ipset-access

Warning

Этот модуль еще не опубликован как nginx-module-ipset-access в APT-репозиториях. Оставайтесь на связи или напишите на support@getpagespeed.com, чтобы запросить его.


Высокопроизводительный модуль NGINX, который позволяет вам включать или исключать IP-адреса клиентов с использованием средства ядра Linux ipset.
Все запросы обрабатываются в пользовательском пространстве через libipset и кэшируются на поток-для минимизации накладных расходов.

Особенности

  • Нулевые накладные расходы во время выполнения – сеансы IPSet инициализируются один раз и кэшируются с помощью хранилища, привязанного к потоку.
  • Режимы черного и белого списков – блокируйте все, кроме перечисленных наборов, или разрешайте все, кроме перечисленных наборов.
  • Динамические обновления – изменение ipset не требует перезагрузки NGINX.
  • Нативные RPM-пакеты для RHEL / Alma / Rocky и производных через репозиторий GetPageSpeed.

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

┌ Client ──► NGINX worker
│             │
│             ├─► Thread‑local ipset session (libipset)
│             │      ├─ Test client IP against one or more sets
│             │      └─ Cache session handle for reuse
│             │
└─────────────┴── Allow / Deny based on match & mode

Модуль:

  1. Инициализирует libipset один раз на каждый рабочий процесс.
  2. Кэширует дескриптор сеанса в данных, специфичных для потока POSIX (pthread_setspecific).
  3. Оценивает адрес клиента по каждому настроенному набору.
  4. Возвращает настраиваемый статус (по умолчанию 403 Forbidden), когда запрос заблокирован.

Быстрая установка (для дистрибутивов на основе RPM)

sudo dnf --assumeyes install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf --assumeyes install nginx-module-ipset-access

Совет: Пакет подписан; убедитесь, что у вас включен gpgcheck=1.

Включите модуль в /etc/nginx/nginx.conf перед любыми блоками http {}:

load_module modules/ngx_http_ipset_access.so;

Перезагрузите NGINX для применения:

sudo systemctl reload nginx

Клонирование NGINX и модуля

git clone https://github.com/nginx/nginx.git
git clone https://example.com/ngx_ipset_access.git

cd nginx
./auto/configure   --with-compat   --add-dynamic-module=../ngx_ipset_access
make -j$(nproc)
sudo make install

Сборка создает objs/ngx_http_ipset_access.so; скопируйте его в каталог модулей вашего NGINX и добавьте load_module, как показано выше.

Директивы конфигурации

ipset_blacklist set1 [set2setN]

Контекст: server
Блокирует запросы если IP клиента присутствует в любом из указанных ipset.

ipset_whitelist set1 [set2setN]

Контекст: server
Разрешает запросы только если IP клиента присутствует в указанном наборе. Все другие IP отклоняются.

ipset_status code

Контекст: server
Устанавливает код состояния HTTP, возвращаемый, когда запрос блокируется whitelist или blacklist. По умолчанию 403, если не установлено.
Общие варианты: 403 (Запрещено) или 444 (заблокировать без ответа).

ipset_autoadd setname

Контекст: location
Функциональность ловушки: Автоматически добавляет IP клиента в указанный ipset, когда доступ к локации осуществляется. Идеально подходит для создания конечных точек-ловушек, которые автоматически заносят в черный список вредоносные боты и сканеры.
Когда IP автоматически добавляется, модуль принудительно закрывает текущее соединение (отключает keep-alive), чтобы злоумышленники не могли повторно использовать соединение.

off

Любая директива принимает буквальное слово off для отключения обработки в текущем контексте:

server {
    listen 80;
    blacklist off;   # нет фильтрации ipset в этом виртуальном хосте
}

Пример использования

## Создание ipset для заблокированных адресов
sudo ipset create bad_guys hash:ip
sudo ipset add bad_guys 203.0.113.4
sudo ipset add bad_guys 198.51.100.23
load_module modules/ngx_http_ipset_access.so;

http {
    server {
        listen 80 default_server;
        root /usr/share/nginx/html;

        # Блокировка любого IP, найденного в "bad_guys"
        ipset_blacklist bad_guys;

        # Возврат 444 для заблокированных запросов (по желанию)
        ipset_status 444;
    }
}

Поскольку запросы обрабатываются в реальном времени, добавление или удаление IP из bad_guys вступает в силу мгновенно без перезагрузки NGINX.

Пример ловушки

Создание автоматических ловушек, которые добавляют вредоносные IP в черные списки:

## Создание ipset для функциональности ловушки
sudo ipset create honeypot hash:ip
sudo ipset create malicious_scanners hash:ip
load_module modules/ngx_http_ipset_access.so;

http {
    server {
        listen 80 default_server;
        root /usr/share/nginx/html;

        # Используйте наборы ловушки в качестве черного списка - попавшие IP блокируются
        ipset_blacklist honeypot malicious_scanners;
        # По желанию: настройка статуса блокировки
        ipset_status 403;

        # Обычное содержимое
        location / {
            try_files $uri $uri/ =404;
        }

        # Конечные точки ловушки - боты, которые попадают на эти, автоматически добавляются в черный список
        location /config.php {
            ipset_autoadd honeypot;
            return 200 "Доступ к конфигурации зарегистрирован";
        }

        location /wp-admin.php {
            ipset_autoadd honeypot;
            return 200 "Доступ к администратору зарегистрирован";
        }

        # Поймать общие попытки эксплуатации
        location ~ ^/(phpMyAdmin|admin|eval\.php|shell\.php)$ {
            ipset_autoadd malicious_scanners;
            return 200 "Сканер обнаружен";
        }
    }
}

Когда бот посещает /config.php, его IP автоматически добавляется в набор honeypot и немедленно блокируется от всех последующих запросов. Соединение закрывается после добавления, чтобы предотвратить повторное использование keep-alive. Без внешних скриптов или fcgiwrap!

Логирование и отладка

Соберите NGINX с --with-debug и установите error_log /var/log/nginx/error.log debug;, чтобы увидеть подробный вывод, например:

test bad_guys 203.0.113.4 -> IPS_TEST_IS_IN_SET
Blocking 203.0.113.4 due to IPSET

Коды возврата

Условие HTTP статус
whitelist и не в любом наборе По умолчанию 403 (или ipset_status)
blacklist и присутствует в наборе По умолчанию 403 (или ipset_status)
Временная ошибка ipset (безопасный отказ) По умолчанию 403 (или ipset_status)

Ограничения и план развития

  • Только IPv4 – AF_INET6 еще не поддерживается.
  • Использует синхронные вызовы libipset; при очень высоких скоростях запросов ядро может работать быстрее только с правилами nft set.
  • Индивидуальный возвращаемый статус 444 подготовлен, но закомментирован; включите, если вам нужна семантика отказа без ответа.

Рабочий процесс разработки и тестирования (быстрая итерация)

Чтобы избежать повторного скачивания и сборки NGINX при каждом запуске теста, мы используем предустановленный образ Docker с NGINX и Test::Nginx. Тесты затем компилируют только этот модуль как динамический .so и загружают его через обертку.

Предварительные требования: Docker (и, по желанию, act, если вы хотите запустить рабочий процесс CI локально).

Быстрый старт:

## 1) Сборка повторно используемого базового образа один раз для каждой версии NGINX
make base-image NGINX_VERSION=release-1.27.2

## 2) Запуск всего набора тестов (компилирует только модуль .so при каждом запуске)
make tests NGINX_VERSION=release-1.27.2

## 3) Повторите для отдельного файла теста
make tests NGINX_VERSION=release-1.27.2 T=t/10-ipset.t

Подробности: - Базовый образ определяется в Dockerfile.tests-base и помечается как nginx-ipset-access-tests-base:<NGINX_VERSION>. - В момент тестирования скрипт-исполнитель (docker-run-tests.sh) настраивает NGINX с помощью --add-dynamic-module, указывая на репозиторий (/work), запускает make modules, затем устанавливает: - TEST_NGINX_BINARY на предустановленный бинарный файл nginx - TEST_NGINX_LOAD_MODULES на скомпилированный .so

Советы: - Для логов и скорости в процессе итерации Makefile устанавливает: - TEST_NGINX_FAST_SHUTDOWN=1 - TEST_NGINX_NO_CLEAN=1 (сохраняет t/servroot/) - TEST_NGINX_LOG_LEVEL=debug - TEST_NGINX_ERROR_LOG=/work/test-error.log - Используйте T=<path> для запуска отдельного файла теста. Проверьте t/servroot/conf/nginx.conf и test-error.log при отладке.

Запустите рабочий процесс CI локально с помощью act:

## Требуется: npm i -g act (или brew install act), работающий демон Docker
act -W .github/workflows/build.yml -j build --container-daemon-socket 'unix:///var/run/docker.sock'

Это локально воспроизводит задание GitHub Actions, которое собирает базовый образ и выполняет набор тестов внутри контейнера с правами возможности NET_ADMIN.