Захист від DDoS: що фільтрує мережа, а що залишається вам
Зміст статті
Стаття для адміністраторів і керівників, які хочуть розуміти, що саме робить захист від DDoS, який входить у вартість кожного нашого сервера. Наприкінці ви знатимете, де його межі, і матимете на сервері власні запобіжники: закриті порти, ліміти запитів у nginx, кеш і fail2ban.
Рівні атак простими словами#
DDoS — це спроба зробити сервіс недоступним, заваливши його трафіком із багатьох адрес водночас. Від рівня, на якому відбувається атака, залежить, хто може її зупинити.
- SYN-флуд
- Сервер отримує потік запитів на відкриття TCP-з’єднання (пакетів SYN), часто з підроблених адрес. Черга нових з’єднань переповнюється, і справжні клієнти під’єднатися не можуть.
- UDP-ампліфікація
- Зловмисник надсилає короткі запити чужим відкритим сервісам (DNS, NTP) від імені вашої адреси, а ті відповідають вам значно більшими пакетами й забивають канал.
- HTTP-флуд
- Тисячі заражених пристроїв відкривають справжні з’єднання і запитують у сайту найважчі сторінки: пошук, кошик, вхід.
Перші дві атаки — мережевого рівня (L3/L4): потік пакетів можна відсіяти в мережі дата-центру ще до сервера. Третя — рівня застосунку (L7): для мережі це звичайний зашифрований HTTPS-трафік, тому зупинити її можуть лише налаштування сервера або зовнішній сервіс, який розбирає HTTP.
Що входить у вартість сервера#
- Захист від атак мережевого рівня (L3/L4) входить у вартість кожного сервера — в усіх лінійках і в усіх країнах: Польщі, Німеччині, Франції та Великій Британії.
- Захист спрацьовує автоматично, коли мережа виявляє атаку на адресу сервера.
- Доплат за обсяг чи тривалість атаки немає.
Два застереження:
- Фільтрація вмикається після виявлення атаки, тому в перші секунди сміттєвий трафік може дійти до сервера.
- Фільтр відсіює пакети, а не стежить за вашим застосунком, тож доступності сайту чи облікової системи він не гарантує.
Чого цей захист не охоплює#
- Атаки рівня застосунку (L7): HTTP-флуд, повільні запити, потік звернень до API.
- Підбір паролів до SSH, RDP, пошти чи адмінпанелі сайту. Докладніше — у статтях про ключі SSH і безпечний RDP.
- Вразливості: застаріла CMS чи плагін, невстановлені оновлення.
- Перевантаження важкими запитами: звіт, що перебирає всю базу, пошуковий робот на сторінках фільтра, власний скрипт у циклі.
Що знадобиться#
- Доступ до сервера через SSH із правами
sudo. Приклади — для Debian 12–13 і Ubuntu 24.04–26.04 з nginx із пакета дистрибутива; для Windows Server придатні лише принципи кроків 1 і 5. - Постійна адреса офісу (у прикладах —
203.0.113.10) і другий комп’ютер для перевірки ззовні.
Що зробити самостійно#
1. Закрийте зайві порти#
Подивіться, які сервіси слухають мережу:
sudo ss -tulpnЛокальна адреса 0.0.0.0, * або [::] означає, що сервіс відкритий на всіх адресах. З інтернету мають бути доступні лише порти, потрібні відвідувачам, — зазвичай 80 і 443, а також SSH із входом лише за ключами. Базу даних і RDP назовні не виставляйте: базу прив’яжіть до 127.0.0.1 або до адреси приватної мережі, а до RDP й облікової системи під’єднуйтеся через VPN.
Увага. Помилка в правилах мережевого екрана може відрізати вам доступ через SSH. Спершу дозвольте свій порт SSH і тримайте відкритим другий сеанс. Якщо доступ усе ж зникне, правила доведеться виправляти через віддалену консоль — її надають за запитом через підтримку (на лінійці Лайт її немає). Готові правила — у статті про базовий захист Linux-сервера.
2. Обмежте запити і з’єднання в nginx#
Зони limit_req і limit_conn оголошують у контексті http. Створіть файл /etc/nginx/conf.d/limits.conf:
# адреси, які не обмежуємо (офіс)
geo $limit_exempt {
default 0;
203.0.113.10 1;
}
map $limit_exempt $limit_key {
0 $binary_remote_addr;
1 "";
}
limit_req_zone $limit_key zone=perip:10m rate=10r/s;
limit_req_zone $limit_key zone=login:10m rate=30r/m;
limit_conn_zone $limit_key zone=addr:10m;
limit_req_status 429;
limit_conn_status 429;Далі ввімкніть обмеження в блоці server вашого сайту (на Debian і Ubuntu це файл у /etc/nginx/sites-available/); замість /login підставте шлях своєї сторінки входу:
server {
# ... listen, server_name, ssl_certificate ...
limit_conn addr 40;
location / {
limit_req zone=perip burst=40 nodelay;
# ... proxy_pass або try_files ...
}
location = /login {
limit_req zone=login burst=5 nodelay;
# ... той самий обробник, що й у location / ...
}
}rate — середня частота запитів з однієї адреси, burst — запас на сплеск, а з nodelay запити в межах запасу обслуговуються одразу, без затримки. Усе, що понад запас, отримує код 429. limit_conn обмежує кількість одночасних з’єднань з однієї адреси; для HTTP/2 і HTTP/3 кожен одночасний запит рахується як окреме з’єднання.
Ці числа — лише відправна точка. Щоб дібрати свої, тимчасово додайте в server рядки limit_req_dry_run on; і limit_conn_dry_run on;: nginx нікого не обмежуватиме, але записуватиме в error.log, кого обмежив би. Перевірте конфігурацію і застосуйте її:
sudo nginx -t
sudo systemctl reload nginx3. Увімкніть кеш для анонімних сторінок#
Найдешевший спосіб пережити HTTP-флуд — не будити застосунок на кожен запит. У nginx для цього є proxy_cache (для PHP-FPM — fastcgi_cache і ті самі директиви з префіксом fastcgi_): навіть кеш на 10 секунд перетворює тисячу однакових запитів на одне звернення до застосунку. Кешуйте лише анонімні сторінки: запити із сесійною cookie чи заголовком Authorization виключіть директивами proxy_cache_bypass і proxy_no_cache, інакше відвідувач може побачити чужий особистий кабінет або кошик.
4. Блокуйте наполегливих за допомогою fail2ban#
Програма fail2ban читає журнали й на певний час блокує в мережевому екрані адреси, які раз у раз порушують правила. Установіть пакети:
sudo apt update
sudo apt install fail2ban nftablesПакет одразу вмикає jail sshd (так у fail2ban називають набір правил), тому адресу офісу додайте до винятків для всіх jail. Створіть файл /etc/fail2ban/jail.d/nginx-limit-req.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.10
[nginx-limit-req]
enabled = true
backend = auto
banaction = nftables
logpath = /var/log/nginx/error.log
findtime = 10m
maxretry = 20
bantime = 1hbackend і banaction задано явно, бо типові значення пакета в різних випусках відрізняються. Якщо сайт має власний error_log, вкажіть у logpath цей файл.
Лише для Debian 12. Jail sshd шукає файл /var/log/auth.log, якого там типово немає, тому служба не запускається і повідомляє про помилку Have not found any log file for sshd jail. Додайте в той самий файл секцію (потрібен пакет python3-systemd, зазвичай його встановлено разом із fail2ban):
[sshd]
backend = systemdПеревірте конфігурацію і перезапустіть службу:
sudo fail2ban-client -t
sudo systemctl restart fail2banФільтр nginx-limit-req входить до пакета й реагує на відмови limit_req в error.log (записи dry_run не рахуються): 20 відмов за 10 хвилин — блокування на годину для портів 80 і 443. Проти флуду з тисяч адрес fail2ban допомагає мало: це засіб проти окремих настирливих джерел.
5. Для сайтів — зовнішній захист рівня застосунку#
Якщо сайт приносить дохід або вже був мішенню атаки, лімітів на одному сервері проти великого ботнету не вистачить. Потрібен зовнішній сервіс класу WAF або CDN: він приймає HTTP-трафік у своїй мережі, відсіює ботів і передає на сервер лише перевірені запити. Після підключення дозвольте порти 80 і 443 лише з мереж цього сервісу, інакше атака піде на адресу сервера напряму. Налаштуйте також модуль realip у nginx (set_real_ip_from, real_ip_header), інакше для limit_req усі відвідувачі матимуть одну адресу.
Як відрізнити атаку від звичайного перевантаження#
Спершу подивіться на з’єднання:
ss -s
ss -Htn state syn-recv | wc -l
ss -Htn state established '( sport = :80 or sport = :443 )' | awk '{print $NF}' | sed 's/:[0-9]*$//' | sort | uniq -c | sort -rn | headПерша команда показує загальну кількість з’єднань, друга рахує напіввідкриті (SYN отримано, підтвердження немає), третя виводить адреси з найбільшою кількістю з’єднань до вебсервера. Далі — журнал вебсервера (формат combined): найактивніші адреси, а якщо замінити $1 на $7 — найчастіші сторінки.
sudo tail -n 50000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -n 20Якщо є моніторинг, порівняйте на одній часовій шкалі графіки вхідного трафіку, пакетів за секунду, з’єднань, процесора й диска. Без нього поточну швидкість на інтерфейсах покаже sar -n DEV 1 5 з пакета sysstat.
- Атака L3/L4
- Різкий стрибок вхідного трафіку й пакетів, а запитів у журналі вебсервера не більшає. Багато напіввідкритих з’єднань; сервер може не відповідати навіть на ping.
- Атака L7
- Шквал запитів у журналі на одну-дві сторінки з безлічі адрес, багато встановлених з’єднань на портах 80 і 443. Процесор зайнятий вебсервером, застосунком і базою, а трафік зростає помірно.
- Звичайне перевантаження
- Трафік звичний або зростає плавно, запити йдуть від знайомих клієнтів і роботів. Диск чи пам’ять завантажені або все навантаження створює один процес, а початок збігається з подією: звітом, резервним копіюванням, розсилкою.
Що повідомити підтримці#
Якщо це схоже на мережеву атаку або сервер недоступний ззовні, зверніться до технічної підтримки — вона працює цілодобово: телефон +38 044 206 08 08, пошта info@united.net.ua. Одразу вкажіть:
- час початку атаки (і закінчення, якщо вона вже минула) із часовим поясом;
- IP-адресу сервера, порт і протокол, на які йде атака, — наприклад,
203.0.113.25, TCP 443; - симптоми: сервер зовсім недоступний, втрачаються пакети, сайт відповідає повільно, не працює лише один сервіс;
- що ви бачили: вивід
ss -s, кілька рядків журналу, знімок графіка, трасу mtr ззовні.
Як перевірити результат#
- Приберіть рядки
dry_run, перезавантажте конфігурацію nginx, як у кроці 2, і з комп’ютера, адреси якого немає у винятках, надішліть серію запитів на сторінку входу. Перші кілька отримають звичайну відповідь, решта — код 429.for i in $(seq 1 20); do curl -s -o /dev/null -w '%{http_code}\n' https://example.com/login; done | sort | uniq -c - Відмови видно в
error.log, а лічильникTotal failedу fail2ban зростає. Після кількох серій адресу буде заблоковано лише для портів 80 і 443; SSH це не зачепить. Остання команда знімає блокування — підставте адресу, з якої перевіряли.sudo grep 'limiting requests' /var/log/nginx/error.log | tail -n 5 sudo fail2ban-client status nginx-limit-req sudo fail2ban-client set nginx-limit-req unbanip 198.51.100.7
Типові помилки#
- Ліміт на адресу без урахування NAT. Увесь офіс або абоненти мобільного оператора виходять в інтернет з однієї адреси. Свої адреси додайте в
geoтаignoreip, а значення добирайте в режиміdry_run. - Режим
dry_runзалишився ввімкненим. У цьому режимі nginx лише пише в журнал і нікого не обмежує.
Що далі#
- Резервний майданчик — якщо простій під час атаки для вас неприйнятний.
- Документація: limit_req, fail2ban.
- Захист мережевого рівня входить у вартість кожного сервера з каталогу; країни — на сторінці локацій.