В этой статье мы продолжаем разбирать возможности веб‑сервера Angie по борьбе с DoS (и немного DDoS) атаками. В предыдущей части мы разобрали стандартные средства, а в этой обсудим возможности сторонних модулей и системы Fail2Ban.

Навигация по циклу

  1. Почему стоит переходить на Angie.

  2. Установка Angie из пакетов и в докере.

  3. Переезд с Nginx на Angie. Пошаговая инструкция.

  4. Настройка location в Angie. Разделение динамических и статических запросов.

  5. Перенаправления в Angie: return, rewrite и примеры их применения.

  6. Сжатие текста в Angie: статика, динамика, производительность.

  7. Серверное кэширование в Angie: тонкости настройки.

  8. Настройка TLS в Angie: безопасность и скорость.

  9. Настройка Angie в роли обратного HTTP-прокси.

  10. Балансировка нагрузки для HTTP(S) в Angie.

  11. Мониторинг Angie с помощью Console Light и API.

  12. Балансировка и проксирование L4-трафика в Angie.

  13. Клиентское кэширование в Angie.

  14. Динамические группы проксируемых серверов в Angie.

  15. Мониторинг Angie с Prometheus и Grafana.

  16. Отказоустойчивый кластер Angie с VRRP и Keepalived.

  17. Контроль доступа в Angie.

  18. Аутентификация клиентов в Angie с помощью TLS-сертификатов.

  19. Кастомизация Angie (njs, Lua, Perl).

  20. Запуск CGI-скриптов в Angie.

  21. Защита от DoS-атак в Angie стандартными модулями.

  22. Защита от DoS-атак в Angie (дополнительные средства).

Видеоверсия

Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.

Блокировка клиентов с помощью Fail2Ban

Система Fail2Ban позволяет блокировать нелигитимных пользователей различных сервисов. Там уже подготовлены правила для блокировки клиентов, подбирающих реквизиты входа для SSH, FTP, HTTP‑аутентификации, баз данных и так далее. Работает сервис на основе парсинга логов приложений по расписанию. Для всех активированных плагинов (jail в терминологии Fail2Ban) указаны места нахождения логов и критерии блокировок. Эти логи регулярно сканируются и на основе записей в них происходит блокировка. 

Для наших задач подходит jail под названием nginx‑limit‑req. Он работает на основе сообщений в error.log от модуля Limit Req, поэтому будем считать, что он уже настроен (как это сделать описано в предыдущей статье).

Установка Fail2Ban тривиальна для Ubuntu:

apt install fail2ban

Для настройки можно скопировать файл jail.conf в jail.local (каталог /etc/fail2ban/) и вносить в jail.local свои настройки.

Секцию [nginx‑limit‑req] в jail.local приводим к следующему виду:

[nginx-limit-req]
port    = http,https
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath = /var/log/angie/*error.log
findtime = 600
bantime = 7200
maxretry = 4

Таким образом мы активируем поиск сообщений о нарушении лимитов в /var/log/angie/*error.log. Для блокировки требуется 4 срабатывания (maxretry) за 600 последних секунд (findtime), время блокировки — 7200 секунд (2 часа — bantime). В качестве метода блокировки используется действие iptables‑multiport. Это довольно аккуратное ограничение — запрещается доступ только по указанным портам (TCP 80 и 443). Это действие будет добавлять правила через iptables, по одному на каждого нарушителя. Такая схема подходит для небольшого количества блокировок (до 100–1000 адресов), для более масштабных список рекомендуется использовать действие iptables‑ipset‑proto4. Для использования ipset не забудьте установить пакет:

apt install ipset

Теперь мы готовы к тестированию. Натравливаем свою любимую утилиту на сайт с лимитом и получаем блок. Проверить это можно следующим образом:

fail2ban-client status nginx-limit-req

Status for the jail: nginx-limit-req
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	1992
|  `- File list:	/var/log/angie/error.log
`- Actions
   |- Currently banned:	1
   |- Total banned:	1
   `- Banned IP list:	192.168.122.1

Найти правило iptables также не составляет труда:

iptables -nvL

...
Chain f2b-ReqLimit (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 REJECT     0    --  *      *       192.168.122.1        0.0.0.0/0            reject-with icmp-port-unreachable
  113 14563 RETURN     0    --  *      *       0.0.0.0/0            0.0.0.0/0           

Заблокирован адрес клиента 192.168.122.1, а разблокирован он будет через 2 часа. Для принудительной разблокировки выполняем команду:

fail2ban-client set nginx-limit-req unbanip 192.168.122.1

Для блокировки вместо unbanip используем banip

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

Геоблокировка клиентов

Следующий вариант блокировки нежелательных запросов на сервер — геоблокировка на основе IP клиента. Допустим, ваш сайт имеет локальное значение и его основная аудитория находится на территории России. При этом для атак с отказом в обслуживании часто используются ботнеты, собранные из разных стран. Решение очевидное: блокируем все страны, кроме России и Белоруссии. В таком случае мы можем потерять часть полезного трафика и доступность для некоторых поисковых ботов, но по крайней мере для основной аудитории сайт останется доступным.

Для реализации геоблокировки мы будем использовать сторонний модуль GeoIP2. Установим его из репозитория Angie:

apt install angie-module-geoip2

Подключим в контексте main (есть также версии для контекстов http и stream):

load_module modules/ngx_http_geoip2_module.so;

Далее потребуется получить базу GeoIP‑данных, которая содержит геопривязку для диапазонов IP‑адресов. Для наших задач вполне подойдёт база с GitHub‑репозитория. Качаем базу со странами (Country). В моём примере файл базы расположен в каталоге /etc/angie/. Проводим базовые настройки в контексте http:

http {
   geoip2 /etc/angie/GeoLite2-Country.mmdb {
        auto_reload 5m;
        $geoip2_metadata_country_build metadata build_epoch;
        $geoip2_data_country_code default=RU source=$remote_addr country iso_code;
        $geoip2_data_country_name country names en;
    }
    map $geoip2_data_country_code $allowed_country {
    	default no;
      RU yes;
	BY yes;
    }
…
}

Здесь в блоке geoip2 мы подключили использование базы данных с диска и установили страну по умолчанию (RU). Для сопоставления стран и признака разрешения доступа мы создали раздел map, в котором назначаем значение yes переменной $allowed_country в случае стран RU и BY. Теперь мы можем использовать переменную $allowed_country для управления доступом на уровне сайтов или других частей сервера. Например:

server {
…
  if ($allowed_country = no) {
    return 403;
  }
}

Такая конфигурация при значении переменной $allowed_country = no возвращает код 403. При желании можно также использовать 444, но с кодом 403 будет проще решать потенциальные проблемы с ошибочно заблокированными клиентами. Другой вариант блокировки: всё разрешить, а заблокировать только отдельные страны. Например, по анализу логов определяем большое количество ботов из Румынии — блокируем Румынию.

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

Наконец, если все эти методы не помогают отсечь паразитный трафик, можно приступить к фильтрации ботов с помощью модуля Testcookie. 

Модуль Testcookie

Идея, стоящая в основе стороннего модуля Testcookie — определение трафика от ботов на основе возможностей клиента: это обработка cookie и исполнение JS‑кода. При первой попытке доступа клиент получает ответ с заголовком для установки cookie или HTML‑документ с JS‑кодом, результатом работы которого опять же будет cookie. При повторных запросах значение cookie проверяется, после чего доступ предоставляется в случае правильного значения.

Сначала стандартная процедура установки модуля:

apt install angie-module-testcookie

Подключаем в контексте main:

load_module modules/ngx_http_testcookie_access_module.so;

Теперь необходимо настроить модуль в одном из режимов работы. Мы будем использовать вариант, который задействует JS‑проверку. Для более подробного обзора можно посмотреть статью автора этого модуля и документацию.

Настраивать конфигурацию проверок целесообразно на уровне блока server (сайта):

server {
    listen 80;
    root /var/www/static;

    testcookie off;
    testcookie_name BPC;
    testcookie_secret keepmescdret31hio31iocqiojcijcqijowc1ijwcq;
    testcookie_session $remote_addr;
    testcookie_arg attempt;
    testcookie_max_attempts 3;
    testcookie_fallback /cookies.html?backurl=http://$host$request_uri;
    testcookie_get_only on;
    testcookie_redirect_via_refresh on;
    testcookie_refresh_encrypt_cookie on;
    testcookie_refresh_encrypt_cookie_key random;
    testcookie_refresh_encrypt_cookie_iv random;

    testcookie_refresh_template '<html><body>setting cookie...<script type="text/javascript" src="/aes.min.js" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("$testcookie_enc_key"),b=toNumbers("$testcookie_enc_iv"),c=toNumbers("$testcookie_enc_set");document.cookie="BPC="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/";location.href="$testcookie_nexturl";</script></body></html>';

    location = /aes.min.js {
    }

    location = /cookies.html {
    }
}

В этой конфигурации мы используем шаблон проверки с JS‑кодом, реализующим алгоритм slow AES (aes.min.js); получить его код можно из репозитория, далее необходимо применить патч для работы с модулем. 

Теперь получить доступ к этому сайту можно будет только с правильным значением cookie с названием BPC. Проверять работу необходимо в браузере. Нужно понимать, что модуль также отфильтрует трафик легитимных ботов, поэтому имеет смысл добавить белый список IP для известных легитимных ботов. Сделать этом можно с помощью блока testcookie_whitelist (в том же контексте server):

testcookie_whitelist {
        8.8.8.8/32;
}

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

Итоги

В двух статьях мы разобрали довольно широкий диапазон инструментов для борьбы с простыми DoS‑атаками на основе Angie. Эти инструменты можно применять как по отдельности, как и в комплексе мер. Конечно, для отражения полноценной DDoS‑атаки необходимы специализированные средства в виде облачного сервиса, позволяющего абсорбировать большие объёмы трафика и оснащённого надёжной системой определения ботов на основе формальных признаков и их поведения.