войти зарегистрироваться

Highload Lab.

Highload Lab.
хабраиндекс
24,48

Учимся отражать микроDDoS на NGiNX

Коротко о нас.
Совместно с Центром телекоммуникаций и информационных технологий МГУ им. М.В. Ломоносова, мы занимаемся исследованиями DDoS.
Основная цель: разработать и донести до сообщества эффективные, доступные техники противодействия распределенным атакам на отказ в обслуживании.
В рамках исследовательской программы мы бесплатно предоставляем защиту. Все что нужно — передать трафик на наше оборудование любым удобным способом (изменить А запись DNS, GRE/IPIP tunnel).

В блоге проекта hll.msu.ru мы будем регулярно публиковать материалы, полученные в ходе исследований. Самые интересные случаи, методики, распространенные ошибки, — публиковать на Хабре.



В прошлую пятницу наблюдали ситуацию, в которой вполне  вменяемый админ, к которому зашли в гости около 7 тыс ботов и, видимо, не очень радостный директор,
поддался панике и соорудил конструкцию вида:
 
NGINX ->  apache ->  mod_php ->  memcached -> [кэшированая версия /]
 
Это бесспорно лучше чем генерация титульной страницы на каждый запрос с несколькими выборками для базы, но  все еще недостаточно хорошо... 
Настолько нехорошо, что даже те 3-8 запросов которые успевал сделать бот, до того как был распознан и забанен, создавали серьезные  проблемы и время обработки запросов сервером составляло 1-2 секунды.
 
Вспомнив пару интересных новых “тематических” фич  которые проходили за последних месяцев в NGINX,  решили поупражняться в config-fu и в итоге получился вот такой вот забавный этюд…
 
http {
# даже обычные медленные клиенты, обычно дороги
reset_timedout_connection on;
client_header_timeout 15;
client_body_timeout 15;
send_timeout 5;
keepalive_timeout 30 15;

# введем две зоны ограничений.
# По открытым соединениям и по request rate
limit_req_zone $binary_remote_addr zone=qglob:16m rate=3r/s;
limit_zone cglob $binary_remote_addr 16m;

server {
listen 80;
server_name www.myhost.ru;

proxy_set_header Host $host;
# необходимо для работы proxy_store
proxy_buffering on;

# ограничим максимальное количество соединений с одного ip
# до 4х клиентов с одного ip по rfc2616
limit_conn cglob 32;

# Быстро отлавливаем “GET / “.
# Дописываем удобное имя файла.
location = / {
rewrite ^/$ /index.html last;
}

#Пост-рейт отдача статического index.html или загрузка с бэкенда.
location = /index.html {
internal;
open_file_cache_errors off;
limit_req zone=qglob burst=9 nodelay;
root /tmp/nginx/cache/;
error_page 404 = /cached$uri ;
}


location /cached/ {
internal;
alias /tmp/nginx/cache/;
proxy_pass phpfarm;
proxy_store on;
proxy_store_access user:rw group:rw all:r;
proxy_temp_path /tmp/nginx/tmp/;
}

#Жестоко зажатый на два запроса в секунду поиск.
location = /advanced_search_result.php {
limit_req zone=qglob burst=2;
proxy_pass phpfarm;
}

# И все остальное.
location / {
proxy_pass phpfarm;
}
}
}

 
 
Далее 5 минутный TTL  на коленке…
nohup `while true; do  rm -f /tmp/nginx/cache/index.html; sleep 300; done`&
 
Что было необходимо и достаточно чтобы остановить 5000 ботов.
 
В  качестве дополнительной меры можно выбирать наиболее назойливых по limiting requests из error_log и отправлять их в Null либо на ближайшем раутере либо в вашем netfilter.

Подумать:
Переработав этот конфигурацианный файл можно организовать и полное кэширование вашего сайта. Попробуем?
 
Хорошим тоном считается что все сервисы, и LAMP в том числе, на сервере стартуют сразу после его перезагрузки автоматически… И автоматически укладываются DDoS ом вместе с сервером.
Возможно стоит выбрать другую стратегию для initrc?
 
В случае например с NAT beeline огромное количество людей может находиться за одним IP, но если вы не yandex.ru разумно предположить что 8 пользователей beeline на вашем сайте это достаточно?
 
Почитать:
NGINX  proxy module.

Дырявые ведра или что такое burst?

Поучаствовать:
Программистам client-side js/ajax и c/c++ разработчик-искусственный интеллект, системы принятия решений — mailto://flx@msu.ru

комментарии (29)

  • Я смотрю сегодня очень хороший день на хабре, сначала streetche рассказал как его проекты спасают, а теперь и вы предлагает сови решения.
    • Они же в рамках исследовательского проекта предлагают, а не за деньги)
  • Это не наши решения нивкоем случае.
    Это простой но эффективный способ отбиться от простой атаки. NGINX очень популярен. DDoS очень популярен. Вот они и нашли друг-друга.

    Кстати, 5-10 тысяч ботов или меньше это каждый ВТОРОЙ случай с которым нам приходят.
    Мы тут вовсе не нужны, все можно своими силами Ж)
  • Вы используете свежие директивы limit_*, но с кэшем работаете по старой схеме, используя «proxy_store on; и т.п.» proxy_cache не использовался намеренно?

    • Есть как минимум еще несколько способов добиться такого-же результата с помощью NGINX, и возможно они будут чуть лучше и быстрее.

      Это не абсолют, это всего-лишь «путевые заметки». Пусть и с комментариями.
      proxy_store нашли более «легким».
  • НЛО прилетело и опубликовало эту надпись здесь.
    • DDoS наверное ;)
      • Не похоже.
    • Рамблер лежит. Скоро поднимут.
  • Есть как минимум еще несколько способов добиться такого-же результата с помощью NGINX, и возможно они будут чуть лучше и быстрее.

    Это не абсолют, это всего-лишь «путевые заметки». Пусть и с комментариями.
    proxy_store нашли более «легким».
  • Как то у вас странно, объявлено
    limit_req_zone $binary_remote_addr zone=qglob:16m rate=3r/s;
    а в limit_zone используется qulag
    И кроме того burst это параметр всплеска относительно ограничения в rate, который будет задержен а не отброшен
    • Limit_zone -> limit_req, задумался )
      • Вообще было-бы крайне разумно на frontend сформулировать разумные query rate политики для различного рода трафика, предупредительно разложив элементы веб-приложения в различные зоны в зависимости от их «стоимости». Статика, простые скрипты и, например поиск.
        • Ну обычно так и делается. Еще не плохо было бы если фронтенд имеет прямой доступ к статике.
    • Спасибо опечатку(qulag).
      Поправил.

      Он будет задержан но не дропнут.
      Если вам хочется получить вместо 2-3 запросов больше — добавьте опцию nowait.
      Мы считаем что и необычных честных клиентов с залипшей F5 и ботов лучше попридержать.
      • Нет, я просто к тому что
        #Жестоко зажатый на два запроса в секунду поиск.
        limit_req zone=qglob burst=2;

        По сути будет обработано 3 запроса в секунду, а 2 запроса сверх них будут задержаны до нормализации, остальные будут дропнуты.

        Лично я понял фразу в коменте как то что будет обработано только 2 запроса. Возможно я не прав )

        >Мы считаем что и необычных честных клиентов с залипшей F5 и ботов лучше попридержать.
        согласен безусловно, сам такое использую для регулирования хайлода
        • Нет, это сработала привычка считать только эффектвный rate.
          У Сысоева-же эффективный burst=sustained+burst.

          Все верно. На самом деле получается эффективных 3 запроса в секунду, хотя изначально подразумевалось sustained == burst.
  • Запехните в memcache страницы по которым идет основной удар — очень поможет ;)
    • И это будет самой популярной ошибкой. С которой и началась эта статья.
      Весь hotlist покэшированной статики осядет в vfs cache.
  • А мне кто-нибудь может объяснить как использовать подобные подходы, когда на странице есть юзерозависимая часть, например какая-нибудь панелька пользователя, и соответственно кешировать всю страницу нельзя?
    • Покэшировать персонализированную информацию конечно-же можно, но hit ratio такого скорее-всего будет cущественно ниже.
      • Так что же тогда делать? Вот пример — главная страница Хабра.
        • докупить бэкэндов и мускула =)
    • кешировать общую часть. а персонализированные данные подтягивать отдельно ajax'ом, например так
      • … и тоже их кэшировать.
        персонализированно.
        самый быстрый запрос к базе — запрос которого небыло.
    • Можно с помощью SSI организовать сборку страницы из кэшируемых и некэшируемых частей. Где-то на хабре про это была статья.
  • !!! reset_timedout_connections = reset_timedout_connection, без S на конце
  • Рискую быть закиданым гнилыми помидорами, но, простите, в упор не могу понять:
    limit_zone cglob $binary_remote_addr 16m;
    # ограничим максимальное количество соединений с одного ip
    # до 4х клиентов с одного ip по rfc2616
    limit_conn cglob 32;


    Почему 32? Если по Сысоеву:

    limit_zone one $binary_remote_addr 10m;
    limit_conn one 1;
    # позволяют не более одного одновременного соединения с одного адреса.

  • вы уверены, что приведённая конфигурация вообще когда либо работала? например:

    location /cached/ {
    internal;
    alias /tmp/nginx/cache/;
    proxy_pass phpfarm;


    что это? в описании директивы алиас сказано, что она говорит энджиниксу смотреть в другой локэйшн. а у вас мало того, что там не локэйшн, а какой-то путь в файловой системе торчит, так ещё и после этого эскалация запроса бэкэнду. приведу простой пример прямо из документации nginx:

    location /images/ {
    root /data/www;
    error_page 404 = @fetch;
    }

    location @fetch {
    internal;

    proxy_pass backend;
    proxy_store on;
    proxy_store_access user:rw group:rw all:r;
    proxy_temp_path /data/temp;

    root /data/www;
    }


    кэшируется целая директория без костылей и сомнительных манипуляций. далее:

    location = / {
    rewrite ^/$ /index.html last;
    }

    зачем этот отдельный локэйшн? и разве не для этого и есть алиас, чтобы не городить тут рерайты? и почему здесь индекс не катит?
Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.