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

Discussion in 'AntiDDos - АнтиДДОС' started by ph4nt0m, 20 Sep 2009.

  1. ph4nt0m

    ph4nt0m Member

    Joined:
    10 Aug 2009
    Messages:
    11
    Likes Received:
    15
    Reputations:
    0
    Защищаемся от 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) приблизительно к такому виду:

    Code:
    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 отсюда: http://code.google.com/p/lighttpd-improved/ , но вам никто не мешает скачать его из репозиториев дистрибутива (apt-get install lighttpd).
    И правим конфиг до тех пор, пока он не примет следующий вид:

    Code:
    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:

    Code:
    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
    Увеличиваем количество открытых файлов:
    Code:
    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, если в кэше данного контента нет или он устарел.

    (c) http://habrahabr.ru/blogs/infosecurity/68301/

    ==========================
    Статья честно взята с хабрахабр, интересная своим решением и вроде на ачате её нет...
    \\надеюсь поместил в тот раздел\\
     
    4 people like this.
  2. ghostwizard

    ghostwizard Member

    Joined:
    4 Dec 2005
    Messages:
    127
    Likes Received:
    36
    Reputations:
    21
    Хорошая статья. С varnish не работал, но возник вопрос о плюсах и минусах lighttpd, если сравнивать с nginx. Также если можно в двух словах рассказать о возможностях loadbalance для lighttpd (определение, что сервер "упал", автозамена упавшего сервера другим из списка запасных). Это к тому, насколько оправдывает всю мощь lighttpd loadbalance против скажем специально-заточенного под это haproxy.
     
  3. Root-access

    Root-access Elder - Старейшина

    Joined:
    18 Jun 2008
    Messages:
    193
    Likes Received:
    195
    Reputations:
    91

    Loadbalance для php в fastcgi, к примеру, можно сделать так:

    Code:
    fastcgi.server = ( ".php" => 
      (
        ( "host" => "первый_сервер",
          "port" => 1030
        ),
        ( "host" => "второй_сервер",
          "port" => 1030 )
        )
      )
    
    Параметр host здесь - это ip-адрес бэкэнда с fastcgi.

    Полную документацию можно найти здесь.