Сайт за Cloudflare — частая ловушка для fail2ban. Посетители приходят на сервер не со своих адресов, а с адресов узлов Cloudflare, и если веб-сервер записывает в журнал именно их, jail банит Cloudflare. Вместе с узлом в бан уходят все посетители, которые шли через него. Разберём, как это заметить, как записывать в журнал настоящий IP и почему даже после этого бан на брандмауэре работает не так, как вы ожидаете.
Как выглядит проблема
Без настройки nginx записывает в access.log адрес, с которого пришло TCP-соединение. Для сайта за прокси Cloudflare это всегда узел Cloudflare:
172.70.42.17 - - [09/Oct/2026:03:12:44 +0300] "POST /wp-login.php HTTP/1.1" 200 ...
172.70.42.17 - - [09/Oct/2026:03:12:45 +0300] "GET / HTTP/1.1" 200 ...
162.158.90.204 - - [09/Oct/2026:03:12:45 +0300] "GET /catalog/ HTTP/1.1" 200 ...
Первая строка — перебор пароля WordPress, вторая и третья — обычные посетители. Для fail2ban это один и тот же «атакующий» 172.70.42.17. Jail для wp-login.php с порогом 5 забанит его через минуту, и часть посетителей сайта начнёт получать ошибку 522.
Это не теоретический случай. На одном из серверов, где работает анализ журналов KIPBan, 37 из 58 «атакующих» по шаблону WordPress оказались адресами Cloudflare. Если бы на этом журнале был включён jail без настройки реального IP, он банил бы Cloudflare.
Шаг 1. Записывать в журнал реальный IP
Cloudflare передаёт адрес посетителя в заголовке CF-Connecting-IP. Веб-серверу нужно сказать, что этому заголовку можно верить, но только если запрос пришёл с адреса Cloudflare. Иначе любой сможет подставить в заголовок чужой адрес.
nginx
Модуль ngx_http_realip_module есть в сборках nginx из репозиториев Debian, Ubuntu и RHEL. Список диапазонов Cloudflare публикуется на cloudflare.com/ips и иногда меняется, поэтому конфигурацию удобно генерировать скриптом:
#!/bin/sh
# /usr/local/sbin/cloudflare-realip: обновить список узлов Cloudflare для nginx
set -e
CONF=/etc/nginx/conf.d/cloudflare-realip.conf
TMP=$(mktemp)
{
for ip in $(curl -fsS https://www.cloudflare.com/ips-v4) $(curl -fsS https://www.cloudflare.com/ips-v6); do
echo "set_real_ip_from $ip;"
done
echo "real_ip_header CF-Connecting-IP;"
} > "$TMP"
grep -q set_real_ip_from "$TMP"
[ -f "$CONF" ] && cp "$CONF" "$CONF.bak"
mv "$TMP" "$CONF" && chmod 644 "$CONF"
if nginx -t 2>/dev/null; then
systemctl reload nginx
else
[ -f "$CONF.bak" ] && mv "$CONF.bak" "$CONF"
exit 1
fi
Запускайте его раз в неделю по cron. После перезагрузки nginx в журнале появятся настоящие адреса посетителей, а $remote_addr станет адресом посетителя для всех директив, включая allow и deny.
Apache
В Apache то же делает mod_remoteip:
a2enmod remoteip
# /etc/apache2/conf-available/cloudflare-remoteip.conf
RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxy 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22
# ... остальные диапазоны с cloudflare.com/ips
И в LogFormat замените %h на %a: именно %a выводит адрес, который подставил mod_remoteip.
Шаг 2. Понять, что блокирует бан на брандмауэре
Здесь начинается то, о чём обычно не пишут. После настройки реального IP fail2ban банит правильный адрес, но блокирует его через iptables или nftables, то есть по адресу TCP-соединения. А соединения к сайту за прокси по-прежнему приходят с узлов Cloudflare. Забаненный посетитель или бот продолжает ходить на сайт через Cloudflare как ни в чём не бывало.
Бан на брандмауэре в такой схеме полезен только для запросов напрямую на сервер, в обход Cloudflare. Чтобы остановить запросы, которые идут через Cloudflare, нужна блокировка на уровне HTTP:
- Правило в самом Cloudflare. В поставке fail2ban есть действия
cloudflareиcloudflare-token, которые добавляют забаненный адрес в правила доступа Cloudflare через API. - Отказ в nginx по реальному IP. После настройки realip директива
denyработает по адресу посетителя. Список адресов можно подключать файлом черезinclude.
И закройте прямой доступ: если на портах 80 и 443 сервер принимает соединения только с адресов Cloudflare, обходить прокси станет нельзя, а бан на брандмауэре перестанет быть нужен для веба вообще.
Если реальный IP настроить нельзя
Бывает, что сайт обслуживает чужая панель или прокси, и журнал поменять не получится. Тогда хотя бы не давайте fail2ban банить узлы Cloudflare: добавьте их диапазоны в ignoreip. Защиты от атак через Cloudflare это не даст, но и сайт не сломает.
Как с этим работает KIPBan
- Анализ журналов предупреждает. Если сайт за Cloudflare и в журнале адреса Cloudflare вместо посетителей, отчёт об этом говорит, а у каждого шаблона видно, сколько найденных адресов принадлежат Cloudflare.
- Галочка «Никогда не банить IP Cloudflare» в настройках фермы, группы или сервера. Такой сервер не банит адреса Cloudflare сам и не получает их из общей базы. Включайте её для серверов с сайтами за Cloudflare, рядом с настройкой реального IP, а не вместо неё. На серверах без таких сайтов она не нужна: с адресов Cloudflare ходят и настоящие сканеры, например Cloudflare Workers.
- Блок-лист без Cloudflare. Из блок-листа для периметра адреса Cloudflare исключены. Его формат
nginx.conf— готовый списокdeny, который после настройки realip блокирует атакующих и тогда, когда они идут через Cloudflare.
Подробнее о том, что анализ журналов находит на обычных серверах, читайте в статье «Что fail2ban на вашем сервере не видит».
Вопросы
Почему после бана fail2ban посетители сайта получают ошибку 522?
Скорее всего, в журнале веб-сервера записаны адреса узлов Cloudflare, а не посетителей, и fail2ban забанил узел. Все посетители, которые идут через этот узел, перестают достучаться до сервера. Настройте реальный IP через CF-Connecting-IP и снимите бан с адреса Cloudflare.
Блокирует ли iptables посетителя за Cloudflare, если в журнале его реальный IP?
Нет. Соединение к серверу всё равно приходит с адреса Cloudflare, поэтому правило iptables для адреса посетителя не срабатывает. Нужна блокировка на уровне HTTP: правило в Cloudflare или deny в nginx после настройки realip.
Где взять актуальный список адресов Cloudflare?
На cloudflare.com/ips, в текстовом виде по адресам cloudflare.com/ips-v4 и cloudflare.com/ips-v6. Список иногда меняется, поэтому конфигурацию веб-сервера лучше обновлять скриптом по расписанию.