Блог компании 3v-Hosting

Настройка защиты Fail2ban для сайта за CloudFlare

Администрирование

8 мин.


Многие владельцы сайтов вздыхают с облегчением, как только включают Cloudflare. Такие функции, как фильтрация трафика, защита от DDoS-атак и пограничное кэширование, делают инфраструктуру любого проекта более устойчивой. Однако есть один тонкий нюанс, который заключается в том, что CloudFlare скрывает реальные IP-адреса посетителей вашего сайта, в том числе и злоумышленников. В итоге, когда ваш сервер видит только IP-адреса CloudFlare, то такие инструменты, как например Fail2ban, не могут блокировать определенных клиентов по IP адресу.

Представьте, что ваш сайт на WordPress подвергается брутфорс-атакам на страницу /wp-login.php. Cloudflare проксирует все эти запросы, но ваш конечный сервер видит только несколько IP-адресов, которые принадлежат узлам Cloudflare, которые делают десятки запросов. И если в таком случае Fail2ban блокирует их, то он случайно блокирует и весь легитимный трафик. Это как закрыть всю станцию метро из-за того, что туда проник один карманник - по факту не самая умная стратегия:)

Тогда возникает правомерный вопрос: Так как-же сделать Fail2ban снова полезным, когда сайт находится за CloudFlare? Об этом и поговорим в данной статье.

 

 

 

 

Как Fail2ban работает без CloudFlare и с ним

 

Fail2ban - это такой сторожевой механизм, который проверяет логи вашего сервера и отыскивает в них подозрительные паттерны поведения пользователей, такие как неудачные попытки входа по SSH, чрезмерное количество запросов к какой либо странице и повторяющиеся ошибки в логах доступа Nginx. Если количество таких нарушений с какого либо IP адреса превышает определенный предел, то Fail2ban добавляет нарушивший правила IP-адрес в правила брандмауэра на вашем сервере.

Например, если кто-то совершает 20 неудачных попыток входа по SSH на ваш сервер в течение одной минуты, то Fail2ban вставляет правило в iptables, чтобы заблокировать соединения с этого IP-адреса.

Однако, если вы используете CloudFlare, то сервер не может видеть реальный IP адрес посетителя, а только его "маску" в виде IP-адреса самого CloudFlare. То есть каждый запрос к вашему серверу поступает из инфраструктуры CloudFlare. Вместо компьютера злоумышленника (203.0.113.45) в журналах вы видите дата-центр CloudFlare (172.71.150.2).

Если Fail2ban заблокирует этот адрес, он заблокирует сам CloudFlare, отрезав тысячи невинных пользователей вместе с злоумышленником.

Решением является настройка вашего веб-сервера и Fail2ban для идентификации реального IP-адреса клиента и его блокировки. Здесь в игру вступают HTTP-заголовки и немного магии настройки.

 

 

 

 

Настройка веб-сервера для установление реального IP-адреса

 

В данном примере мы будем приводить все настройки применительно к веб-серверу Nginx, но если вы используете другой веб-сервер, например Apache, то в нём подобный функционал также можно легко реализовать.

Итак, CloudFlare добавляет к каждому запросу, который он проксирует на ваш сервер, заголовок X-Forwarded-For или CF-Connecting-IP. Этот заголовок содержит фактический адрес клиента, который вы можете увидеть, настроив веб-сервер так, чтобы он доверял этому заголовку.

 

Пример настройки Nginx

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

real_ip_header CF-Connecting-IP;
​
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
​
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
​
real_ip_recursive on;
​

Как вы видите, в файле мы указали все публичные IPv4 и IPv6 адреса самого Cloudflare. Теперь ваш сервер, видя, что запрос пришёл с одного из этих IP адресов, будет проверять заголовок X-Forwarded-For, беря из него информацию о реальном IP-адресе посетителя. Таким образом в логах веб-сервера будет отображаться реальный IP-адрес посетителя. Для точности всегда используйте полный список подсетей CloudFlare, доступный на странице документации.

И тут, казалось-бы, бери да блокируй эти адреса в случае нарушений правил. Но если бы всё было так просто, мы бы эту статьи не писали. Давайте обсудим чуть глубже, как работает Cloudflare.

 

 

 

 

Как работает Cloudflare под капотом

Когда вы настроили свой веб-сервер для получения реальных IP-адресов посетителей из заголовков X-Forwarded-For, веб-сервер читает эти заголовки и в лог складывает эти реальные IP адреса, по каждому запросу. Но сам трафик фактически продолжает приходить на ваш сервер всё под теми же IP адресами Cloudflare.

То есть представьте, что вы получили письмо от Шарлиз Терон на свою электронную почту, открыли его, а в тексте самого письма написано "отправитель Джим Керри". Да, вы теперь знаете, что письмо переслали и что его фактическим составителем является Джим, но получили то вы его от Шарлиз, с его почтового адреса. И если вы заблокируете адрес Джима, то с адреса Шарлиз вам продолжат поступать шутливые письма :)

Простите за такой яркий пример, но суть, я думаю, вы уловили.

 

Что же делать в таком случае?

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

 

Вы спросите, как же это сделать?

Сделать это оказывается достаточно просто, хотя и придётся слегка повозиться. Но сам механизм существует и он достаточно хорошо работает. Для этого нам понадобится связка вашего обычно Fail2ban с API самого Cloudflare.

 

 

 

Настройка Cloudflare

Когда доходишь до идеи переноса блокировки на сторону Cloudflare, то возникает логичный вопрос, как именно блокировать? В Cloudflare для этого есть как минимум два варианта:

Первый - это так называемые IP Access Rules. По сути, это простые правила вида "заблокировать этот IP". Они создаются по одному, вручную или через API, и работают на уровне аккаунта или зоны.

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

Представь типичную ситуацию, когда боты начинают сканировать ваш сайт, в результате чего Fail2ban за сутки ловит несколько сотен или тысяч адресов. Если для каждого из них создавать отдельное правило, то через неделю у тебя в Cloudflare будут уже тысячи записей. Интерфейс, в таком случае, превращается в свалку, управлять этим становится неудобно и сложно, а любые массовые операции, например банальная очистка, становится отдельной болью.

Кроме того, этот метод довольно плохо масштабируется, ведь Fail2ban - инструмент автоматический и он может добавлять десятки или сотни IP в час. А модель "одно правило - один IP" для таких сценариев просто не предназначена.

 

Именно поэтому гораздо более правильный путь - это использование Custom Lists в сочетании с одним WAF-правилом (Web Application Firewall).

Смысл здесь очень простой. Ты создаёшь один список IP-адресов, например fail2ban-blocklist, а дальше создаёшь лишь одно правило в WAF, которое говорит:

если IP клиента находится в этом списке - блокируй запрос от него.

 

В итоге у тебя в интерфейсе всего один список, в который добавляются IP и одно правило, которое на него ссылается. А Fail2ban уже просто управляет содержимым этого списка. И такой подход даёт сразу несколько практических преимуществ.

Во-первых, это банально выглядит чище. У тебя нет тысячи разрозненных правил, которые заменяются одним аккуратным списком.

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

В-третьих, этот метод легко масштабируется, так как добавление нового IP - это просто запись в список, а не создание нового объекта в системе правил.

Ну и, наконец, сам Cloudflare рекомендует использовать свой WAF, а им-то виднее как лучше :)

 

 

 

 

Как это реализуется на практике

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

Начинается всё с создания API-токена, через который Fail2ban будет общаться с Cloudflare.

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

  • доступ к управлению WAF (Firewall Services);
  • доступ к спискам (Lists).

Этот токен будет лежать на сервере, и его не стоит делать всемогущим, на всякий случай.

 

После этого создаётся сам список.

В интерфейсе Cloudflare это делается в разделе WAF → Tools → Lists. Нужно создать новый список IP-адресов и дать ему понятное имя, например:

fail2ban-blocklist

На этом этапе список пока пустой и это абсолютно нормально, ведь заполнять его будет Fail2ban, когда мы его настроим.

 

Дальше создаётся ключевой элемент всей нашей схемы - WAF правило. Это правило максимально простое по своей логике:

ip.src in $fail2ban-blocklist

Поле Action: Block

 

То есть оно гласит, что если IP источника запроса находится в нашем списке, тогда применить к нему действие Block.

Именно это правило связывает всё воедино и без него список сам по себе ничего не делает.

Всё, на этом настройка Cloudflare закончена и мы можем переходить к настройке Fail2ban.

 

 

 

 

Настройка Fail2ban

После того как мы перенесли точку блокировки на сторону Cloudflare, сам Fail2ban никуда не исчезает, он остаётся центральным элементом всей схемы. Просто его роль становится чуть другой. Теперь он не блокирует трафик напрямую, а только принимает решение о том, кто является нарушителем, после чего передаёт это решение дальше - в Cloudflare.

Приступим.

 

Базовая настройка jail

Здесь, что важно, ничего принципиально нового не появляется. Мы по прежнему всё так же описываем:

  • что считаем нарушением;
  • сколько попыток допускаем;
  • за какой период;
  • и на какой срок нужно банить.

 

Например, защита стандартной страницы логина WordPress может выглядеть приблизительно так:

[wordpress-login]
enabled  = true
port     = http,https
filter   = wordpress-login
logpath  = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime  = 3600
ignoreip = 127.0.0.1/8 YOUR_IP YOUR_SUBNET

 

Здесь мы описали, что если за 10 минут было больше 5 неудачных попыток логина, то мы считаем IP подозрительным. Итак, на этом этапе мы научили Fail2ban находить злоумышленников.

ВАЖНО! В последней строке мы указали параметр ignoreip, который обязательно рекомендуем вам использовать, чтобы ненароком не заблокировать себя самого.

 

 

Настройка action

Вот здесь начинается отличие от классической схемы. Напомним, что по умолчанию Fail2ban при бане добавляет блокирующее правило в iptables. Но в нашей архитектуре это бессмысленно и выше мы уже разобрали почему.

Поэтому мы заменяем стандартный action на свой, который будет работать через API Cloudflare.

Проще всего создать отдельный action-файл, например /etc/fail2ban/action.d/cloudflare.conf, внутри которого мы опишем то, что делать при блокировке и при разблокировке пользователя. Соответственно при блокировке мы добавляем IP в список Cloudflare, а при разблокировке - удаляем IP из этого списка.

Простейший пример может выглядеть так:

[Definition]
​
# API
_cf_account_id = {{ YOUR ACCOUNT ID }}
_cf_list_id = {{ YOUR WAF LIST ID }}
_cf_api_prms = -H "Authorization: Bearer {{ YOUR TOKEN }}" -H "Content-Type: application/json"
​
# Actions
actionstart =
actionstop =
actioncheck =
​
# BAN → добавляем IP в list
actionban = curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/<_cf_account_id>/rules/lists/<_cf_list_id>/items" \
  <_cf_api_prms> \
  --data '[{"ip":"<ip>","comment":"fail2ban"}]'
​
# UNBAN → удаляем IP из list
actionunban = ITEM_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/<_cf_account_id>/rules/lists/<_cf_list_id>/items" \
  <_cf_api_prms> | jq -r '.result[] | select(.ip=="<ip>") | .id'); \
  [ -n "$ITEM_ID" ] && curl -s -X DELETE "https://api.cloudflare.com/client/v4/accounts/<_cf_account_id>/rules/lists/<_cf_list_id>/items" \
  <_cf_api_prms> \
  --data "{\"items\":[{\"id\":\"$ITEM_ID\"}]}"

 

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

На практике такие action’ы часто дорабатывают, например добавляя проверки успешности ответов API, логирование ошибок и т.п. Но суть от этого не меняется - Fail2ban превращается в клиента Cloudflare API.

 

 

 

Подключение action

Помните наш jail, который мы создали выше? Так вот после того как action описан, его нужно подключить к нашему jail. А делается это одной строкой:

action = cloudflare

 

То есть наш Jail стал выглядеть так:

[wordpress-login]
enabled  = true
port     = http,https
filter   = wordpress-login
logpath  = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime  = 3600
ignoreip = 127.0.0.1/8 YOUR_IP YOUR_SUBNET
action = cloudflare

 

Перезагружаем Fail2ban с помощью команды

systemctl restart fail2ban

и с этого момента Fail2ban перестаёт трогать локальный firewall и начинает работать через Cloudflare.

 

 

 

Проверка работы

Как после выполнения любой работы, работу нужно проверить.

Сначала протестируем принудительную блокировку. Наберите в консоли сервера команду:

fail2ban-client set wordpress-login banip 1.2.3.4

 

После этого нужно убедиться в трёх вещах:

  • IP 1.2.3.4 появился в custom list в Cloudflare;
  • правило WAF его действительно блокирует;
  • запросы с этого IP перестали доходить до сервера (да, да, можете подставить любой тестовый IP)

 

Затем обязательно тестируем разблокировку:

fail2ban-client set wordpress-login unbanip 1.2.3.4

 

И проверяем, что IP удален из листа, а доступ для него вернулся.

Только после этого можно считать, что схема реально работает.

 

 

 

Важно!

И последнее, что важно зафиксировать, чтобы не было путаницы. Вся эта конструкция нужна именно для HTTP/HTTPS-трафика, который идёт через Cloudflare.

Для других сервисов, таких, например, как SSH - ничего не меняется. Ведь в их случае:

  • соединение идёт напрямую на IP адрес вашего сервера;
  • Fail2ban работает по обычной схеме, через iptables;
  • блокировка происходит локально на вашем сервере.

 

Поэтому не нужно пытаться "натянуть Cloudflare" на всё подряд.

 

 

 

 

Подытожим, как это начинает работать

После того как все настройки были произведены, а сервис Fail2ban был перезагружен, наша схема начинает полноценную работу. Кратко резюмируем её ещё раз:

  • Fail2ban находит нарушителя в логах вашего веб-сервера;
  • вместо добавления правила в локальный iptables он делает HTTP-запрос в API Cloudflare;
  • добавляет IP нарушителя в fail2ban-blocklist;
  • Cloudflare тут же начинает блокировать этот IP.

При разблокировке происходит всё то же самое, только в обратную сторону.

 

 

 

 

 

SEO и перспектива цифрового бизнеса

 

С точки зрения бизнеса, неспособность защитить ваш сайт от атак методом перебора - это не только повышение нагрузки на процессор вашего сервера. Постоянные попытки генерируют ошибки, раздувают журналы и могут замедлять работу уровней кэширования. Время отклика сервера имеет решающее значение для SEO, так как Googlebot не будет ждать вечно и может просто прекратить обход вашего сайта. Более того, если сервер перегружен запросами злоумышленников и страницы вашего сайта открываются медленно и с задержками, то ваш сайт вовсе может попасть под пессимизацию в выдаче, как не прошедший тесты PageSpeed Insights.

Например, владелец одного интернет-магазина заметил, что боты атаковали /wp-login.php сотни раз в минуту. Страницы начали загружаться медленно, и клиенты бросали свои корзины. После настройки Fail2ban с помощью заголовков Cloudflare нагрузка на сервер нормализовалась, и коэффициент конверсии восстановился.

В этом случае безопасность напрямую влияет на продажи.

 

 

 

 

FAQ

Почему Fail2ban не блокирует IP, хотя в логах всё видно правильно? Потому что лог и сеть - это разные уровни. После настройки real IP nginx показывает реальный адрес клиента, но соединение до сервера всё равно приходит от Cloudflare. Fail2ban передаёт IP в firewall, а тот просто не видит такого источника. В результате бан есть, но на трафик он не влияет.

 

Можно ли всё-таки использовать iptables вместе с Cloudflare? Можно, но не для HTTP/HTTPS. Локальный firewall остаётся актуальным для SSH и других сервисов, которые идут напрямую. Просто не стоит ожидать от него блокировки клиентов, которые приходят через Cloudflare.

 

Зачем вообще нужен Fail2ban, если есть Cloudflare WAF? Cloudflare не знает контекста вашего приложения. Он не понимает, что для вас является подозрительным поведением, например перебор пароля в админке. Fail2ban как раз это и делает, а Cloudflare уже применяет блокировку.

 

Почему нельзя просто создавать IP Access Rules вместо списков? Можно, но это быстро превращается в хаос. При активной атаке появятся сотни правил, с которыми неудобно работать. Список и одно правило дают ту же функциональность, но без лишнего мусора.

 

Что будет, если случайно заблокировать свой IP? Вы просто перестанете попадать на сайт, потому что блокировка произойдёт на стороне Cloudflare. Поэтому лучше сразу добавить свои IP в исключения, чтобы не ловить такие ситуации в самый неподходящий момент.

 

Насколько безопасно использовать API token Cloudflare на сервере? Это нормально, если дать ему только необходимые права и не хранить его где попало. По сути это доступ к управлению блокировками, поэтому относиться к нему нужно аккуратно и при малейших сомнениях перевыпускать.

 

Подойдёт ли это только для WordPress или для любого сайта? Это универсальный подход. WordPress просто удобный пример, но схема работает для любого сайта, где есть логи HTTP-запросов и можно описать, что считать подозрительным поведением.

 

 

 

 

Выводы

Когда начинаешь настраивать Fail2ban за Cloudflare, кажется, что достаточно просто "вернуть" реальные IP в логи и всё снова будет работать как раньше. Формально это правда, определение злоумышленника начинает работать корректно. Но на практике этого недостаточно, потому что меняется сама точка, где нужно применять блокировку.

Главное, к чему приходишь на основе реального опыта, что в архитектуре с reverse proxy нельзя слепо переносить старые подходы. Если трафик проходит через Cloudflare, то и управлять им нужно там же. Попытка блокировать клиентов локально через iptables в этом случае даёт только иллюзию защиты.

Правильная схема оказывается довольно простой и логичной: Fail2ban остаётся инструментом анализа и принятия решения, а Cloudflare тем местом, где это решение реализуется. Как только разделяешь эти роли, как сразу исчезают все странные эффекты вроде того, что блокировка есть, а атака продолжается.

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

 

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

3v-Hosting Team

Автор

3v-Hosting Team

Команда 3v-Hosting - это группа преданных своему делу инженеров и операторов, которые занимаются созданием и поддержкой основы наших сервисов. Каждый день мы погружаемся в мир виртуальных и выделенных серверов, занимаясь всем, от развертывания и мониторинга до устранения реальных проблем, возникающих в производственных средах. Большинство наших статей основано на практическом опыте, а не просто на теории. Мы делимся своими наблюдениями о проблемах, с которыми сталкиваемся: перебоях в производительности, ошибках в настройке, тонкостях сетевых решений и архитектурных выборах, влияющих на стабильность и надежность. Наша миссия проста - мы хотим делиться знаниями, которые позволят вам управлять своими проектами с меньшим количеством неожиданностей и гораздо большей предсказуемостью.

Установка Nginx на AlmaLinux 9
Установка Nginx на AlmaLinux 9

Установка и настройка Nginx на AlmaLinux 9: команды, запуск, firewall, конфиги, virtual host и частые ошибки. Пошаговая инструкция для быстрого старта и стабиль...

8 мин
Основы UFW или как настроить файервол в Linux
Основы UFW или как настроить файервол в Linux

Что такое UFW, его особенности и преимущества. Разбираем базовую настройку файервола в Linux: логика правил, открытие портов, управление доступом и типичные оши...

13 мин