На карте атак плотное пятно над одной страной, и напрашивается правило «запретить её целиком». Оно почти ничего не даст: сменить страну выхода стоит нападающему несколько минут, а под запрет попадут ваши же посетители и вы сами в поездке. Хуже того, видимый обходчик из одного облака после блокировки переедет на жилые адреса и станет неотличим от людей.
Ниже — пять шагов, которые на том же сервере дают результат: отсечь сканеры, ограничить темп, не отрезать нужное и, если всё-таки нужно, заблокировать правильно. Всё проверяется и настраивается за один заход.
Шаг 1. Посмотреть, кто именно создаёт пятно
Сначала найдите журнал запросов. На сервере с несколькими сайтами access.log нередко пуст, а записи идут в журнал виртуального хоста:
sudo ls -lS /var/log/apache2/*access*.log /var/log/apache2/*requests*.log 2>/dev/null
Дальше во всех командах подставляется тот файл, который оказался наверху списка. Сначала адреса-лидеры за сутки, потом — что именно просит тот, кто вверху:
sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
sudo grep '^203\.0\.113\.' /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
Вторая команда отвечает на главный вопрос. Если в выводе десятки разных страниц вашего сайта — это сборщик содержимого, он безвреден и его достаточно не считать посетителем. Если там /wp-login.php, /.env, /phpmyadmin и прочее, чего у вас нет, — это сканер, и с ним работает шаг 2. Для nginx журналы лежат в /var/log/nginx/, поля те же.
Шаг 2. Джейл на запросы к тому, чего у вас нет
Путь запроса подделать нельзя — он и есть намерение, в отличие от адреса и User-Agent. Фильтр /etc/fail2ban/filter.d/web-probes.local:
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) /(wp-login\.php|wp-admin|xmlrpc\.php|\.env|\.git/|phpmyadmin|administrator/)
ignoreregex =
Джейл /etc/fail2ban/jail.d/web-probes.local:
[web-probes]
enabled = true
port = http,https
filter = web-probes
logpath = /var/log/apache2/*access.log
maxretry = 1
findtime = 1h
bantime = 1d
Здесь maxretry = 1 оправдан: случайных обращений к чужой админке не бывает. Применить и убедиться, что фильтр ловит записи в вашем журнале:
sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/web-probes.local
sudo systemctl reload fail2ban && sudo fail2ban-client status web-probes
Шаг 3. Ограничить темп
Сканер и обходчик выдают себя скоростью, независимо от того, из какой они страны. Тридцать страниц в минуту с одного адреса — порог, который не задевает человека, листающего каталог:
[http-flood]
enabled = true
port = http,https
filter = apache-overflows
logpath = /var/log/apache2/site_access.log
maxretry = 30
findtime = 60
bantime = 1h
Ключевая деталь — какой журнал указан в logpath. Если под маску попадёт журнал самой панели управления или админки, вы забаните себя: одна её страница тянет десяток запросов к стилям и скриптам, и тридцать набираются за пару кликов. Поэтому панели дают отдельный файл журнала, который не совпадает с маской джейла, а статике отдают долгий срок кэширования, чтобы браузер не перезапрашивал её при каждом переходе. Симптом, если это всё-таки случилось: HTML приходит, оформление нет, страница висит. Снять бан:
sudo fail2ban-client set http-flood unbanip 203.0.113.10
Шаг 4. Защитить то, что нельзя отрезать
Прежде чем включать блокировки, впишите исключения — иначе первым под них попадёт нужное:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24 66.249.64.0/19
Что обязательно должно быть в этой строке и почему:
- Ваш собственный адрес или подсеть офиса. Самая частая причина потери доступа к серверу — собственное правило, а не атака;
- Диапазон
66.249.64.0/19. Поисковый робот Google опознаётся по User-Agent, но та его часть, которая исполняет JavaScript, ходит с обычнымChrome/Linuxи себя не называет. Любое правило вида «блокировать трафик из дата-центров» задевает именно её, после чего страницы тихо выпадают из индекса. Проверить подозрительный адрес можно обратным запросом:dig -x 66.249.66.1 +shortдолжен вернуть имя в зонеgooglebot.com; - Адреса платёжных уведомлений. Вебхуки платёжных систем приходят с серверов AWS и Azure. Если вы закроете эти облака целиком, подтверждения оплат перестанут доходить, и узнаете вы об этом тогда, когда клиент напишет, что деньги списаны, а доступа нет. Диапазоны берутся из документации вашего провайдера платежей.
Шаг 5. Если блок всё-таки нужен — блокируйте подсеть, а не страну
Устойчивое сгущение из одной сети, которое держится днями и сопровождается однотипными запросами, — это один исполнитель, идущий по списку. Такой случай блокируется точечно и на время:
sudo ufw deny from 198.51.100.0/22 to any
sudo ufw status numbered
Второй командой запомните номер правила: снимать блок придётся им же, sudo ufw delete <номер>. Договоритесь с собой о сроке — например, месяц, — и внесите снятие в календарь. Правила, поставленные «навсегда», через год превращаются в список, который никто не решается тронуть, потому что никто не помнит, за что эти адреса сюда попали.
Узнать, какую подсеть блокировать, можно по адресу из первого шага: whois 198.51.100.17 | grep -iE 'route|cidr|netname'.
Что меняется после этих пяти шагов
Сканеры отсекаются по намерению, а не по происхождению, и ловятся с первого запроса. Обходчики упираются в лимит темпа. Поисковый робот и платёжные уведомления продолжают проходить. Единственная блокировка, которая осталась широкой, — точечная, с известным сроком и понятной причиной.
Карта при этом никуда не девается, но её роль меняется: она нужна не для того, чтобы закрывать страны, а чтобы заметить само сгущение и сравнить его с тем, что было неделю назад. Для этого нужна история, а не снимок текущих суток. Как выглядит такая карта вместе с журналом банов и списком открытых портов — на страницах демо ниже.