Як захистити сервер/сайт від ddos-атак (мій секретний спосіб)

Атака на ваш сервер може здійснюватися на різних рівнях. Ці рівні мають назви L2, L4 і L7. На кожному такому рівні треба створити окремий ешелон захисту від атак. Далі про кожний рівень окремо.

Захист від L2-атак

L2 - це атака спрямована на відмову вашого обладнання, на якому працює сервер. Це спроба фізично перенавантажити мережеву карту хоста, щоб та фізично не встигала обробити об’єм поступаючих запитів. Також сюди можна віднести навмисне досягання штучного ліміту швидкості інтернету на хості.

Програмно від такого типу атак захиститися неможливо. Якщо вас пробивають на такому рівні, тоді треба міняти постачальника хостингових послуг. Або покращувати своє обладнання, якщо ви власноруч зробити датацентр в своєму гаражі.

Особисто я зараз можу порадити хостинг OVH. Це єдиний хостинг серед тих, що я перевірив особисто, який дійсно захищає від будь-яких L2 атак. Але хости OVH коштують приблизно в два рази дорожче, ніж звичайні хости. Але водночас вони і дешевше, ніж хости, які безпосередньо спрямовані на захист від ddos-атак, як, наприклад, ddos-guard.

Розділення хостів на публічні і приватні

Якщо хочете користуватися дешевими хостами і мати добрий захист від ddos-атак, тоді раджу розділити всі ваші хости на публічні і приватні.

Публічні хости - це дорогі хости з гарним захистом від ddos-атак, наприклад, від хостингу OVH. Ваші клієнти повинні з’єднуватися тільки з публічними хостами. В DNS ваших доменів повинні бути тільки айпі публічних хостів.

Приватні хости - це дешеві хости з поганим захистом від ddos-атак, наприклад, від хостингу Hetzner. Айпі цих хостів не повинні ніде публікуватися. Про них не повинні дізнатися ваші вороги дудосери.

Я раджу купляти найдешевшу мінімальну VPS для публічних хостів. Налаштовувати там peverse proxy на приватні хости. Якщо вам важливо зберігати оригінальні айпі клієнтів, використовуйте proxy protocol, приклад через nginx. Але найкрутіший варіант в плані продуктивності буде використовувати iptables forwarding.

Peverse proxy через iptables

Через iptables можна перетворити ваші публічні хости в маршрутизатори. В iptables існує nat таблиця правил, де можна створити правила DNAT і SNAT. Ці правила дозволяють перенаправляти вхідні пакети на інший хост, підміняючи айпі адрес призначення (destination).

Завдяки таким правилам можна створити найпродуктивніший проксі, який майже не буде споживати ресурси хосту (процесор, оперативна пам’ять). Вам не доведеться купляти багато дорогих публічних хостів. Зазвичай вистачає однієї VPS за 5$ на OVH для тисяч одночасних з’єднань.

Наприклад, у вас є сервер на приватному хості на порті 666. У такому випаду на публічному хості треба створити такі правила:

iptables -t nat -A PREROUTING -p tcp --dport 666 -j DNAT --to-destination айпі_приватного_хосту:666
iptables -t nat -A POSTROUTING -p tcp --dport 666 -j SNAT --to-source айпі_публічного_хосту

Ці правила будуть перенаправляти всі з’єднання з публічного хосту з порту 666 на приватний хост на порт 666. Приватний хост буде мати з’єднання тільки з публичним хостом, всі з’єднання на приватному хосту матимуть айпі адрес публічного хосту.

Хибнопозитивні спрацювання L2-захисту на ваші приватні хости

Коли ddos-атака починається, система хостингу її фіксує і вмикає захист. Захист передбачає фільтрування трафіку: всі клієнти розділяються на хороші і погані. Всі погані клієнти блокуються і не пропускаються до хосту. Іноді так буває, що захист хибнопозитивно блокує легітимних клієнтів і навіть наші власні приватні хости.

Давайте подивимося на уявну картину ddos-атаки:
image

Для кожного з’єднання наш публічний хост створює proxy з’єднання з приватним хостом. Але система захисту хостингу нічого не знає про наші приватні хости, для неї це просто якісь з’єднання. Наші приватні хости виглядатимуть дуже підозріло, бо з ними встановлено дуже багато з’єднань. І скоріше за все захист хибнопозитивно заблокує наші власні приватні хости, через що наш сервер стане недоступним.

Я зіштовхнувся з цим на хостингу OVH. Але я думаю, цей ефект існує і на інших хостингах. Я називаю цей ефект атакою через L2-захист.

Щоб уникнути цього ефекту, потрібно виконати дві дії:

  1. Додати айпі своїх приватних хостів в білий список в системі захисту хостингу публічних хостів. Але не на всіх хостингах є така функція, а ще рідше вона якось допомогає. На OVH такої функції немає.
  2. Купити на публічні хости додаткові приватні айпі. Ці айпі будуть використовуватися виключно для зв’язку з приватними хостами (про це нижче окремо). Або в якості альтернативи використовувати IPv6.

Приватний айпі на публічний хост для з’єднань з приватними хостами

Захист від ddos-атак зазвичай вмикається саме на айпі адрес хоста, а не на сам хост загалом. Тобто якщо у хоста декілька айпі адресів, тоді при атаці на один адрес захист буде захищати тільки його, а інші адреса будуть працювати в штатному режимі.

Я раджу на публічний хост купити додатковий айпі адрес. Один адрес буде публічний, а другий приватним. Публічний айпі адрес буде використовуватися для з’єднань з клієнтами, а приватний - для з’єднань з приватними хостами.

Приватні айпі не повинні ніде публікуватися, про них не повинні дізнатися вороги дудосери. Інакше вас зможуть атакувати через L2-захист.

Додати додатковий айпі адрес на хост можна завдяки службі systemd-networkd. Цей адрес треба зробити адресом за замовчуванням для вихідних з’єднань. У такому разі саме він буде використовуватися для з’єднань з приватними хостами.

Щоб змінити адрес для вихідних з’єднань, потрібно в конфіг /etc/systemd/network/50-default.network додати такі секції [Route] и [Address] :

[Route]
Gateway=gateway_адрес
PreferredSource=приватний_адрес

[Address]
Address=приватний_адрес/32
Label=external_ip

Значення для параметра Gateway треба взяти із однойменного параметра із секції [Network] . Айпі адрес для вихідних з’єднань треба вказати в параметрах PreferredSource з секції [Route] та Address з секції [Address] .

В параметрі Label вказується ім’я мережевого інтерфейсу, який буде створений для нового адресу. Значення цього параметра може бути будь-яким, але не більше за 15 символів.

Щоб зміни вступили в силу, треба перезавантажити службу systemd-network такою командою:

systemctl restart systemd-networkd

Приватні IPv6 адреса

Якщо всі ваші хости підтримують IPv6, тоді в якості кращої альтернативи можна використовувати приватні айпі IPv6. Зазвичай IPv6 даються безкоштовно. Захист для IPv6 працює повністю окремо від IPv4, якщо він взагалі існує :smile:. До того ж атаки на IPv6 не так розповсюджені, як на IPv4.

Ще один полу-приватний айпі для з’єднань з сторонніми серверами

Якщо хочете ще кращого захисту, тоді на публічний хост можна купити ще один полу-приватний айпі. Це вже буде третій айпі адрес. Його можна використовувати для з’єднань з сторонніми серверами, які не є вашою інфраструктурою. Наприклад, це rest api сервісів, якими ви користуєтеся, dns запити, запити синхронізації часу, запити системи моніторінгу.

Якщо це не зробити, тоді для цих задач буде використовуватися ваш приватний адрес. Використовувати приватний адрес для з’єднань з сторонніми серверами - це ризик розкрити приватний адрес.

Полу-приватний айпі також не можна публікувати. Але з іншого боку стороннім серверам повністю довіряти не можна. З’єднуючих з сторонніми серверами, ми по суті розкриваємо айпі, який використовується для цих з’єднань.

У разі використання полу-приватного айпі адреса саме його треба налаштувати як адрес вихідних з’єднань за замовчуванням.

Щоб приватний адрес використовувався виключно для з’єднань з приватними хостами, можна це налаштувати такими командами:

ip r add адрес_приватного_хоста1 via gateway_адрес dev eth0 proto static src приватний_адрес
ip r add адрес_приватного_хоста2 via gateway_адрес dev eth0 proto static src приватний_адрес
ip r add адрес_приватного_хоста3 via gateway_адрес dev eth0 proto static src приватний_адрес

Нагадую, що gateway_адрес можна подивитися в /etc/systemd/network/50-default.network в параметрі Gateway.

// todo Додати інструцію, як зробити, щоб зміни ip r зберігало після ребуту.

Підсумок по L2

Схематична модель приватних айпі:
image

Захист від L4-атак

L4 - це атака спрямована на вразливості протоколу TCP/IP і особливості обробки цього протоколу операційною системою. Найчастіше атаки L4 спрямовані на те, щоб вичерпати ліміт портів для з’єднань, а саме це syn flood атака.

Настройки ядра лінукс sysctl

В першу чергу треба змінити настройки ядра лінукса. Для цього створюємо конфіг /etc/sysctl.d/local.conf, в який додаємо такі параметри:

# включаємо ipv6, якщо треба
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0

# ми не маршрутезатор, вирубаємо редірект
# але локально дозволяємо для докера
net.ipv4.conf.lo.accept_redirects = 1
net.ipv4.conf.lo.secure_redirects = 1
net.ipv4.conf.lo.send_redirects = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_forward = 1

# збільшуємо ліміт з'єднань
net.ipv4.netfilter.ip_conntrack_max = 16777216

# налаштування, значення яких швидше за все підібрано добре, т.к. у багатьох уроках мають однакове значення
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_orphan_retries = 0
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_congestion_control = htcp
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.ip_local_port_range = 32768    60999
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_window_scaling = 1
net.core.somaxconn = 65535

# копіював бездумно )))
net.ipv4.tcp_max_orphans = 400000
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 10
net.ipv4.tcp_mem = 65536 131072 262144
net.ipv4.tcp_wmem = 4096 87380 33554432
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_syncookies = 1
net.ipv4.route.flush = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.lo.accept_source_route = 0
net.ipv4.conf.eth0.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.tcp_rfc1337 = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.core.netdev_max_backlog = 262144
net.ipv4.tcp_rmem = 4096 87380 33554432
net.netfilter.nf_conntrack_max = 16777216
net.ipv4.icmp_echo_ignore_all = 0
net.core.rmem_default = 31457280
net.core.wmem_default = 31457280
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
fs.inotify.max_user_watches = 16777216

# копіював бездумно, але з ще менш значних джерел
kernel.printk = 4 4 1 7
kernel.panic = 10
kernel.sysrq = 0
kernel.shmmax = 4294967296
kernel.shmall = 4194304
kernel.core_uses_pid = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
vm.swappiness = 60
vm.dirty_ratio = 80
vm.dirty_background_ratio = 5
fs.file-max = 2097152
net.core.optmem_max = 25165824
net.ipv4.neigh.default.gc_thresh1 = 4096
net.ipv4.neigh.default.gc_thresh2 = 8192
net.ipv4.neigh.default.gc_thresh3 = 16384
net.ipv4.neigh.default.gc_interval = 5
net.ipv4.neigh.default.gc_stale_time = 120
net.netfilter.nf_conntrack_tcp_loose = 0
net.netfilter.nf_conntrack_tcp_timeout_established = 1800
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 20
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 20
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 10
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.ip_no_pmtu_disc = 1
net.ipv4.route.max_size = 8048576
net.ipv4.udp_mem = 65536 131072 262144
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_fack = 1
net.ipv4.tcp_ecn = 2

Щоб ці настройки вступили в силу, треба виконати таку команду:

sysctl -p /etc/sysctl.d/local.conf

В основному ці параметри збільшують ліміти з’єднань. Чомусь за замовчуванням ці ліміти сильно менше того, на що здатні сучасні хости. По суті, ми ці ліміти просто відключаємо, ставлячи їм дуже великі параметри. Також тут є параметри, які зменшують тривалість життя мертвих з’єднань. Мертві з’єднання були актуальні в епоху слабкого нестабільного інтернету.

Відверто кажучи, я не шарю за всі ці параметри, які я вказав вище. Але я їх перевірив особисто на собі, вони працюють у мене на хостах вже багато років. Я їх просто копіюю на всі свої нові хости. Ці параметри дійсно захищають від syn flood атак.

Iptables

Iptables - це фаєрвол в лінукс. Через цей фаєрвол можна блокувати з’єднання за різними умовами, тобто фільтрувати трафік. Кожен вхідний пакет (і вихідний також) спочатку обробляться iptables, а вже потім потрапляє на ваш сервер.

Iptables складається з правил. Кожен пакет проходить через всі правила, поки не натрапить на правило, яке або заблокує пакет, або пропустить його. Я раджу за замовчуванням блокувати весь трафік, окрім того, що вам явно треба пропускати.

Спочатку очистимо старі правила і поставимо настройки за замовчуванням:

# Вказуємо політику за замовчуванням: пропускаємо весь вхідний, пересилаючий і вихідний трафік
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# Видалити всі поточні правила, ланцюжки, лічильники
iptables -F
iptables -X
iptables -Z

Заблокуємо весь вхідний трафік за деякими винятками (читаємо коментарі). Для цього треба створити такі правила:

# Пропускаємо пакети, які належать з'єднанням, які вже були пропущенні раніше
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Пропускаємо всі пакети локальних з'єднаннь
iptables -A INPUT -i lo -j ACCEPT
# Пропускаємо пакети з вашим айпі адресом, який ви використовуєте для з'єднання з хостом по ssh
iptables -A INPUT -s ваш_айпі -j ACCEPT
# Все інше блокуємо
iptables -A INPUT -j DROP

Тепер тільки ви можете з’єднатися з вашим хостом і більше ніхто і ніщо. Але будьте обережні: якщо у вас зміниться айпі, тоді ви втратите доступ до вашого хосту.

Далі відкриваємо порти серверів, які ви хочете зробити публічними, такими правилами:

iptables -I INPUT -p tcp --dport 228 -j ACCEPT
iptables -I INPUT -p tcp --dport 666 -j ACCEPT

Окремо хочу зауважити, що закривати всі порти важливо навіть на приватних хостах. Одним із способів пошуку приватних хостів - це скануваннях всіх можливих айпі хостингів.

Але це не все. В iptables також існують таблиці. За замовчуванням правила зберігаються в таблицю filter. Про це мало розповідається в учбових матеріалах про iptables, але таблиця filter не найпродуктивніша таблиця в плані ddos-атак.

Окрім filter, також існує таблиця mangle. В mangle є ланцюжок PREROUTING, який спрацьовує на самих ранішніх етапах обробки пакетів. Якщо блокувати пакети на цьому етапі, тоді система буде витрачати менше ресурсів на оброку пакетів. Ланцюжок PREROUTING був створений для редагування метаданих пакетів. Але нам нічого не заважає в цьому ланцюжку проводити фільтрацію трафіку, хоч це і не підходяще для цього місце.

Всі ті правила, які я писав вище, можна тупо перенести в mangle. А filter залишити просто порожнім:

# чистимо filter
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
iptables -X
iptables -Z

# чистимо mangle
iptables -t mangle -P PREROUTING ACCEPT
iptables -t mangle -P INPUT ACCEPT
iptables -t mangle -P FORWARD ACCEPT
iptables -t mangle -P OUTPUT ACCEPT
iptables -t mangle -P POSTROUTING ACCEPT
iptables -t mangle -F
iptables -t mangle -X
iptables -t mangle -Z

# створюємо правила в mangle
iptables -t mangle -A PREROUTING -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t mangle -A PREROUTING -i lo -j ACCEPT
iptables -t mangle -A PREROUTING -s ваш_айпі -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp --dport 666 -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp --dport 228 -j ACCEPT
iptables -t mangle -A PREROUTING -j DROP

Iptables IPv6

Якщо на хості є IPv6, тоді треба зробити все теж саме, тільки для IPv6 адрес. Для IPv6 існує окремий фаєрвол ip6tables. Там теж існує таблиця mangle з ланцюжком PREROUTING. Робимо там все так само, як і в звичайному iptables.

Але є правило, яке в ip6tables треба додатково додати, на відміну від iptables (читайте коментар):


# Accept all ICMP packets. Unlike with IPv4, it's not a good idea to block ICMPv6 traffic as IPv6 is much more heavily dependent on it:
ip6tables -t mangle -A PREROUTING -A INPUT -p ipv6-icmp -j ACCEPT

Invalid пакети

Окремо хочу зауважити про INVALID пакети. Пакети INVLAID - це такі пакети, які не належать жодному пропущеному з’єднанню. Часто в інтернеті я натикався на пораду, що такі пакети треба блокувати через iptables.

На практиці я помічав, що блокування INVALID пакетів призводить до блокування пропущених раніше з’єднань. Це відбувалося з цілком легітимними з’єднаннями і навіть зі мною особисто. Я неодноразово це перевіряв і я абсолютно впевнений в цьому. Але чому саме так відбувається, і чому вони хоч якось впивають на раніше пропущені з’єднання - я не знаю. Просо запам’ятайте - блокувати INVALID пакети НЕ треба.

Захист від L7-атак

L7 - це атака спрямована на вразливості вашого сервера. Код вашого сервера може мати обмеження у зв’язку з поганим кодом, поганою оптимізацією, багами тощо.

Бот-атака

Бот атака - це атака на сервер великою кількістю ботів, які імітують собою звичайних клієнтів. Сервер не справляється з обробкою такої великої кількості клієнтів і падає.

Зазвичай одна з найтяжких операцій будь-якого сервера - це початок з’єднання клієнта. Під час цього виконується дуже багато операцій ініціалізації з’єднання. Дані клієнта завантажуються з бази даних, ініціалізуються структури даних в коді сервера, викликаються обробники подій тощо. Тому сам факт з’єднання з сервером великої кількості ботів за короткий проміжок часу може причинити шкоду.

Захиститися від бот атак можна завдяки лімітам в iptables.

Ліміт на кількість одночасних з’єднань по окремим айпі

Визначте, яка кількість одночасних з’єднань від одного клієнта є нормою для вашого сервера. Наприклад, для серверів, у яких клієнти це звичайні люди, нормою зазвичай буває 1 одночасне з’єднання. Візьмемо число 1 для прикладу.

Ставити ліміт 1 буде тупо, тому що на одному айпі може сидіти багато людей. В сучасному світі IPv4 адресів не вистачає. Дуже часто відбувається колізія IPv4 адресів у різних людей. Іноді ціла будівля використовує один айпі адрес, іноді це ціле село, іноді це мобільний інтернет. Також можуть виникати баги, через які попередні мертві з’єднання клієнта все ще висять, а клієнт хоче створити нове з’єднання.

Мої власні дослідження виявили, що з одного айпі може сидіти до 16 різних клієнтів-людей при загальній кількості одночасних клієнтів 1000 штук. Тому, щоб отримати ліміт, помножуємо його на 16.

Створюємо правила ліміту одночасних з’єднань в iptables:

# Будьте уважні, щоб це правило було ДО лімітів, щоб ліміти обробляли тільки перший пакет з'єднання.
iptables -t mangle -A PREROUTING -m state --state ESTABLISHED,RELATED -j ACCEPT

# Блокуємо нове з'єднання, якщо на порті 666 вже є 64 з'єднань з одного айпі адресу без урахування двух крайніх чисел адресу.
iptables -t mangle -A PREROUTING -p tcp --dport 666 -m connlimit --connlimit-above 64 --connlimit-mask 16 -j DROP
# Блокуємо нове з'єднання, якщо на порті 666 вже є 24 з'єднань з одного айпі адресу без урахування крайного чисела адресу (маска --connlimit-mask 24).
iptables -t mangle -A PREROUTING -p tcp --dport 666 -m connlimit --connlimit-above 24 --connlimit-mask 24 -j DROP
# Блокуємо нове з'єднання, якщо на порті 666 вже є 16 з'єднань з одного айпі адресу (маска --connlimit-mask 32). 
iptables -t mangle -A PREROUTING -p tcp --dport 666 -m connlimit --connlimit-above 16 --connlimit-mask 32 -j DROP
# Інакше пропускаємо з'єднання на порт 666.
iptables -t mangle -A PREROUTING -p tcp --dport 666 -j ACCEPT

Як бачите, я також додав ліміти для айпі адрес без урахування одного і двох крайніх чисел адресу. Дуже часто ботнети мають схожі айпі адреса з однієї підмережі.

Не соромтеся ставити ліміти з запасом. Ваш захист повинен захищати, а не хибнопозитивно блокувати легітимні з’єднання. Краще ліміт буде в 100 раз більше потрібного, ніж його взагалі не буде. Без ліміту будь-який школяр з одного айпі зможе створити десятки тисяч з’єднань.

Ліміт на частоту з’єднань по окремим айпі

Визначте, яка частота створень нових з’єднань від одного клієнта є нормою для вашого сервера. Це число індивідуальне для різних типів серверів. Для прикладу візьмемо не більше 20 з’єднань за хвилину.

Створюємо правило ліміту частоти з’єднань в iptables:

# Будьте уважні, щоб це правило було ДО лімітів, щоб ліміти обробляли тільки перший пакет з'єднання.
iptables -t mangle -A PREROUTING -m state --state ESTABLISHED,RELATED -j ACCEPT

# Блокуємо нове з'єднання, якщо на порті 666 за крайню хвилину вже було 20 з'єднань.
iptables -t mangle -A PREROUTING -p tcp --dport 666 -m hashlimit --hashlimit-htable-max 262144 --hashlimit-htable-size 262144 --hashlimit-name limit_name --hashlimit-mode srcip --hashlimit-above 20/minute --hashlimit-burst 40 -j DROP

Розберемо аргументи:

  • --dport 666 - ліміт на порт 666.
  • --hashlimit-htable-max 262144 --hashlimit-htable-size 262144 - збільшений розмір таблиці ліміту. Правило запам’ятовує історію з’єднань для кожного айпі в таблиці. За замовчуванням ліміт маленький, таблиця переповнюється, і правило просто перестає працювати.
  • --hashlimit-name limit_name - назва таблиці ліміту. Замість limit_name впишіть свою назву.
  • --hashlimit-mode srcip - тип ліміту srcip. Тип srcip означає ліміт для кожного окремого айпі відправника пакету.
  • --hashlimit-above 20/minute - ліміт 20 з’єднань за хвилину.
  • --hashlimit-burst 40 - початковий ліміт. Зазвичай, це значення ставлять в два рази більше за ліміт. Якщо вам сильно цікаво, що це таке, загугліть Token Bucket Algorithm, крута штука.

Загальний ліміт на частоту з’єднань

Окрім індивідуального ліміту на частоту з’єднань по окремим айпі, я також раджу поставити загальний ліміт на частоту з’єднань для всіх айпі разом. На перший погляд такий ліміт може здатися марним, але він просто імба. Якщо почнеться ddos-атака з десятками тисяч айпі адресів, тоді індивідуальні ліміти не спасуть. Від такої атаки захистить саме загальний ліміт.

Загальний ліміт треба ставити такий, який в теорії здатен витримати ваш сервер. В прикладі я буду використовувати значення 20 нових з’єднань за секунду.

Створюємо правила загального ліміту в iptables:

# Загальний ліміт повинен бути ПІСЛЯ інших лімітів.

# Якщо за крайню секунду було менше 20 нових з'єднань, тоді пропускаємо нове з'єднання.
iptables -t mangle -A PREROUTING -p tcp --dport 666 -m limit --limit 20/s --limit-burst 40 -j ACCEPT
# Інакше блокуємо нове з'єднання.
iptables -t mangle -A PREROUTING -p tcp --dport 666 -j DROP

Параметр --limit-burst 40 означає теж саме, що і --hashlimit-burst 40 в попередньому ліміті.

На цьому моменті у вас може виникнути питання. Якщо почнеться бот атака, цей ліміт спрацює. З сервером з’єднаються перші 20 ботів, сотні наступних ботів з’єднатися не зможуть, алеж і легітимні клієнти також з’єднатися не зможуть. В чому тоді сенс такого ліміту? Так, цей ліміт заблокує і легітимних клієнтів в тому числі. Задача цього ліміту інша - зберегти ваш сервер від перенавантаження.

Під час масивної бот атаки сервер може перенавантажитися. Логи будуть забиті інформацію про з’єднання ботів, через що може закінчитися місце на диску хоста. Сервер почне неконтрольовано споживати ресурси хосту. Від цього хост почне лагати. В кращому випадку сервер просто впаде, а в гіршому - буде працювати на межі можливого. Всі інші сервера, які стояли поряд на тому ж хості, теж почнуть лагати. Ваш сервер може перенавантажити базу данних, через що ВСЯ ваша інфрактруктура проекту почне лагати.

Система захисту хостингу може подумали, що ваш хост сам почав бот атаку. Це пов’язано з тим, що ваш сервер на кожне з’єднання бота сам може робити запити на сторонні сервери. Тому ваш хост може бути заблокований як вашим хостингом, так і сторонніми серверами.

Приклад з життя: один мій сервер отримував геологацію для кожного нового з’єднання через сервіс https://geoip.maxmind.com/geoip/. Під час бот атаки мій сервер почав отримуватися геолокацію для ВСІХ ботів. Тобто кожен бот провокував мій сервер зробити запит в https://geoip.maxmind.com/geoip/. Хтось атакував мене, а я ненавмисно атакував https://geoip.maxmind.com/geoip/.

Саме від цих поганих наслідків буде захищати загальний ліміт.

Обхід загального ліміту для перевірених клієнтів

На ваш сервер можуть здійснювати тривалі масивні бот атаки. Якщо так станеться, тоді ваш сервер буде недоступний дуже довго через дію загального ліміту. З цим можна боротися створенням обходу ліміту для перевірених клієнтів.

Створюємо ipset список для перевірених клієнтів такою командою:

ipset create anti_ddos_bypass_ips iphash maxelem 524288 -exist

Я назвав цей список anti_ddos_bypass_ips. Вказуємо максимальний розмір списку (maxelem) таким, щоб всі айпі ваших перевірених клієнтів туди влізли. Чомусь розмір за замовчуванням доволі невеликий.

Створюємо правила обходу загального ліміту в iptables:

# Вставляємо обхід ДО правила загального ліміту.
# Пропускаємо всі з'єднання, айпі яких є в ipset-списку обходу. 
iptables -t mangle -A PREROUTING -p tcp -m set --match-set anti_ddos_bypass_ips src -j ACCEPT

# Загальний ліміт.
iptables -t mangle -A PREROUTING -p tcp --dport 666 -m limit --limit 20/s --limit-burst 40 -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp --dport 666 -j DROP

Ваша задача оновлювати цей ipset-список anti_ddos_bypass_ips. Для цього вам треба буде створити код, який буде цим займатися. Ось команди для редагування списку:

# Додати айпі в список.
ipset add anti_ddos_bypass_ips айпі
# Видалити айпі зі списку.
ipset del anti_ddos_bypass_ips айпі

Слабкі місця

Ваш сервер може мати слабкі місця. Слабкі місця - це такі частини програмного коду сервера, які споживають неоправдану велику кількість ресурсів хосту. Дудосери можуть спрямовувати свої ddos-атаки на ці слабкі місця. Їм може бути достатньо всього декілька десятків запитів, щоб ваш сервер повністю перенавантажити.

Боротися з слабкими місцями доведеться вам самостійно. Переписуйте код сервера, оптимізуйте його, створюйте в коді ліміти тощо.

DNS захист

Є декілька хитрощей з DNS, які теж допоможуть вам захиститися від ddos-атак.

Фейкові DNS записи

Часто для пошуку ваших приватних хостів дудосери сканують DNS записи ваших доменів. Вони перевіряють популярні адмінські домени, як наприклад: smtp.your-domain.com, vpn.your-domain.com, ssl.your-domain.com. Дійсно, багато недосвідчених адмінів палять там айпі приватних хостів.

Я раджу вам піти на хитристь. Навмисно створіть в своїх доменах всі популярні DNS записи. Тільки замість айпі своїх хостів вставте туди фейкові айпі або айпі ваших конкурентів. Нехай дудосер піде по хибному сліду.

Всі такі DNS записи можна знайти в інтернеті. Наприклад, можна подивитися, які DNS перевірять популярні сканери по типу цього DNS Spy: public domain scans.

SRV записи

Якщо клієнти вашого серверу підтримують SRV записи, тоді можна зробити ще одну хитрість.

Зазвичай дудосери атакують сервер по A запису. Багато хто з них навіть не знають про існування SRV записів. Або вони вставляють домен вашого сервера в свою програму для дудосу, а ця програма тупа, і теж підтримує тільки на А-записи.

Купіть ще один маленький публічний хост. Я такий хост назваю хостом для дудосу. На цей хост теж треба розмістити ваш сервер. В DNS створюєте A запис, який повинен вказувати на ваш хост для дудосу, а для інших публічних хостів створюєте SRV записи.

Кількість атак на ваші публічні хости зменшиться на 90%. Всі ці атаки відтепер будуть здійснюватися на хост для дудосу, на якому не буде жодного реального клієнту. А дудосер буде думати, що він вас успішно атакує.

Приховання наслідків дудосу

Потужні масивні ddos-атаки в будь-якому випадку здатні нанести вам шкоду. Від цього важко захиститися повністю. Через ddos-атаку ви можете втрачати 5-10% легітимного трафіку через хибнопозитивну роботу захисту. Якщо ви погано налаштували ліміти, тоді ще більше легітимного трафіку буде втрачено.

Будь-яка ddos-атака прагне досягти результату. Якщо атака не приносить результат, тоді дудосер не буде запускати її знову і знову. Масивні ddos-атаки коштують грошів. Тому важливо приховати всі наслідки атаки, щоб дудосер думав, що у нього нічого не виходить.

Показники онлайну проекту

Показники онлайну проекту треба запрограмувати так, щоб вони не показували справжній онлайн під час ddos-атак. Якщо сталося різке зменшення онлайну, не треба цього показувати. Якщо дудосер буде орієнтуватися по цим показникам, тоді це введе його в оману. Він подумає, що атака не працює і припинить її. Це не жарт :smiley:.

Новини проекту

Іноді ddos-атаки все ж таки приносять серйозну шкоду. В цих випадках доводиться зв’язуватися зі своєю аудиторією проекту, щоб дати пояснення за те, що сталося.

Дудосери також читають ці пояснення. Дудосер отримає задоволення, якщо ви в поясненні напишите, що проект задудосили. Якщо ви напишете, що працюєте над захистом, дудосер це сприйме як додатковий виклик. Якщо ви будете жартувати над дудосером або ображати його, тоді він озлобиться і буде дудосити ще більше.

Я раджу вмикати дурника і в зверненні взагалі не писати про ddos-атаку. Зробіть вигляд, що ви тупі, і навіть не зрозуміли, що сталося. Напишіть, наприклад, що це просто хост залагав. Це повністю знецінить всю працю дудосера, він втратить інтерес.

Якщо дудосер пише вам в особисті повідомлення, відповідати не треба. Киньте його в ЧС. Не треба з ним намагатися подружитися, не треба його ображати, взагалі нічого не треба.

Діагностика атаки

Перед тим, як робити захист від ddos-атаки, потрібно точно визначити, що вас саме атакують. Не завжди атаку видно в логах. І не завжди те, що схоже на атаку, являється нею.

Зовнішні проблеми

Проблеми хостів бувають зовнішні і внутрішні. До внутрішніх проблем відноситься: перенавантаження хосту, баги, дії адмінів тощо. До зовнішніх проблем відноситься: ddos-атака, технічні неполадки на хостингу, збій мережі, технічні неполадки на сторонніх серверах.

Найдієвіший метод визначити, що проблема зовнішня - це подивитися на стан чужів проектів, які схожі на ваш. Знайдіть проекти, які використовують такі самі технології, що і у вас, такі самі хостинги, мають спільну з вами аудиторію.

Не завжди адміністрація хостингу повідомляє про технічні неполадки, тому всі офіційні канали зв’язку - нахер. Іноді відбувається збій інтернету в цілих країнах. Для вас це буде виглядати, як мінус половина трафіку. Але якщо все теж саме сталося в чужих проектах, тоді це проблема зовнішня.

Перенавантаження хосту

У хоста є ресурси, а саме: оперативна пам’ять, диск, процесор, мережа. Якщо хоча б щось з цього в дефіциті, тоді наслідки можуть бути непередбачуваними. Скоріше за все у вас почнуться лаги. Лагати почне те, що в нормальних умовах взагалі ніколи не повинно лагати. В таких випадках вам може здатися, що ваш хост атакують, але насправді це не так.

Але можливо і таке, що ddos-атака спричинить перенавантаження хосту. Особливо це поширено при L7 атаках. Раджу завестися таким інструментом, як Zabbix. Якщо у вас буде історія нагрузки хоста, тоді ви зможете відрізняти причину проблем від наслідків.

2 Вподобання

Після цієї статті можна мене вбити. Більше нічого корисного від мене на цьому форумі ви не побачите.

2 Вподобання

О за іптейблс знаю, а ще можна зробити шлюз, не бачив писав ти про це чи ні, короче тема типу така - що на приваті ти скидаєш усі можливості підключення до цього серверу і дозволяєш коннект лише з публічного серверу і потім просто перекилуєш їх на приватний.

Але я не знаю як тоді сховати приватний ще, якщо брати якісь чіт клієнти чи наприклад в клієнті Labymod показує на якому ти айпі граєш.

А за ddos-guard - дизлайк, бо він рф) На хецнері знаю теж нормальний захист, ось був випадок клієнта ддосили - а вони навіть не помітили)

@pika_ks

приваті ти скидаєш усі можливості підключення до цього серверу і дозволяєш коннект лише з публічного серверу і потім просто перекилуєш їх на приватний

Я про це писав. Заголовок - Розділення хостів на публічні і приватні. І далі про те, як це зробити.

Але я не знаю як тоді сховати приватний ще

В статті написано, як. Ніде не буде видно приватний айпі.

А за ddos-guard - дизлайк, бо він рф)

Та він говно. Як і будь який сервіс анті дудосу. Наживаються на людях, які не знають про iptables.

1 Вподобання