null

Хроники пикирующего бомбардировщика или распределённое нагрузочное тестирование

Как могли заметить наши постоянные посетители неделю назад, 22 сентября на наш сайт была осуществлена DDoS атака. В результате мы смогли провести нагрузочное тестирование нашего сервера и выявить слабые места в конфигурации. Если бы не досадный аппаратный сбой на пограничной системе, то отражение атаки было в каком-то смысле даже увлекательно.

А началось всё 15 сентября, т.е. за неделю до атаки, со следующего письма, пришедшего на контактные адреса во whois:

Your site www.tune-it.ru will be subjected to DDoS attacks 100 Gbit/s.
Pay 100 btc(bitcoin) on the account 1CCCaAwZtkQj7nEVprrvVG8zXaiSGydjg8
Do not reply to this email

Но, почему-то, 100 bitcoin-ов ни у кого из нас не оказалось, да и подарить кому-то ~$1700 не понятно за что тоже желающих не нашлось. Хотя возможно и не совсем уж не понятно, так как попадаются статьи утверждающие, что не такой уж этот bitcoin и анонимный.

А в четверг, вернувшись с выезда к заказчику, меня всретили словами:

- Посмотри пожалуйста, что-то у нас при заходе на tune-it nginx пишет "Bad gateway".

Захожу на сервер, задумчиво набираю команду dmesg и обнаруживаю строчку:

kern.maxfiles limit exceeded by uid 80, please see tuning(7).

При внимательном рассмотрении выяснилось, что залёг мой любимый node agent от glassfish. Вроде раньше за ним тяги к исчерпанию открытых файлов не наблюдалось. Перезапускаем агента, типа разбираться в причинах такого его поведения будем потом. Заходим на главную страницу - вроде работает, можно дальше заниматься своими делами.

Через какое-то время проверю работу сайта - опять "Bad gateway". Опять залёг node agent. После повторного перезапуска запускаю top и наблюдаю, что некий java процесс потребляет примерно 400% процессорного времени. То, что glassfish в момент запуска иногда умудряется потребить до 1200% процессорного времени я уже привык, но обычно он довольно быстро уменьшал свои аппетиты. Запускаем tail -f http.access.log и... я понимаю, что у меня в глазах рябит от пролетающих строк лога.

- DDoS! - догадался я.

Это конечно не обещанные 100Gbit/s, так как иначе бы нам положили бы основной канал целиком и полностью вместе с нашим провайдером, а я даже заходил на сервер не через резервный. Да и количество запросов, согласно логам nginx, в самом пике достигло только лишь 1298 за одну секунду. Вполне возможно, что еще нам помог наш относительно узкий канал, но тем не менее.

При этом флудящий бот старательно пытается косить под живого человека, запрашивая различные URL внутри сайта по ссылкам с корневой страницы (всего ботом было запрошено 8737 разных URL) и используя на первый взгляд разные user agent.

Посмотрев на это дело я первым делом отфильтровал наиболее активных:

fgrep 22/Sep/2011: http.access.log | cut -d ' ' -f 1 | cut -d . -f 1-3 | sort | uniq -c | sort -n

и заблокировал подсети с которых было больше тысячи запросов на уровне пакетного фильтра. Правда, как показал последующий разбор полётов, на эти подсети пришлось только около 1/6 всех запросов, и, соответственно, это не очень помогло спасению ситуации.

После блокирования подсетей я в очередной раз попытался перезапустить упавший к тому моменту node agent, и наблюдение за логами показало явную зависимость активности флудеров от доступности сервера, так как количество запросов в секунду после падения node agent, когда nginx начинал возвразать "Bad gateway", резко снижалось.

Дальше я стал пытаться воспользоваться возможностями pf для отражения DDoS. Но тут уже возникла проблема другого рода. Обыкновенный заход браузером на главную страницу нашего сайта генерирует 72 запроса, при неудачном стечении обстоятельств каждый запрос будет отправлен отдельным соединением. Таким образом попытки ограничения количества устанавливаемых соединений с одного IP через max-src-conn и max-src-conn-rate обречены на провал - каждый отдельно взятый атакующий агент отправляет запросы с меньшей скоростью. Да и попытки использовать большие значения приводят к весьма заметной нагрузке на ядро. Единственным полезным ограничением на уровне pf является использование max, ограничивающее максимальное количество состояний для конкретного правила, не переполняя глобальную таблицу состояний и не усложняя жизнь других сервисов.

И тут мне на глаза попалась последовательность запросов с разных IP и одинаковым user agent:

Mozilla/4.0 (compatible; MSIE 8.0; Win32; Trident/4.0

Который показался мне подозрительным, и grep по логу показал, что до сегодняшнего дня запросов с таким user agent не встречалось. Увидев такое дело я решил посмотреть статистику по user agent за сегодняшний день:

fgrep 22/Sep/2011: http.access.log | cut -d '"' -f 6 | sort |
  uniq -c | sort -n | tail -10

Результат оказался красноречив:

1110 Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0
1292 Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
2002 Mozilla/5.0 (compatible; AhrefsBot/1.0; +http://ahrefs.com/robot/)
157043 Mozilla/4.0 (compatible; MSIE 8.0; Win32; Trident/4.0)
160808 Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.00
164912 Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.10
167480 Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7
176359 Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.17) Gecko/2009122116 Firefox/3.0.17
177059 Opera/9.64 (Windows NT 5.1; U; ru) Presto/2.1.1
332151 Mozilla/4.0 (compatible; MSIE 7.0; Win32)

Не долго думая прибиваю в nginx возрат "привычного" для ботов 502 Bad Gateway при совпадении user agent с особо отличившимися из перечисленных выше и наблюдаю плавное падение активности атаки. Немного огорчает тот факт, что некоторые из них совпадают с реальными user agent реальных браузеров, поэтому если у Вас в одном из браузеров не открывается наш сайт, значит пора обновить браузер.

Стоит отметить тот факт, что nginx за время атаки nginx обслужил 1335812 запросов от ботнета с 31748 различных IP адресов и, в общем, показал себя полезной "прокладкой" между пользователями и glassfish.

Как показало исследование интернетов мы попали под "замес" ботнета известного по крайней мере с 2010 года, совмещающего в себе и числодробилку для bitcoin, и DDoS модуль. И мы далеко не единственные его жертвы. Правда в таких случаях не очень понятна позиция хостеров, не желающих поанализировать логи и поискать простые и не требующие дорогостоящего оборудования решения. Хотя я конечно не буду утверждать, что против "правильного" DDoS такое решение может найтись. Но попытаться стоит.