DDoS и методы борьбы с ним (для малых серверов)

9 Июля 2014 14:43
0

В прошлую пятницу на один из наших серверов была DDoS-атака (атака на вычислительную систему, совершаемая с целью довести её до отказа, то есть создание таких условий, при которых легальные пользователи не могут получить доступ к предоставляемым ресурсам, либо этот доступ затруднён). Ниже постараюсь рассказать, что мы делали по серверу и как смогли восстановить его работу, чтобы в будущем подобную проблему можно было бы решить быстро и на других ресурсах.

Начну с того, в чем выражалась атака. Первое, что заметили - недоступность всех хостов и проблемы с подключением по SSH к серверу. Посмотрев htop (намного удобнее, чем типовой top; желательно его ставить на серверах, так как это помогает при разборе полетов и управлении процессами), увидели 462 процесса apache и 28 тредов. Увеличив serverlimit, maxclients в httpd.conf за 1 минуту, они опять забились целиком и полностью.

Чтобы понять, кто атакует и сколько их, удобно пользоваться:
"netstat -ntu | awk ‘{print $5}’ | cut -d: -f1 | sort | uniq -c | sort -n"

А затем вручную блокировать IP и подсети в htaccess /var/www или /var/www/html

Если IP - динамические и не из одной подсети, то такой способ отваливается автоматически. iptables - дефолтный файрвол, доступный в CentOS, и, если ядро позволяет, то удобно использовать это:
iptables -A INPUT -p udp —dport 80 -m connlimit —connlimit-above 10 -j DROP
iptables -A INPUT -p tcp —dport 80 -m connlimit —connlimit-above 10 -j DROP


Ограничивается число соединений на один IP. Наше ядро этого не позволяло, и iptables при рестарте ругался. Перекомпилировать, обновлять ядро под атакой с и так лежащими хостами очень не хотелось, и было боязно. Блокировка ручками не решала проблему. Вместе с netstat нашелся еще tcpdump, который позволяет складывать атакующие IP-адреса в файл и сортировать его по количеству, а потом использовать для защиты.

В итоге было найдено это: http://deflate.medialayer.com/
DDoS и методы борьбы с ним (для малых серверов)
Смысл работы прост. В определенный, заданный промежуток времени, выполняется отслеживание количества коннектов и при превышении заданного лимита, эти IP добавляются в reject для iptables. Раз в другой промежуток времени IP удаляются из черного списка (хранятся в /tmp). Мы запуск и добавление поставили минуту, на бан - 1 час (т.к. идет подмена IP, то они могут быть и хорошими на самом деле, поэтому освобождать их нужно). Штука может работать как с apf, так и с iptables. И работает это, как показала практика, очень хорошо. Но оказалось, что есть некоторые детали работы apache под DDoS'ом. Да, разумеется, часть проблем бы изначально снял nginx, но на сервере, который атаковали, его не было, поэтому ниже о деталях.

Как я уже написал выше, каждую минуту ddos.sh выполнял свою задачу: добавлял в iptables reject заданных ip/sources и блокировал их доступ. Но apache завершает коннекты не сразу после блокировки iptables, а только после завершения выполнения запущенного скрипта. То есть через минуту коннекты начинают отпадать, но появляются новые атакующие IP, которые их создают заново. При нашем заданном maxclients, на 1 коннект отводится 8×425 мб ОЗУ (особенно при включенном keepalive) в пределе, что намного больше максимально доступного 1 Гб ОЗУ. Сократить maxclients нельзя, так как его не хватит для обработки реальных запросов. Убрать keepalive тоже нельзя, так как иначе под каждый поток будет создаваться отдельный коннект (нормальные клиенты будут отсекаться ddos.sh). Кстати, это оказалось очень важным, так как без keepalive ddos.sh работать будет неправильно (количество коннектов при обновлении одной страницы стремится к 40-ка, и в бан будут попадать обычные пользователи при завешенных параметрах в ddos .conf). В итоге получалось переполнение по памяти и вылетал mysqld, затем postfix, а затем сам ddos.sh, и атака продолжалась полным ходом.

Мы решили проблему добавлением части подсетей в disallow .htacces /var/www/html и параллельным запуском нескольких ddos.sh (не меняя sleep, так как после прекращения атаки лишние ddos.sh проще просто поубивать через htop).

Также важным критерием оказался быстрый поиск хоста, который атакуют. У нас на сервере их больше 40, поэтому мы делали так: удалили все существующие access_log, перезапустили apache и вывели размеры access_log всех имеющихся хостов. Тот, который больше всего вырос, и есть виновник торжества (главное - проверить, что access_log включен везде - на всех хостах). Ресурс, который атаковали, у нас был заглушкой сервера (т.е. атака была по IP). Мы хотели его закрыть через order allow, deny .htaccess, но это оказалось неверно. Так как это unknownhost, то он должен существовать. Иначе apache выведет дефолтную страницу CentOS с сервера, и атака (выкачивание и забивание канала и коннектов) продолжится. Правильно - либо давать ошибку 500 по хосту (сознательно внести неправильный набор символов, например, в .htaccess), либо требовать авторизацию (через тот же .htaccess + .htpasswd). Тогда канал не забивается, и коннект отваливается сразу.

Полезные ссылки:

http://linux-beginner.ru/ddos/ - подробно о netstat, tcpdump, ddos.sh
http://deflate.medialayer.com/ - сам скрипт ddos.sh
http://habrahabr.ru/post/204508/ - другие способы, типы анализа хедеров при атаке
http://habrahabr.ru/post/124492/ - в одном из последних комментариев отлично описана разница в настройках CentOS и стандартном файле ddos.sh