На карті атак щільна пляма над однією країною, і напрошується правило «заборонити її цілком». Воно майже нічого не дасть: змінити країну виходу коштує нападникові кілька хвилин, а під заборону потраплять ваші ж відвідувачі і ви самі в поїздці. Гірше того, видимий обхідник з однієї хмари після блокування переїде на житлові адреси і стане невідрізнимим від людей.
Нижче — п’ять кроків, які на тому самому сервері дають результат: відсікти сканери, обмежити темп, не відрізати потрібне і, якщо все-таки потрібно, заблокувати правильно. Усе перевіряється й налаштовується за один захід.
Крок 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'.
Що змінюється після цих п’яти кроків
Сканери відсікаються за наміром, а не за походженням, і ловляться з першого запиту. Обхідники впираються в обмеження темпу. Пошуковий робот і платіжні повідомлення продовжують проходити. Єдине блокування, яке залишилося широким, — точкове, з відомим терміном і зрозумілою причиною.
Карта при цьому нікуди не зникає, але її роль змінюється: вона потрібна не для того, щоб закривати країни, а щоб помітити саме згущення і порівняти його з тим, що було тиждень тому. Для цього потрібна історія, а не знімок поточної доби. Як виглядає така карта разом із журналом банів і списком відкритих портів — на сторінках демо нижче.