Укрепление Nginx с помощью Fail2ban: тестируем и оцениваем профит

Укрепление Nginx с помощью Fail2ban: тестируем и оцениваем «профит»

Показываем, как с помощью open source-инструмента Fail2ban укрепить Nginx и защитить сервисы от взлома.

Изображение записи

«Только запустил Nginx-приложение, а в access.log уже десятки записей с непонятными запросами, переборами паролей и другого» — типичная ситуация для незащищенного сервера. Это следствие работы «ботов» для сканирования белых сетей, поиска открытых портов, сбора информации о версиях запущенных служб и подбора паролей к сервисам.

В статье расскажу, как с помощью open source-инструмента Fail2ban укрепить Nginx и защитить сервисы от взлома. А также продемонстрирую способы блокирования DDoS-атак.

Базовая защита в Nginx

Nginx — это не только быстрый и гибкий веб-сервер, но и довольно безопасный, если правильно его настроить. Рассмотрим пару примеров. 

Защита от Clickjacking. С помощью встроенного параметра X-Frame-Options в HTTP-ответе веб-сервера можно задать разрешение страницы в фреймы и iframe — и свести вероятность уязвимости Clickjacking к минимуму.

Защита от DDoS. Если правильно «откалибровать» размеры клиентского буфера, можно снизить влияние DDoS-атак. Также с помощью команд deny и allow можно настроить доступ к приложению с определенных IP-адресов и сетей.

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

В случае с Bruteforce эффективней настроить временную блокировку пользователя — либо на уровне логики приложения, либо на обратном прокси-сервере. Если используется базовая аутентификация Nginx, сделать это еще проще.

Для автоматизированной отработки атак лучше использовать специальный софт — в нашем случае это Fail2ban. Но для начала давайте посмотрим, что будет, если атаковать беззащитный сервер.

Атака незащищенного веб-сервера

В рамках статьи рассмотрим два вида атак, в которых промежуточным звеном будет Nginx, а целью хакера — некое веб-приложение.

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

Wazuh — это комплексная open source-платформа для обеспечения мониторинга безопасности. С помощью нее можно:

  • закрыть потребность в полноценном SIEM-решении,
  • обеспечить реагирование на инциденты,
  • собирать логи со множества систем и аналитику корреляции событий.

Благодаря встроенным шаблонам Wazuh позволяет быстро оценивать соответствие системы требованиям различных стандартов в области ИБ. Наиболее подробно установка и базовая настройка описаны в официальной документации.

Посмотрим, что будет, если атаковать Nginx-сервер без защиты Fail2ban.

Имитация DoS-атаки

Суть атаки сводится к тому, чтобы «забить» канал связи бессмысленным трафиком и исчерпать ресурсы сервера. При этом нелегитимные запросы должны отработать до такой степени, чтобы было невозможно обработать легитимные. В качестве такой атаки будем рассматривать флуд HTTP-запросами — для этого используем Siege. Он отлично подходит для стресс-тестов HTTP-приложений. 

Нагрузим сервер запросами в 100 конкурентных пользователей. 

root@kali:~# siege -t 1m -c 100 http://192.168.100.3/admin
** SIEGE 4.0.4
** Preparing 100 concurrent users for battle.
The server is now under siege...
Lifting the server siege...
Transactions:		      206090 hits
Availability:		      100.00 %
Elapsed time:		       59.83 secs
Data transferred:	       40.13 MB
Response time:		        0.03 secs
Transaction rate:	     3444.59 trans/sec
Throughput:		        0.67 MB/sec
Concurrency:		       99.05
Successful transactions:      206271
Failed transactions:	           0
Longest transaction:	        0.32
Shortest transaction:	        0.00

В мониторинге видим график, содержащий большое количество запросов.

График с запросами.

Непроизводительный веб-сервер на таком количестве запросов может ощутимо загрузить процессор. Запустив параллельно несколько источников запроса, можно и вовсе «положить» сервер.

Имитация Bruteforce

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

root@kali:~# curl http://192.168.100.3/admin
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.14.0 (Ubuntu)</center>
</body>
</html>

Аутентификация на странице 192.168.100.3/admin настроена. При обращении к ней обычный пользователь получает 401 ошибку. 

На хосте для атаки создадим списки с логинами и паролями соответственно. С помощью инструмента patator и модуля http_fuzz «забрутим» страницу с базовой аутентификацией Nginx. 

Результат — «всплески» в разделе Security Events мониторинга Wazuh. 

Раздел Security Events мониторинга Wazuh.

Для Bruteforce все пары логин-пароль были обработаны сервером Nginx, блокирования не произошло. Если бы хактевист потратил больше времени на перебор, сервис был бы взломан.

Последствия Bruteforce и DoS-атак могут быть критичными для реального проекта. Поэтому попробуем подключить и настроить Fail2ban — решение, которое может защитить веб-сервер от потенциальных простоев, утечек данных и другого.

Установка и базовая настройка Fail2ban

Fail2ban — это open source-приложение для фильтрации подключений. С помощью него можно создать специальную «клетку» jail, куда будут записываться вредоносные IP-адреса из iptables, и установить, например, сроки блокирования.

Установим и настроим пакет Fail2ban — он есть в стандартных репозиториях Ubuntu, поэтому можно воспользоваться менеджером пакетов. 

sudo apt install fail2ban -y

После выполнения команды в каталоге /etc/fail2ban/ появятся файлы конфигурации.

admin@nginx:~# ll /etc/fail2ban/
total 92
drwxr-xr-x  6 root root  4096 Dec 13 17:50 ./
drwxr-xr-x 86 root root  4096 Dec 11 18:10 ../
drwxr-xr-x  2 root root  4096 Nov 25 05:56 action.d/
-rw-r--r--  1 root root  2334 Jan 18  2018 fail2ban.conf
drwxr-xr-x  2 root root  4096 Apr  4  2018 fail2ban.d/
drwxr-xr-x  3 root root  4096 Dec 13 17:30 filter.d/
-rw-r--r--  1 root root 22959 Dec 11 19:11 jail.conf
drwxr-xr-x  2 root root  4096 Dec 11 19:03 jail.d/
-rw-r--r--  1 root root   645 Jan 18  2018 paths-arch.conf
-rw-r--r--  1 root root  2827 Jan 18  2018 paths-common.conf
-rw-r--r--  1 root root   573 Jan 18  2018 paths-debian.conf
-rw-r--r--  1 root root   738 Jan 18  2018 paths-opensuse.conf

Из этого списка нас интересуют следующие файлы:

  • /etc/fail2ban/jail.conf — содержит нативные параметры для поиска совпадений в логах.
  • /etc/fail2ban/filter.d/nginx-limit-req.conf — нужен для отслеживания количества обращений к странице и скорости ее обработки. Может пригодиться для блокирования DoS- и bruteforce-атак.
  • /etc/fail2ban/filter.d/nginx-http-auth.conf — нужен для обнаружения и блокирования bruteforce-атак на базовую аутентификацию Nginx.

По умолчанию в конфиге Fail2ban такие настройки рассматриваемых модулей:

[nginx-http-auth]

port    = http,https
logpath = %(nginx_error_log)s

[nginx-limit-req]

port    = http,https
logpath = %(nginx_error_log)s

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

Стенд после включения Fail2ban.
Стенд после включения Fail2ban.

Установка и базовая настройка — лишь первый этап в защите веб-приложения. Самое главное — правильно настроить правила блокировки IP-адресов для конкретных видов атак. 

Специальная настройка от DoS-атак

Подготовка nginx-limit-req. Попробуем защитить веб-сервер с помощью модуля nginx-limit-req. Он нужен для определения максимального всплеска запросов (burst) и пороговых значений разделяемой памяти (zone). В случае, если эти параметры будут превышены, модуль добавит в лог-файл новые записи. С помощью них Fail2ban определяет и блокирует вредоносные IP-адреса. 

Описания ключевых параметров из файлов конфигураций можно найти по ссылке.

Модуль nginx-limit-req входит в nginx-core, и дополнительная его установка не потребуется. В директиве http добавим параметры для включения модуля и определения зоны:

limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;

Далее в директиве location достаточно добавить значение для ограничения количества запросов:

limit_req zone=one burst=5;

Теперь при превышении количества запросов, который мы задали в burst, в /var/log/nginx/error.log будут сыпаться ошибки следующего вида:

2022/12/14 18:39:28 [error] 9462#9462: *987284 limiting requests, excess: 1.940 by zone "one", client: 192.168.100.1, server: localhost, request: "GET /admin HTTP/1.1", host: "192.168.100.3"

Теперь мы можем искать в логах шаблонные сообщения. Именно их Fail2ban будет «искать» в логе nginx и при достижении определенного количества выполнять действие — блокирование источника атаки.

Подготовка jail. В файле /etc/fail2ban/jail.local зададим следующие значения:

[nginx-limit-req]
port     = http,https
enabled  = true
logpath  = /var/log/nginx/*error.log
findtime = 10
bantime  = 30
maxretry = 5

В фильтрах Fail2ban /etc/fail2ban/filter.d/nginx-limit-req.conf нужно записать следующее регулярное выражение для параметра failregex:

[Definition]

ngx_limit_req_zones = [^"]+

failregex = ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>,

Регулярное выражение позволяет найти строку определенного формата в логах nginx. Как можем видеть, некоторые слова являются постоянными, а часть значений — переменными. Они заданы специальным синтаксисом регулярного выражения.

Результат. После включения Fail2ban и запуска атаки видим следующие записи:

admin@nginx:~# tail -n 5 /var/log/fail2ban.log
2022-12-14 18:30:39,505 fail2ban.filter         [9237]: INFO    [nginx-limit-req] Found 192.168.100.1 - 2022-12-14 18:30:39
2022-12-14 18:30:39,506 fail2ban.filter         [9237]: INFO    [nginx-limit-req] Found 192.168.100.1 - 2022-12-14 18:30:39
2022-12-14 18:30:39,508 fail2ban.filter         [9237]: INFO    [nginx-limit-req] Found 192.168.100.1 - 2022-12-14 18:30:39
2022-12-14 18:30:39,509 fail2ban.filter         [9237]: INFO    [nginx-limit-req] Found 192.168.100.1 - 2022-12-14 18:30:39
2022-12-14 18:30:39,960 fail2ban.actions        [9237]: NOTICE  [nginx-limit-req] Ban 192.168.100.1

Обратите внимание: вредоносный источник превысил параметры nginx-limit-req и попал в клетку jail. Атакующий хост получил сообщение о прекращении сессии.

root@kali:~# siege -t 1m http://192.168.100.3/admin

================================================================
WARNING: The number of users is capped at 255.  To increase this
         limit, search your .siegerc file for 'limit' and change
         its value. Make sure you read the instructions there...
================================================================
** SIEGE 4.0.4
** Preparing 255 concurrent users for battle.
The server is now under siege...ERROR from http_request
ERROR from http_request
ERROR from http_request
ERROR from http_request
[error] socket: unable to connect sock.c:249: Connection refused
ERROR from http_request
ERROR from http_request
ERROR from http_request
siege aborted due to excessive socket failure; you
can change the failure threshold in $HOME/.siegerc

Transactions:		           0 hits
Availability:		        0.00 %
Elapsed time:		        1.20 secs
Data transferred:	        0.27 MB
Response time:		        0.00 secs
Transaction rate:	        0.00 trans/sec
Throughput:		        0.23 MB/sec
Concurrency:		        6.71
Successful transactions:           7
Failed transactions:	        1278
Longest transaction:	        0.20
Shortest transaction:	        0.00

В Wazuh также виден «всплеск» обращений и их резкое прекращение — Fail2ban заблокировал нелегитимные запросы.

Блокировка нелегитимных запросов (всплеск обращений на графике).

Специальная настройка от Bruteforce

На хосте уже установлен Fail2ban. Создадим файл jail.local и добавим в него настройки для базовой аутентификации.

[nginx-http-auth]

enabled = true
port = http,https
logpath = /var/log/nginx/*error.log
findtime = 10
bantime = 30
maxretry = 3

Переводя на человеческий, можно сказать так:

  • после трех неудачных попыток авторизации будут блокироваться порты http (80/TCP) и https (443/TCP),
  • Fail2ban мониторит лог-файл /var/log/nginx/error.log,
  • поиск совпадений в лог-файле длится 10 секунд,
  • вредоносный адрес блокируется на 30 секунд,
  • Fail2ban блокирует адрес, если тот совпадает с шаблоном минимум 3 раза.

В файле /etc/fail2ban/filter.d/nginx-http-auth.conf оставили значение регулярного выражения по умолчанию:

admin@nginx:~# cat /etc/fail2ban/filter.d/nginx-http-auth.conf 
# fail2ban filter configuration for nginx
[Definition]
failregex = ^ \[error\] \d+#\d+: \*\d+ user "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$

При необходимости в этот файл можно добавлять свои регулярные выражения. Их Fail2ban также будет использовать для поиска совпадений.

Результат. Запустим тот же брутфорс, что и в начале статьи.

Alert groups evolution.

Первый пик — действие Bruteforce до включения защиты: Nginx обработал весь список пар из логинов паролей. Отсутствие данных на определенных промежутках обусловлено 30-секундной блокировкой вредоносных IP-адресов. По истечении времени часть адресов продолжает «брутфорсить» страницу авторизации. Но все пары из логинов паролей не успевают обрабатываться — отсюда и «затухающие» пики.

В логах Fail2ban записано следующее:

admin@nginx:~# tail -n 5 /var/log/fail2ban.log
2022-12-13 17:34:58,340 fail2ban.filter         [21234]: INFO    [nginx-http-auth] Found 192.168.100.1 - 2022-12-13 17:34:58
2022-12-13 17:34:58,340 fail2ban.filter         [21234]: INFO    [nginx-http-auth] Found 192.168.100.1 - 2022-12-13 17:34:58
2022-12-13 17:34:58,340 fail2ban.filter         [21234]: INFO    [nginx-http-auth] Found 192.168.100.1 - 2022-12-13 17:34:58
2022-12-13 17:34:58,341 fail2ban.filter         [21234]: INFO    [nginx-http-auth] Found 192.168.100.1 - 2022-12-13 17:34:58
2022-12-13 17:34:58,471 fail2ban.actions        [21234]: NOTICE  [nginx-http-auth] Ban 192.168.100.1

Обратите внимание: запросы авторизации регистрировались одновременно, и Fail2ban даже не успевал учитывать некоторые из них. Но в результате все равно находил совпадения и блокировал вредоносные адреса. Напомню, действие происходило в рамках одного L2-домена.

Fail2ban блокировал источники путем добавления адресов в цепочку Chain f2b-nginx-http-auth, которая в свою очередь добавлена в INPUT:

admin@nginx:~# sudo iptables -L -v
Chain INPUT (policy ACCEPT 2312 packets, 123K bytes)
 pkts bytes target     prot opt in     out     source               destination         
 <b>3517  492K f2b-nginx-http-auth  tcp  --  any    any     anywhere             anywhere             multiport dports http,https</b>
 2521  178K f2b-sshd   tcp  --  any    any     anywhere             anywhere             multiport dports ssh

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 2294 packets, 604K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain f2b-nginx-http-auth (1 references)
 pkts bytes target     prot opt in     out     source               destination         
  <b>172 10320 REJECT     all  --  any    any     192.168.100.1        anywhere             reject-with icmp-port-unreachable</b>
 2594  437K RETURN     all  --  any    any     anywhere             anywhere

Обратите внимание: Fail2ban автоматически описал правила iptables для блокирования исходящего трафика от атакующего IP.

А на стороне атакующего получали следующие результаты атаки Bruteforce:

17:54:38 patator    INFO - code size:clen       time | candidate                          |   num | mesg
17:54:38 patator    INFO - -----------------------------------------------------------------------------
17:54:58 patator    FAIL - xxx  92:-1          0.000 | admin:123123                       |     2 | Unexpected HTTP response
17:55:00 patator    FAIL - xxx  92:-1          0.000 | admin:12345                        |     4 | Unexpected HTTP response

Это как следствие того, что сервер отклонял обращения на порт 80/TCP с адреса атакующего хоста:

root@kali:~# curl http://192.168.100.3/admin
curl: (7) Failed to connect to 192.168.100.3 port 80: Connection refused

Таким образом можно настраивать защиту от брута как на все ресурсы, так и на отдельные страницы с разными условиями бана.