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

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

В данном руководстве все программное обеспечение будет устанавливаться в Ubuntu 16.04 или 18.04. Практически без изменений данные решения можно применить и в других дистрибутивах, основанных на Debian.

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

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

Установка Nginx и Redis в Ubuntu Linux

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

$ sudo apt update
$ sudo apt install redis-server nginx nginx-extras lua-nginx-redis

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

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

$ redis-cli
127.0.0.1:6379> ping
PONG                                                                                               
127.0.0.1:6379> quit
$ 

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

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

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

Мы будем использовать рецепт скрипта белого списка, который приведет в 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

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

$ sudo nginx -t && sudo service nginx restart

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

Теперь добавьте в 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 для него (автор использует для этого скрипта resty.redis, а не nginx.redis):

$ sudo sed -i 's|your.redis.server.here|127.0.0.1|' ip_blacklist.lua
$ sudo sed -i 's| \= require "resty.redis"| \= require "nginx.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

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