Comments 50
В базе есть поле counter — сколько раз IP-адрес добавляли в базу. По нему вполне можно определить плохиш это, или просто F5 залипло.
Чего я точно ещё не сделал, так это механизм whitelist-а для поисковиков. В теории, они запросто могут давать подозрительную активность. Доверять спискам из интернета не стоит (?), да и Google не рекомендует так делать. Вместо этого предлагают проверять с помощью User-Agent-а и обратного разрешения IP-адреса. Всё это с лёгкостью можно сделать в скрипте.
Если не затруднит объясните в чем разница между

limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 100;

и

limit_req_zone $binary_remote_addr zone=dynamic:10m rate=2r/s;
limit_req zone=dynamic burst=10 nodelay;

Из доки, тоже как то не очевидно, в каких случаях что использовать.
limit_conn perip устанавливает предел количества установленных с одного IP соединений
limit_req_zone — количество обращений с одного адреса, которое допустимо за секунду

В рамках одного соединения может быть множество обращений. Соединение может длиться долго, а обращения могут происходить с разной интенсивностью.
Вот здесь было разрешено 100 соединений с 1 IP (это важно для клиентов, которые за NAT), но при этом более 2 запросов в секунду нельзя формировать.

И к сожалению, при использовании SPDY, ограничить скорость обработки запросов нельзя, поэтому пришлось отключить. А жаль — технология реально ускоряющая загрузку страницы.
Упс, прошу прощения, это писатель доки ошибся, не туда ссылку поставил.
Скорость обработки запросов в SPDY-соединении не может быть ограничена.
И ссылка на документацию ngx_http_limit_req_module.

По-моему, однозначно написано. Да и логично, т.к. в SPDY отдельные запросы мультиплексируются в один пакет данных. И в теории, в одном HTTP-запросе может прийти вся страничка целиком — с текстом, картинками и скриптами.
Эм… то что вы написали не имеет ничего общего с реальностью, даже в представлении о том, как работает SPDY.

На самом же деле все гораздо прозаичнее. Модуль limit_req работает совсем на другом уровне. Речь там о директиве limit_rate, ограничивающей bandwidth на ответ, просто потому что в nginx этот интерфейс реализован на уровне соединения, и им воспользоваться не получилось, нужно свой городить ещё выше.

В процессе редактирования доки кто-то неправильно понял оригинальный limitation, а я не вчитался и упустил этот момент. Исправим.
limit_conn — ограничение на количество одновременно обрабатываемых запросов.
limit_req — ограничение на частоту поступления/обработки запросов.
UFO landed and left these words here
C Cloudflare не доводилось работать, но был опыт с Amazon и Heroku. Ничего плохого не скажу, на всякий товар найдётся свой покупатель. Пока ещё dedicated-серверы могут конкурировать с «облаками» как по аптайму, так и по цене. Ну и философско-шкурный вопрос — если «не изобретать велосипед» и отдавать услугу (хостинг/администрирование) на откуп «дядькам с большими квадратными головами» (фраза знакомых из Intel-а), то самому придётся зарабатывать чем-то другим.
из запретил доступ со всех IP, не принадлежащих им
Вот этого не понял. Разъясни, пожалуйста.
Cloudflare — это не хостинг. Это CDN+Защита от DDoS + WAF. Причем базовый тариф бесплатен.
В моём понимании CDN — это тоже хостинг, узко-специализированный. Про CloudFlare я очень высокого мнения, особенно после атаки на Спамхаус.
Бесплатный звучит заманчиво, возможно он даже очень хорош, но вот бесплатный Heroku вызывает только слёзы, а платный широко раскрывает глаза.
А вообще всё надо считать — у меня была ситуация, когда «CDN» из пяти «серверов» на Intel Atom оказался выгодней настоящих CDN-ов.
Т.е. если как-то можно грубо оценить нагрузку, то я беру dedicate, а если нагрузка может расти до облаков — то и хоститься там же.
Бесплатный тариф cloudflare с легкостью заменит решение, описанное в посте. И сделает это более продуманно, в том числе с автоматической отработкой ложных срабатываний, анализом и защитой на L7, ну и, собственно, распределением кеша контента по континентам.
Как академическое упражнение пост весьма полезен. Но на сегодняшний момент для большинства проектов мелкого и среднего уровня (да и крупного, чего уж там) гораздо выгоднее отдать эту тему на аутсорс таким конторам, как cloudflare, а усилия свои направить на улучшение/доработку функционала ресурса и наполнение его контентом.
UFO landed and left these words here
эм а разве без nginx нельзя ограничивать кол-во соединений с одного IP?
примерно так:
iptables -p tcp --dport 80 -m iplimit --iplimit-above 20 --iplimit-mask 24 -j REJECT
еще есть такой модуль помоему:
iptables -I INPUT 1 -p tcp --dport 80 -m string --string «GET / HTTP/1.0» --algo kmp -j DROP
От DDoS-а это не спасаёт, так как атакующих много. Да и в пределах одного keep-alive соединения можно порядочно насолить.
А в случае с SPDY (VBart, поправьте меня, если ошибаюсь) соединение и так будет всего одно.
Это совершенно разного уровня ограничения. С помощью модуля limit_conn ограничивается не количество соединений, а количество одновременно обрабатываемых запросов (в документации это названо «активными соединениями»). Keep-alive соединений при этом может быть больше. А со SPDY может быть соединение одно, а параллельных запросов в нём много.
Пользуясь случаем хочу пропиарить решение знакомых людей, которое связыват лог апача с бан таблицами и мне очень неплохо помогало:
github.com/unicodefreak/log2ban

Для совсем тяжелых случаев (тысячи в секунду) не очень походит, для описанной ситуации более чем.
Респект! Почерпнул оттуда идею — надо вместе с IP хранить в базе хэш URL-а, тогда плохишей будет легче отделять, они действительно ломятся (как правило) по одному адресу. А если начнут бегать по всему дереву форума? Решение хорошее, но всё равно парсер логов, от которого я пытался избавиться. Да и от логов тоже можно избавляться — нечего им убивать SSD-шку или отжирать память в tmpfs-е. В идеале — лог — это только средство отладки (ИМХО).
Не могли бы вы рассказать, почему не помог fail2ban, о принципах атаки ботов, как они его обходят в то же время загружая систему?
fail2ban помог, но я описал его недостатки — при высокой нагрузке на WEB-сервер, он уже сам по себе является тормозящим фактором (усиливающим эффект атаки?). Вот например, при ~800 запросах в секунду (это только на динамику), у нас лог вылетает со скоростью ~ 200кб/с
В данном случае это недостаток polling-архитектуры, логичнее и быстрее event/interrupt подход, что я и сделал.

Атаки, что я наблюдал у себя, были двух видов:
— не прекращающийся POST на регистрацию в форуме — вероятно спамеры подбирают логины
— ярко выраженный волнообразный наплыв однообразных запросов с нескольких сотен адресов
А по моему сейчас если можно защититься программными средствами от атаки уже ддосом не назвать… Вот если прийдет реальный syn-flood или udp flood то там дело до фаерволла или любого иного ПО даже не дойдет. Все остальное это в открытом доступе баловство с которым справляется и iptables и nginx.
Да, все так. Подобные методы работают только для пионер-атак, типа «уснул на кнопке F5» или «ололо, пацаны, я в первый раз скачал ab, такая крутая штука».
Увы, даже ab или whiletruedo программы это было популярно когда nginx еще и лимитить не умел ничего, сейчас в доступе уже вещи потяжелее, от которых Ваш хостер может в лучше случае перецепить адрес на nullroute. А что обиднее всего что это все еще и продавать умудряются, не говоря уж о полноценноп ботнете.
а разве это дерево комментариев не с методов началось? :) методы и инструменты в руках скрипткидди — синонимы.
Вот как раз в руках скрипткидди есть только инструменты. Но при чем тут скрипткидди?
А по моему сейчас если можно защититься программными средствами от атаки
Cпорный вопрос — на многих аппаратных фаерволах установлены вполне себе программные операционные системы, и даже Linux. Так что это, ИМХО, вопрос терминологии.
уже ддосом не назвать…
Если атака с большого количества адресов приводит к деградации или недоступности сервиса — то это есть чистый DDoS, по определению.

С серьёзным SYN-флудом пока не сталкивался. Да и статья не про этот тип атаки.
Решение с CGI скриптом забавное, но я не понимаю, почему именно CGI? Мне кажется вы могли бы аналог этого bash скрипта на том же PHP написать и не извращаться с fcgiwrap
Во-первых, извращаться с fcgiwrap не пришлось — он у меня уже был настроен для collectd. Думаю что у многих так же.
Во-вторых, я тоже сразу подумал про PHP, но нужно, чтобы скрипт выполнялся от рута. А для этого пришлось бы подымать ещё один экземпляр php-fpm и запускать его от рута, что громоздко и не красиво.
Дать пользователю, с которым выполняется php-fpm, права на sudo /usr/sbin/ipset add ... не думали?
Думал, но ИМХО, это потенциальная дырка в безопасности, ну и exec() нужно разрешать, что мне не очень нравится.
Мне кажется CGI-скрипт на bash, выполняющийся с высочайшими привилегиями, потенциально большая дыра в безопасности, чем очень ограниченное право на выполнение команды через sudo.
Придумать пример, как скомпрометировать сервер с разрёшенным ipset из PHP кода довольно несложно, особенно на хостинге с массой разношёрстных пользователей. А вот как это сделать с моим CGI скриптом я пока не нашёл. Поможете?
Например, есть некий пользователь samowar. Через sudoers мы даём ему такое право:

samowar ALL = NOPASSWD: /usr/sbin/ipset add web_black_list *.*.*.* timeout 600

Как пользователь samowar сможет скомпрометировать сервер если разрешена только такая команда? Расскажите, очень интересно.
Он может добавить в чёрный список адреса важных клиентов, таким образом лишив их доступа к сервису, может вырастить список до размеров, когда это станет проблемой для ядра (добавив туда весь Интернет (шутка).

Кроме того, sudo у меня не используется, почти с таким же успехом можно поставить SUID на ipset.

На продакшен серверах я использую spawn-fcgi, не через localhost:1234 как в примере (потому что это потенциально очень опасно), а через unix socket, на который выставлены права 700 для nginx:nginx. Сам CGI-скрипт тоже с такими правами.

Таким образом, сделать что-то плохое, можно только скомпрометировав nginx. Я в него свято верю. Но вы правы — потенциально это тоже небезопасно. Какие могут быть ещё варианты?
Если злодей сможет через sudo добавить в блок-лист важных клиентов на 10 минут, то значит у него есть доступ к выполнению других произвольных команд или изменению кода, что в свою очередь означает что лишение доступа важных клиентов — лишь малая толика того, что этот злодей может сделать. Я бы даже сказал что вам очень повезет если злодей окажется настолько глуп что заблокирует важных клиентов, которые в свою очередь об это сообщат вам. Не о том беспокоиться надо.
Не совсем уловил идею, почему вы пишете «значит у него есть доступ к выполнению других произвольных команд или изменению кода». Если я вас правильно понял, вы предлагаете разрешить из PHP-кода выполнение ipset от рута. О каких других произвольных командах идёт речь?
Допустим. Ну там ещё есть вагон функций, через которые можно запускать.
Собственно всё очевидно, кроме, разве что, SQLite. Я его добавил пока просто для статистики, но в принципе можно использовать и для удаления устаревших плохишей из черного списка. Время 5 минут пока тоже не используется.

Ipset позволяет задать как дефолтный timeout хранения записей в списке так и определять его индивидульно для каждой записи (для особо злостных адресов, например).
Очень удобно. Не надо городить механизм очисти списка.
Согласен, для простой конфигурации — очень удобно. Но если надо выявлять злостных плохишей, то нужна статистика, а она в базе. Кроме того база пригодится для создания white-list-ов. Скорее всего оптимальныйм будет гибридный вариант: использовать таймаут, для того чтобы не тулить в cron скрипт по удалению старых записей, а базу использовать для сбора статистики и решения о том, на сколько банить очередного плохиша, т.е. первый раз засветился — на три минуты, второй раз — на 10 и т.п.

P.S.: Очень люблю статистику, просто млею от rrd графиков.
Что в вашем понимании злостный плохиш? И причем тут статистика?
ДДос они в африке ддос. Он может быть умным, может быть тупым. Но в любом случае, главным во время ддоса будет выживание ресурса, а не вычисление кто плохиш а кто чесный пионер.
Если с адреса идёт 100500 реквестов в секунду то лучше его блокировать на неделю для профилатики и написать письмо владельцу адреса. Это в разы эфективнее.
Статистика как раз и нужна для того, чтобы узнать сколько было блокировок, и на основе этого задавать время очередной блокировки.
Например, если мы знаем, что IP уже блокировался 20 раз, то при очередной попытке напакостить — баним на неделю, т.к. он — злостный плохиш.

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

Пару IP я для любопытства отслеживал — они начинают и заканчивают отмечаться примерно в одно и то же время, очень похоже на домашний компьютер с трояном.
Only those users with full accounts are able to leave comments. Log in, please.