Реализация динамических черных и белых списков доступа по IP большого объема в Nginx с помощью Lua и Redis в CentOS 7

В данном руководстве мы рассмотрим как реализовать концепцию большого динамического черного или белого списка с помощью веб-сервера Nginx, сервера Redis и скрипта расширения Nginx, реализованного на Lua.

В данном руководстве все программное обеспечение будет устанавливаться в CentOS 7. Практически без изменений данные решения можно применить и в других дистрибутивах, основанных на RPM и Yum.

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

К счастью, Nginx поддерживает модули расширения на языке Lua, которые совместно с кэшу в памяти Redis позволяют реализовать требуемую функциональность черных и белых списков.

Установка Nginx, Lua и Redis в CentOS 7

Установка всех компонентов будет производиться с помощью Yum.

Вероятно, вам придется подобрать подходящую для пакета nginx-module-lua версию Nginx, поскольку они устанавливаются из разных репозиториев. На момент написания руководства nginx-module-lua «хотел» версию Nginx 1.15.7.

$ sudo yum install epel-release
$ sudo yum update

$ sudo yum install redis

# запускаем redis
$ sudo systemctl start redis
$ sudo systemctl enable redis

# устанавливаем nginx и модуль lua для него
$ sudo yum install -y http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm

$ sudo sed -i -e 's@/packages/@/packages/mainline/@' /etc/yum.repos.d/nginx.repo

$ sudo curl -L https://copr.fedorainfracloud.org/coprs/khara/nginx-module-ndk-lua/repo/epel-7/khara-nginx-module-ndk-lua-epel-7.repo -o /etc/yum.repos.d/nginx-module-ndk-lua.repo

$ sudo yum install -y nginx-1.15.7 nginx-module-lua

# запускаем nginx
$ sudo systemctl start nginx
$ sudo systemctl enable nginx

Проверяем доступность Nginx, открыв страницу по ip-сервера в браузере:

Проверим доступность Redis:

$ redis-cli ping
PONG                                                                                               
$ 

Если вы настраиваете сервер для боевого использования, обязательно настройте безопасность Redis по нашему руководству.

Перейдем к настройке белого или черного списков.

Установка модуля Lua для доступа к Redis

Модуль будем устанавливать из Git-репозитория OpenResty, поскольку в пакетах CentOS данный модуль отсутствует:

$ sudo mkdir -p /opt/openresty && cd /opt/openresty
$ sudo yum install git
$ sudo git clone https://github.com/openresty/lua-resty-redis.git

Добавим путь к модулю в Nginx и протестируем конфигурацию. Для этого отредактируем файл /etc/nginx/nginx.conf, добавив строки, отмеченные в следующем листинге цветом:

# в первые строки файла
load_module modules/ndk_http_module.so;
load_module "modules/ngx_http_lua_module.so";
...
...
http {
    lua_package_path "/opt/openresty/lua-resty-redis/lib/?.lua;;";
    ...
}

Теперь убедимся в том, что конфигурация nginx корректна и перезапустим его:

sudo nginx -t && sudo systemctl restart nginx

Настройка белого списка

Мы будем использовать рецепт скрипта белого списка, который приведет в GitHub. Для его использования внесем несколько директив в Nginx:

В файл /etc/nginx/nginx.conf добавим строку, приведенную ниже:

http {

	##
	# Basic Settings
	##

	lua_shared_dict ip_whitelist 1m;

и в секцию server или location сам обработчик по белому списку. В нашем примере добавим в файл /etc/nginx/sites-enabled/default:

# Default server configuration
#
server {
     listen 80 default_server;
     listen [::]:80 default_server;
     access_by_lua_file /etc/nginx/lua/ip_whitelist.lua;
     ....

Скачаем сам Lua-скрипт:

$ sudo mkdir /etc/nginx/lua
$ cd /etc/nginx/lua
$ sudo wget https://gist.githubusercontent.com/itbdw/bc6c03f754cc30f66b824f379f3da30f/raw/a655713ecbe676558244d44dc78c875d7a84f8d1/ip_whitelist.lua
$ sudo sed -i 's| \= require "nginx.redis"| \= require "resty.redis"|' ip_whitelist.lua

Проверим, конфигурацию Nginx и перезапустим его:

$ sudo nginx -t && sudo systemctl restart nginx

Если теперь вы попытаетесь открыть сайт, то получите страницу с ошибкой вида:

Теперь добавьте в Redis свой IP с помощью консоли Redis:

$ redis-cli SADD ip_whitelist A.B.C.D

и снова проверьте доступ к сайту, должна открываться страница Nginx, которую мы уже видели раньше:

Опять удалите запись для вашего IP с помощью консоли Redis:

$ redis-cli SREM ip_whitelist 148.77.35.49

Результатом доступа к сайту снова будет ошибка.

Таким образом, мы успешно настроили белый список, используя Nginx, Redis и скрипт Lua, который подходит для динамического управления записями и работы с большими списками. Список реализован с помощью высокопроизводительных множеств Redis, скорость доступа к которым подчиняется закону трудоемкости O(1), то есть работает быстро, если по-русски.

Настройка черного списка

Черный список настраивается аналогично с точностью до замены white на black:

  • lua_shared_dict ip_blacklist 1m;
  • access_by_lua_file /etc/nginx/lua/ip_blacklist.lua;
  • скрипт.

Модифицируйте скрипт для использования правильного сервера Redis и правильного модуля Lua для него:

$ sudo sed -i 's|your.redis.server.here|127.0.0.1|' ip_blacklist.lua
$ sudo sed -i 's| \= require "nginx.redis"| \= require "resty.redis"|' ip_blacklist.lua 

Добавление и удаление записей в черный список осуществляется следующими командами:

$ redis-cli SADD ip_blacklist A.B.C.D
$ redis-cli SREM ip_blacklist A.B.C.D

Заключение

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

Хранение списков в Redis позволяет обеспечить высочайшую производительность решения, которая не деградирует с ростом списка адресов в списке. Доступность Redis через API подходит для интеграции со сторонними поставщиками записей для списков.

В том случае, если необходимо сохранение списков между перезагрузками Redis, то в файл конфигурации /etc/redis/redis.conf необходимо изменить строку:

appendonly no

на

appendonly yes

Установка данной конфигурации для CentOS 7 является весьма трудоемкой и требует установки пакетов из внешних репозиториев.

Скрипт на Lua достаточно прост и легко может быть адаптирован для более специфических задач.