Nginx защита от DDoS

Дата: 11.04.2019Метки:

В данной заметке речь пойдет о защите веб-сервера от DDoS. Наиболее часто для нарушения работы сайта злоумышленники используют HTTP-флуд. Для защиты от HTTP-флуда я буду использовать связку из Nginx и модуля nginx-module-testcookie. Все действия производились под CentOS 7, но данная заметка будет актуальна и для других систем за исключением этапа установки пакетов.

Для начала необходимо добавить репозиторий rpms.southbridge.ru в систему. Для этого выполните команду:

rpm -Uhv http://rpms.southbridge.ru/southbridge-rhel7-stable.rpm

Далее стандартной командой устанавливаем пакеты:

yum install nginx nginx-module-testcookie

С учетом того, что Nginx я обновляю достаточно редко, по ряду причин на постоянной основе я не хочу использовать выше указанный репозиторий, то я выполню установку непосредственно из скачанных rpm-пакетов:

wget http://rpms.southbridge.ru/rhel7/stable/x86_64/nginx-1.9.5-1.el7.centos.ngx.x86_64.rpm
wget http://rpms.southbridge.ru/rhel7/stable/x86_64/nginx-module-testcookie-1.15.6.1.24-1.el7.ngx.x86_64.rpm

Если в системе ранее уже был установлен Nginx, нужно его удалить. Устанавливаем Nginx и nginx-module-testcookie:

rpm -Uhv nginx-1.15.6-1.el7.ngx.x86_64.rpm
rpm -Uhv nginx-module-testcookie-1.15.6.1.24-1.el7.ngx.x86_64.rpm

Чтобы включить модуль добавьте строку ниже в файл /etc/nginx/nginx.conf:

Для включение и отключения testcookie нам необходимо периодически просматривать статистику подключений. Для этого добавьте в конфиг:

server {
    listen 80;
    server_name localhost;

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}

Применяем настройки, проверяем:

# service nginx restart
# curl http://localhost/nginx_status
Active connections: 4
server accepts handled requests
 137 137 190
Reading: 0 Writing: 1 Waiting: 3

Берем файл https://github.com/duy13/vDDoS-Protection/blob/master/aes.min.js.txt и копируем его в отдельный каталог на сервере /www/public_html/aes.min.js

Добавляем в конфиг вашего сайта следующие строки:

location = /aes.min.js {
    gzip on;
    gzip_min_length 1000;
    gzip_types text/plain;
    gzip_static on;
    root /www/public_html;
}

Сохраняем настройки:

nginx reload

Проверяем, что файл доступен по адресу http://site.ru/aes.min.js

Добавляем в конфиг вашего сайта параметры по примеру ниже:

http {
    testcookie off;
    testcookie_name RCPC;
    testcookie_secret nlos7m5hYc6gNmm7NvhCZJ8W7lJHuAaV2dp73px9diYMYzKmhvrpjZLNaoK989wW;
    testcookie_session $remote_addr;
    testcookie_arg attempt;
    testcookie_max_attempts 3;
    testcookie_get_only on;
    testcookie_redirect_via_refresh on;
    testcookie_refresh_encrypt_cookie on;
    testcookie_refresh_encrypt_cookie_key ea5891045bdfddd19682146f1e88e6e8;
    testcookie_refresh_encrypt_cookie_iv ea5891045bdfddd19682146f1e88e6e8;
    testcookie_refresh_template '<html><body>setting cookie...<script type=\"text/javascript\" src=\"/aes.min.js\" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("$testcookie_enc_key"),b=toNumbers("$testcookie_enc_iv"),c=toNumbers("$testcookie_enc_set");document.cookie="RCPC="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/";document.location.href="$testcookie_nexturl";</script></body></html>';



 server {
        listen 80;
        server_name test.com;

        location = /aes.min.js {
            gzip on;
            gzip_min_length 1000;
            gzip_types text/plain;
            root /var/www/public_html;
        }
        
        #Если используется проксирование, в противном случае не использовать
        location / {
            testcookie off;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:80;
        }
    }

Параметр testcookie_secret заполняем случайным набором из 64 символов. Для testcookie_refresh_encrypt_cookie_key и testcookie_refresh_encrypt_cookie_iv генерируем hex число длиной 32 символа этим генератором.

Теперь осталось настроить скрипт, который будет автоматически проверять количество подключений к Nginx и в случае превышения лимита, автоматически включать testcookie и атпровлять уведомление администратору на почту.

#!/bin/sh

NGINX_CONF=/etc/nginx/conf.d/default.conf
MAIL="admin@site.ru"
MAILTO=root
LA_ACTIVATE=17
LA_DEACTIVATE=5
NGINX_CONNECT=1000

TMPLOG=/tmp/testcookie_module.tmp
NGINXCONN=`curl -s http://localhost/nginx-status | grep "Active" | awk '{print($3)}'`
LA=`cat /proc/loadavg | awk -F '.' '{print($1)}'`

function e {
    echo -en $(date "+%F %T"): "$1"
}

if [ ! -f $TMPLOG ];then echo 0 > $TMPLOG; fi
LASTRESULT=`cat $TMPLOG`

if [ -n "$NGINXCONN" ]; then
  if [ $NGINXCONN -gt $NGINX_CONNECT ]; then
    ALERT1="1"
  fi
fi

if [ -n "$LA" ]; then
  if [ $LA -gt $LA_ACTIVATE ]; then
    ALERT2="1"
  fi
fi

ALERT=$ALERT1$ALERT2

if [ -n "$ALERT" -a $LASTRESULT -eq 0 ]; then
    e; printf "Nginx connect: %-4s LA: %-3s | Activate testcookie\n" "$NGINXCONN" "$LA"
    sed -i 's/.*##-AUTO-DDOS-LABEL-##/\ttestcookie on; ##-AUTO-DDOS-LABEL-##/g' $NGINX_CONF
    /sbin/service nginx reload >/dev/null 2>&1
    echo 1 > $TMPLOG
    if [ "$MAIL" = "true" ];then
        echo "Nginx connect $NGINXCONN, LA $LA. Nginx test-cookie enable" | mail -s "`hostname` DDOS detected. Nginx test-cookie enable" $MAILTO
    fi
fi

if [ $LA -le $LA_DEACTIVATE -a $LASTRESULT -eq 1 ]; then
    e; printf "Nginx connect: %-4s LA: %-3s | Dectivate testcookie\n" "$NGINXCONN" "$LA"
    sed -i 's/.*##-AUTO-DDOS-LABEL-##/\ttestcookie off; ##-AUTO-DDOS-LABEL-##/g' $NGINX_CONF
    /sbin/service nginx reload >/dev/null 2>&1
    echo 0 > $TMPLOG
fi

Добавляем задание в cron:

* * * * *      /etc/nginx/check.sh >/dev/null 2>&1