Защищаемся от HTTP DDoS и прочих Хабраэффектов

    Простой способ защиты от HTTP DDoS — включить syn-cookies и заблокировать подонков. Но что делать если атакует 5к-10к хостов да еще и с динамическими IP? Тут нам на помощь придет frontend-backend архитектура c промежуточным кэшированием! Почему с промежуточным кэшированием? А потому что в моем случае от шквала запросов от frontend'а backend умирал унося за собой систему.

    Итак алгоритм действий:
    • Меняем порт нашего backend сервера на любой отличный от 80 (пусть будет 2080)
    • Устанавливаем Varnish
    • Устанавливаем и настраиваем lighttpd
    • Ограничиваем кол-во соединений с одного хоста средствами iptables

    Так, как, у меня в наличии было несколько серверов, то будет рассмотрена версия конфигурации именно с несколькими серверами, но никто не мешает вам все это запихнуть на один сервер =)

    Как менять порт вашего любимого веб-сервера я думаю рассказывать не нужно, предлагаю перейти сразу к настройке Varnish.

    Собственно устанавливаем сам пакет:
    apt-get update && apt-get install varnish

    Далее приводим C-подобный файл конфигурации (в Ubuntu это /etc/varnish/default.vcl) приблизительно к такому виду:
    backend default {
    .host = "1.1.1.1"; #IP нашего backend'а
    .port = "2080"; #порт
    .first_byte_timeout = 300s; #без этого таймаута varnish не хотел забирать контент с backend'а
    }

    acl purge {
    "localhost"; #разрешаем очистку кэша только с локалхоста
    }

    sub vcl_recv {
    if (req.request == "GET" && req.url ~ "\.(jpg|jpeg|gif|ico)$") {
    lookup;
    }

    if (req.request == "GET" && req.url ~ "\.(css|js)$") {
    lookup;
    }

    if (req.request == "GET" && req.url ~ "\.(pdf|xls|vsd|doc|ppt|iso)$") {
    lookup;
    }

    if (req.request == "POST") {
    pipe;
    }

    if (req.request != "GET" && req.request != "HEAD") {

    if (req.request == "PURGE") {
    if (!client.ip ~ purge) {
    error 405 "Not allowed.";
    }
    lookup;
    }

    pipe;
    }

    if (req.http.Expect) {
    pipe;
    }

    if (req.http.If-None-Match) {
    pass;
    }

    if (req.http.Authenticate || req.http.Authorization) {
    pass;
    }
    lookup;

    }

    sub vcl_hit {
    if (req.request == "PURGE") {
    set obj.ttl = 0s;
    error 200 "Purged.";
    }
    }

    sub vcl_miss {
    if (req.http.If-Modified-Since) {
    pass;
    }

    if (req.request == "PURGE") {
    error 404 "Not in cache.";
    }
    }

    sub vcl_fetch {
    if ( obj.http.x-accel-redirect ~ ".*" ) {
    set req.url = obj.http.x-accel-redirect;
    restart;
    }
    }


    Перезапускаем varnish: service varnish restart

    Теперь можно приступить к установке и настройке нашего frontend'а — lighttpd.
    Я предпочитаю брать lighttpd отсюда, но вам никто не мешает скачать его из репозиториев дистрибутива (apt-get install lighttpd).
    И правим конфиг до тех пор, пока он не примет следующий вид:
    server.modules = (
    "mod_cache",
    "mod_proxy",
    "mod_access",
    "mod_evasive"
    )

    server.network-backend = "writev"
    server.max-keep-alive-requests = 4
    server.max-keep-alive-idle = 4
    server.max-read-idle = 10
    server.max-write-idle = 30
    server.event-handler = "linux-sysepoll"
    server.stat-cache-engine = "disable"
    server.protocol-http11 = "enable"
    server.max-worker = 2 #Если у вас один процессор или вы все запускаете на одном сервере то стоит поставить 1
    server.max-fds = 10000
    server.max-connections = 5000
    server.port = 80
    server.document-root = "/var/www"
    server.errorlog = "/var/log/lighttpd/error.log"
    server.pid-file = "/var/run/lighttpd.pid"
    server.username = "www-data"
    server.groupname = "www-data"
    etag.use-inode = "enable"
    etag.use-mtime = "enable"
    etag.use-size = "enable"
    server.dir-listing = "disable"
    evasive.max-conns-per-ip = 3 #Разрешаем только три одновременных подключения

    cache.enable = "enable" #Включаем кэширование на стороне лайти
    cache.bases = ("/var/spool/cache") #Здесь мы будим хранить кэш
    cache.max-memory-size = 40960 #40Gb
    cache.lru-remove-count = 512
    cache.support-queries = "enable"
    cache.dynamic-mode = "enable"
    cache.refresh-pattern = (
    "\.(?i)(js|css|xml|po)$" => "240", # update js/css/xml every 4 hours and on refresh requests
    "\.(?i)(htm|html|shtml)$" => "30 use-memory", # update html/htm/shtml every 30 minutes and on refresh requests
    "\.(?i)(jpg|bmp|jpeg|gif|png)$" => "2880", # update graphics files every 2 days
    "\.(?i)(rar|zip|wmv|iso|avi|mp3|ape|rm|mpeg|mpg|wma|asf|rmvb|flv|mkv|ogg|ogm|swf|flac)$" => "0 fetchall-for-range-request", # cache media file forever
    ".(?i)php$" => "5", # update php request every 5 minutes
    "." => "30 use-memory" #
    )

    mimetype.use-xattr = "enable"
    include_shell "/usr/share/lighttpd/create-mime.assign.pl"

    #Bad users go to hell
    $HTTP["useragent"] == "" {
    url.access-deny = ( "" )
    }

    $HTTP["host"] =~ "(^|\.)habrahabr\.ru$" {
    proxy.balance = "round-robin"
    proxy.server = ( "/" =>
    (
    ( "host" => "1.2.1.1", "port" => 6081 ), #Отправляем запросы
    ( "host" => "1.2.1.2", "port" => 6081 ), #серверам varnish
    ( "host" => "1.2.1.3", "port" => 6081 ),
    ( "host" => "1.2.1.4", "port" => 6081 )
    )
    )
    }
    proxy.worked-with-mod-cache = "enable"


    И напоследок iptables и небольшой тюнинг системы:

    Разрешаем 10 подключений в секунду с одного IP:
    iptables -I INPUT 1 -p tcp -m hashlimit --hashlimit-upto 10/sec --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-name HTTPD_DOS -m tcp --dport 80 -m state --state NEW -j ACCEPT
    Увеличиваем количество открытых файлов:
    ulimit -n 5000
    Плюшки для sysctl.conf:
    vm.swappiness=10
    vm.vfs_cache_pressure=10000
    vm.dirty_ratio = 1
    vm.dirty_background_ratio = 1
    vm.dirty_writeback_centisecs = 250
    vm.dirty_expire_centisecs = 3000
    kernel.panic = 10
    net.ipv4.tcp_fin_timeout = 15
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_sack = 1
    net.core.rmem_max = 16777216
    net.core.rmem_default = 16777216
    net.core.netdev_max_backlog = 262144
    net.core.somaxconn = 262144
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_max_orphans = 262144
    net.ipv4.tcp_max_syn_backlog = 262144
    net.ipv4.tcp_synack_retries = 2
    net.ipv4.tcp_syn_retries = 2
    net.ipv4.netfilter.ip_conntrack_max = 1048576
    net.nf_conntrack_max = 1048576
    net.ipv4.icmp_echo_ignore_all = 1
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 15
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 15
    net.ipv4.ip_local_port_range= 10000 65000


    Как все это работает? lighttpd получает запрос от клиента и если он удовлетворяет определенным критериям (в нашем случае это не пустой User-agent, клиент запрашивает домен habrahabr.ru и это не 4ый одновременный запрос) отправляет запрос одному из серверов varnish. Varnish проверяет свой кэш на наличие нужного пользователю контента отдает его либо из кэша либо отправляет запрос на backend, если в кэше данного контента нет или он устарел.

    UPD: Спасибо за карму, перенес «Информационную безопасность»
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 55

      +28
      класс, статья призвана научить я так понимаю.

      1. было бы неплохо комментарии хотя бы на блоки конфигов зачем оно все.
      2. вкратце обьяснить для чего нужен софт.

      я понимаю что можно пойти и почитать все в гугле или мануале. ну раз можно — зачем тогда ваша статья ?:)
        +1
        полностью согласен.
        ИМХО, в сфере безопасности howto типа «напишите это и кликните тут» совершенно неуместны.
        0
        А если, к примеру, 50к хостов атакует, этот способ поможет?
          +1
          Да, но нужно разносить lighttpd, backend и varnish на разные машины. Лайти выдержит любую нагрузку, а вот varnish при большом количестве коннектов начинает работать немного криво, потому и пришлось ставить несколько серверов varnish и включать кэш у lighttpd.
            –3
            Лайти не выдержит любую нагрузку, точнее даже сетевая подсистема умрет от 50К одновременных запросов. Тут надо скрипты для бана подключать, а в случае с такими объемами еще и на несколько серверов разносить и использовать ddns.
              0
              # netstat -tapn | grep 1.2.3.4:80 | wc -l
              47189

              =)
                +5
                Это не отражает реальную картину. 50К висящих соединений это не 50К запросов одновременно.
                У лайти есть статистика по количеству запросов в секунду. При 50К ботов будет где-то 200-500К запросов в секунду. Ну или по крайней мере не меньше 50К.
                  0
                  было бы интересно увидеть такую статистику и узнать как себя ведет сервер
                    0
                    Лайти говорит о ~2.5к одноременных запросов. Сервер по CPU загружен на 10-15% (AMD Athlon X2 4400) кроме лайти на этом сервере крутится один из серверов varnish.
            +2
            При и на порядок более меньшем количестве хостов http флуда уже надо принимать меры по
            ограничению количества параллельных коннектов с 1го ип и ограничения количества создания тсп сессий к вашему 80му порту в еденицу времени (речь не про син-кукисы, а про полноценные конекты).
            И делать это желательно на уровне файрвола.

            Кроме того, надо отслеживать както ддосящие хосты и добавлять их в DROP цепочки.

            Как и чем — вторично. Главное не надеятся только на фронтенды.
              +1
              50к динамических хостов?
                0
                для 50к (равно как и для 5к) есть ipset из path-o-matick, правда там 65к лимит, но никто не мешает иметь несколько сетов
            0
            А я делаю так
            rrdns на домен
            N штук веб-морд которые отдают статику nginx (200k с каждого тазика)
            1 штук мастер-сервер, который вливает контент на морды

            Для динамических сайтов и форумов такое решение не очень, но если надо держать шквал посещаемости на обычном сайте — это решение с большим КПД
              0
              Может перенесёте в тематический блог? (вдарил по карме, теперь её 5)
                +1
                В какой и как?
                  +3
                  Защита информации. Для начала нужно зайи на страницу блога и в него вступить
                    0
                    Каким образом это относится к защите информации? Скорее уж «системное администрирование» какое-нибудь.
                      0
                      или чтото на тему highload
                +1
                >>uname -n 5000

                ulimit :)
                  +2
                  Мало спал много работал =)
                  Спасибо исправил.
                  +7
                  Как то сложно все, чем это решение отличается от родного nginx с тем же кэшем и ограничением коннектов? Зачем кэшируем в лайти и что за варниш?
                  • UFO just landed and posted this here
                      +1
                      C ngnix не работал, это просто еще одно решение. И интересно ngnix не убьет тот-же самый apache на vps при обработке 50к запросов одновременно?
                        0
                        nginx в данном случае может еще взять еще кусок работы на себя. Например, количество соединений с ip. Хотя там есть опции и поинтереснее.

                        А вот насчет апача, спорно. Для php лучше работает php-fpm, для остальных ЯП возможно тоже есть альтернативные решения. Очень уж прожорлив на память это апач.
                          +1
                          Ну и да, 50К запросов и ваше решение ниразу не выдержит. И не только на ВДС.
                        0
                        А если я наоборот хочу создать хаброэффект, что бы проверить работоспособность и какой пик нагрузки?
                        Как это можно без создание глупого топика в «Я пиарюсь»?
                        0
                        Ух намудрили… это гораздо лучше сделать с последним стабильным nginx. Хотя при настоящем DDoS будет жопа в любом случае… поможет только наращивание фронтендов, бэкендов и каналов…
                        • UFO just landed and posted this here
                            0
                            он реально медленней/тяжелей на проксировании? Сам не пробовал, негде, но на вскидку то что он умеет purge cache по http запросу уже хорошо
                            • UFO just landed and posted this here
                          0
                          Спасибо за дополнение к моим TCP/IP конфигам :-)
                            +1
                            От хабраэффекта спасает nginx — проверено.

                            Недавно оставил ссылку в комментариях, так у меня в течении нескольких часов сервер лежал ))… пока не поставил nginx www.picamatic.com/view/4868857_ram_day/
                              0
                              «Недавно»? А на watermark'е написано «RUcenter 2006»…
                                0
                                )) забавно. График недавний. Может это у них типо копирайта? :)))
                              +1
                              А есть что нибудь для IIS?
                                +7
                                отдельная машинка c BSD и nginx как frontend
                                +2
                                vm.dirty_ratio = 1
                                vm.dirty_background_ratio = 1
                                vm.dirty_writeback_centisecs = 250
                                vm.dirty_expire_centisecs = 3000

                                это призвано сделать что?
                                  0
                                  Я что-то упустил. А для чего используется varnish c кешем, если в быстром лайти в самом есть кеширующий модуль?
                                    +1
                                    Лайти настолько быстрый, что пока он пытался прокэшировать под таким шквалом запросов, бекэнд умирал от запросов лайти =)
                                      0
                                      Ага, а varnish более умный хоть и более медленный как-то не грузит при этом бакенд или дело в том что varnish-ы сидят за балансером?
                                        0
                                        да все верно, а варниши сидят за лайти (что-бы не загнулись) + конечно распределение нагрузки, в текущей схеме, варниш ресурсы вообще не жрет ни на одном из хостовв =)
                                    0
                                    А как данная связка будет работать в случае если пользователь на сайте может залогиниться и контент ему будет отдаваться уникальный?
                                    Для каждого пользователя будет создавать отдельная страница с кешом?
                                    Или такой вариант этой схемой не предусмотрен?
                                      0
                                      Предусмотрен, в теории должно кэшировать в конфигурации по умолчанию, на практике надо тестить, возможно придется вносить изменеия в конфигурацию варниша.
                                        0
                                        То есть пониманием того, что пользователь залогинен и созданием для него закешированных данных занимается варниш? лайт занимается только быстрой отдачей этого кеша, и в теории ничто не помешает поменять его на nginx?
                                          0
                                          ничто не помешает боту залогиниться и продолжить флуд от авторизованного пользователя
                                            0
                                            А лучше понять, по какому признаку (читай: по какой куки) работает кэш, и флудить от «разных» пользователей, чтобы гарантированно положить бэкенд.
                                            +1
                                            Все верно.
                                        –1
                                        на самом деле одна из корневых защит от ддоса — быстрые веб-приложения и >1 сервера минимум. Т.е. если не почивать себя приятными мыслми, что «ну намана, 60-70% CPU на сервере юзается при обычной работе», а разгонять проект до минимум 200-300 req/s с одного ядра, то бится с ддосом уже будет много легче, чем если и так сервер еле тянет не проект.

                                        во-вторых моё мнение — при ddos бан просто _необходим_. Как минимум чтобы по тем IP кто в бан попал стопроцентно раскидать abuse по подсетям =).

                                        в-третьих перебор с нагрузкой по http надо обрабатывать не просто отрубалкой соединений, но и показом чего-нибудь злого на экран, типа «извините, сервера в данный момент перегружены» и блаблабла. По секрету говоря, у ддосеров такие сообщения (а они явно наблюдают за сайтом!) вызывают ощущение «защищённости» ресурса куда больше, чем просто timeout на соединение.

                                        в-четвертых, в комплекс все этих мер ОБЯЗАТЕЛЬНО надо делать шейпинг для ssh-трафика! Через htb/qdisc хотя бы.

                                        в-пятых, нужна «резиновость». Атака стоит денег и не имеет смысла если не приносит никакой выгоды. Именно поэтому любой проект изначально должен иметь возможность подключать сервера в кратчайшие сроки. Чтобы за 1 день, например, можно было бы взять 1-2 сервера за 1 день и быстро там все устаканить, удвоив стойкость. Тут удобны разные cloud хостеры, само собой. Конечно, это будет стоить денег, но охоту атаковать если она не приносит результатов вообще — это отбивает начисто.

                                        в-шестых, надо сесть и написать «антидос» на Си =)).
                                          0
                                          Уважаю за то что помог людям безвозмездно :) Мы — Русские! Должны помогать друг другу! (с) плюсик тебе.
                                            0
                                            lighttpd отлично, порт поменять прекрасно, оптимизация iptables замечательно,
                                            Varnish можно заменить Tinyproxy, ratproxy и т.п.
                                              0
                                              оно reverse умеет?
                                              0
                                              Небольшая поправка:
                                              mod_cache не скомпилирован если устанавливать из репозиториев ubuntu 12.04
                                              т.е. как я понимаю — единственный вариант, компилировать из исходников.

                                              Only users with full accounts can post comments. Log in, please.