Как стать автором
Обновить

Как мы боролись с DDoS-атакой (и победили)

Уровень сложностиСредний

✨ Предисловие

Кажется, у нас появился настоящий «фанат», причём настолько страстный, что решил обрушить наш сайт массированной DDoS‑атакой. Сначала мы даже улыбнулись — ну, внимание приятно. Но серверу было не до шуток.

Несмотря на то, что у нас стоит «оранжевая защита», включение режима JS Challenge не помогло — часть запросов всё равно проходила, и сервер ложился от нагрузки. Пришлось действовать решительно.


🧱 Первый рубеж обороны: чёрный список IP

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


🧠 Вдохновение: а давайте подключим Fail2ban?

Так и родилась идея: использовать fail2ban для автоматической защиты на уровне сервера. И, скажем прямо, это было отличное решение.


🔧 Установка Fail2ban

Устанавливаем через APT:

sudo apt install fail2ban


🎯 Фокус на точку атаки

Нам повезло: злоумышленник атаковал один конкретный адрес —
https://www.ru/login/.
https://www.ru/register/
https://www.ru/wp-login.php
и прочие варианты по шаблону

Это дало преимущество: множество повторяющихся POST-запросов с одного IP — именно то, что умеет отсекать fail2ban.


🛠 Создание фильтра

Создаём фильтр:
/etc/fail2ban/filter.d/wordpress.conf

[Definition]
failregex = ^ - - [.] "POST /login/ HTTP/." (200|403|404)

📌 Что делает фильтр?
Он отслеживает POST-запросы к /login/, которые получают коды ответа:

  • 200 — успешный доступ (например, фальшивая регистрация)

  • 403 — доступ запрещён

  • 404 — страницы не существует (спамеры промахнулись)


⚙️ Настройка Jail

Открываем файл:
/etc/fail2ban/jail.local
и добавляем:

[wordpress-register]
enabled = true
filter = wordpress
logpath = /var/log/nginx/domains/www.ru.log
maxretry = 5
findtime = 60
bantime = 36000000
action = iptables[name=wordpress-register, port="http,https", protocol=tcp]

📌 Объяснение параметров:

  • enabled = true — включаем правило

  • filter = wordpress — имя фильтра без .conf

  • logpath — путь к nginx-логу

  • maxretry = 5 — допустимое количество попыток до бана

  • findtime = 60 — за какой период искать попытки (в секундах)

  • bantime = 36000000 — сколько длится бан. Это ~68 лет 😄

  • action — блокировка через iptables на порт 80 и 443 (HTTPS)


✅ Проверка фильтра

Перед применением можно протестировать фильтр:

fail2ban-regex /var/log/nginx/domains/www.ru.log /etc/fail2ban/filter.d/wordpress.conf


🚀 Запускаем защиту

Применяем конфигурацию:

sudo fail2ban-client reload
sudo systemctl restart fail2ban (Перезапускать службу не всегда желательно, так как он забывает все заблокированные IP адреса, а это плохо при массированной атаке, поэтому рекомендую использовать только fail2ban-client reload, где обновляются правила)


📊 Проверяем результат

Проверка статуса:

fail2ban-client status wordpress-register

Пример:

Status for the jail: wordpress-register
|- Filter
| |- Currently failed: 138
| |- Total failed: 6038
| - File list: /var/log/nginx/domains/www.ru.log - Actions
|- Currently banned: 821
|- Total banned: 821
`- Banned IP list: 1.20.227.66 101.126.80.238 ...

Если видите IP — значит, fail2ban работает.

Дополнительно можете проверить цепочку в iptables:

sudo iptables -L f2b-wordpress-register -n


После этого наш фанат решил проявить смекалку и нам пришлось создать еще несколько правил.

Открываем файл:
/etc/fail2ban/jail.local
и добавляем:

[wordpress-ddos]
enabled = true
filter = wordpress-ddos
logpath = /var/log/nginx/domains/www.ru.log
maxretry = 5
findtime = 60
bantime = 36000
backend = auto
action = iptables[name=wordpress-ddos, port="http,https", protocol=tcp]

[hestia-ddos]
enabled = true
filter = hestia-ddos
logpath = /var/log/hestia/nginx-access.log
maxretry = 90
findtime = 60
bantime = 360
action = iptables[name=hestia-ddos, port=8027, protocol=tcp]

[site-ddos-param]
enabled = true
filter = site-ddos-param
logpath = /var/log/nginx/domains/www.ru.log
findtime = 60
maxretry = 2
bantime = 360000
action = iptables[name=site-ddos-param, port="http,https", protocol=tcp]

[nginx-ddos-errorlog]
enabled = true
filter = nginx-ddos-errorlog
logpath = /var/log/nginx/domains/www.ru.error.log
findtime = 60
maxretry = 3
bantime = 86400
action = iptables[name=nginx-ddos-errorlog, port="http,https", protocol=tcp]

[nginx-referrer-selfip]
enabled = true
filter = nginx-referrer-selfip
logpath = /var/log/nginx/domains/www.ru.log
findtime = 60
maxretry = 1
bantime = 2592000 # 30 дней
action = iptables[name=referrer-selfip, port="http,https", protocol=tcp]

Все правила писали через регулярные выражения

/etc/fail2ban/filter.d/hestia-ddos.conf
[Definition]
failregex = ^ - - [.] "." [2-5][0-9]{2} .*

/etc/fail2ban/filter.d/site-ddos-param.conf
[Definition]
failregex = ^ - - [.] "GET /?\d{5,} HTTP/." [2-5][0-9]{2} .*

/etc/fail2ban/filter.d/nginx-ddos-errorlog.conf
[Definition]
failregex = ^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} [error].access forbidden by rule, client: , server: ., request: "GET /?\d{6,} HTTP/1.1"

/etc/fail2ban/filter.d/nginx-referrer-selfip.conf
[Definition]
failregex = ^ - - [.] "GET ." \d+ \d+ "https?://.100.90.80.70."


🚀 Договариваемся с nginx

Так же часть запросов ?78698675876 грузили сервер, т.е. Fail2ban пропускал запросы к серверу из-за того, что трафика было много, но мы это решили делать пересылку на 403 запрос, что бы экономить ресурсы сервера

Открываем файл:
/etc/nginx/conf.d/domains/www.ru.ssl.conf
и добавляем:

Между блоком

server {

...

#Slait
    # Блокировать запросы с реферером, содержащим наш IP
    if ($http_referer ~* "100\.90\.80\.70") {
        return 403;
    }

    # Блокировать GET-запросы типа /?123456 (только цифры в query string)
    if ($request_uri ~* "^/\?\d{9,}$") {
        return 403;
    }
#Slait

...
}

Применяем конфигурацию:
sudo systemctl restart nginx

🎉 Итог

Fail2ban помог нам перехватить атаку в реальном времени, за считанные минуты блокируя сотни IP-адресов. Простая настройка и мощный результат — рекомендуем!

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.