Страницы

Сохранить статью у себя в соцсети:

четверг, 21 июня 2012 г.

§ Nginx+Testcookie: yet another L7 firewall.

Еще один вариант защиты от DDoS посредством nginx­-модуля testcookie.

Существует сторонний nginx­-модуль testcookie-nginx-module.
Модуль умеет ставить cookies стандартным способом через HTTP-заголовок Set-Cookie, после установки перенаправляет пользователя, используя код ответа 301 и заголовок Location или используя код ответа 200 и HTML тег Meta «refresh». Он также может считать количество попыток поставить cookies и отправлять пользователя по заданному URL после превышения максимального количества неудачных попыток. 
Вариант реализации:
После установки cookie делается редирект на текущий url с параметром label_XYZ=1, если после редиректа запрос пришел без cookie, делается повторный редирект на текущий url с параметром label_XYZ=2 и так 3 раза. При четвертом обращении без cookie отдается ответ 403.

Можно конечно использовать limit_req/limit_zone, но есть небольшие минусы:
- усложненная реализация белых списков;
- упрощенные методы отлова и распознания "злодеев";
Но и у testcookie также есть свои минус, он является сторонним модулем и не идет в официальном дистрибутиве (в Gentoo это не проблема, существующий ebuild был переписан в течение часа).

Сборку модуля в nginx я пропускаю, т.к. для каждого дистрибутива и пакетного менеджера это отдельная история. Переходим к настройке.
Основная настройка сводится к правке nginx.conf, где особое внимание уделяется параметрам testcookie_secret и testcookie_arg. Генерируем значения самостоятельно. Входные строки можно брать любые, главное получить на выходе славный набор символов.
# echo label |md5sum 
41cc0e4945e162021cfdd993f4c1104d  -
# echo defcon |md5sum 
15dbedaeabc7f0c09f0fe834e4a3b46a  -

Полученные значения подставляем в качестве аргументов для testcookie_secret и testcookie_arg.
значение для testcookie_name также можно установить произвольное.
# vi /etc/nginx/nginx.conf
http {
        ...
        testcookie off;
        testcookie_name XSAE;
        testcookie_secret 15dbedaeabc7f0c09f0fe834e4a3b46a;
        testcookie_session $remote_addr;
        testcookie_arg label_41cc0e4945e162021cfdd993f4c1104d;
        testcookie_max_attempts 3;
        testcookie_get_only on;
        testcookie_internal off;
        # используем белые списки
        include /etc/nginx/testcookie_whitelist.conf;
        ...
}

Создаем движок rewrite'ов в­ /etc/nginx/testcookie.conf
Cуть в следующем, необходимо делать реврайт конечного урла, не делать это так чтобы не светился параметр редиректа (?label_*)
# vi /etc/nginx/testcookie.conf
set $do_rewrite 0;
# проверяем наличие выданной куки
if ($http_cookie ~ "XSAE=[0-9a-f]+") {
        set $do_rewrite 1;
}
# если в аргументах присуствует метка выданная модулем, выкусываем её
if ($args ~ "^((.*)(label_41cc0e4945e162021cfdd993f4c1104d=1&|&label_41cc0e4945e162021cfdd993f4c1104d=1(.*))|label_41cc0e4945e162021cfdd993f4c1104d=1)$") {
        set $target "$2$4";
        set $do_rewrite "1${do_rewrite}";
}
# делаем правильный реврайт, подставляем только урл и другие возможные параметры (без метки)
if ($do_rewrite = 11) {
        set $args $target;
        rewrite ^.*$ http://$host$uri break;
}

Белый список (для краткости сократил до двух адресов)
# vi /etc/nginx/testcookie_whitelist.conf
testcookie_whitelist {
# Yandex
77.88.0.0/18;
87.250.224.0/19;
}

изменения в /etc/nginx/proxy_headers.conf
# vi /etc/nginx/proxy_headers.conf
testcookie         on;
proxy_redirect     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;

Затем в каждый из конфигурационных файлов виртуальных хостов прописываем в начало секции server. Таким образом в каждом виртуальном хосте мы подгружаем rewrite-движок, и белые списки. Сам механизм testcookie будет работать в случаях когда запросы проксируются на бэкенды и подгружается proxy_headers.conf
server {
        ...
        include /etc/nginx/testcookie.conf;
        include /etc/nginx/testcookie_whitelist.conf;
        ...
# пример локейшена
location / {
include /etc/nginx/proxy_headers.conf;
        proxy_pass http://upstream;
}

Перезапускаем.
# nginx -t -c /etc/nginx/nginx.conf
# /etc/init.d/nginx restart

Каким образом проверить? Выбираем url который точно отдастся в бэкенд и отдаем его curl или wget
# curl -s -L -I http://serversite.dom/link
# wget --no-cookie http://serversite.dom/link

Ниже пример проверки рабочей конфигурации:
Видим что нам дают редирект и выдают куку, но мы игнорируем куку и на третью попытку получаем 403 Forbidden.

Как мониторить и в случае чего забанить? Используем скрипт в кроне (перезапуск iptables для того если мы баним iptables'ом злодеев)
# crontab -e
*/5 * * * * /root/bin/testcookie-search.sh

Сам скрипт
#!/bin/sh
NGINX_LOG="/var/log/nginx/acceess.ru-access_log"
DEBUG_LOG="/var/log/testcookie/$(date +"%Y%m%d-%H:%M").log"
LABEL="label_41cc0e4945e162021cfdd993f4c1104d=3 .* 403"
MAILTO="mail@mail.com"
ALL="/tmp/all"
BORED="/tmp/bored"
THRESHOLD=15
if [ ! -f /tmp/offset ]; 
 then
   echo "offset file not found. create it. restart script after 1 minute."
   stat -c '%s' "$NGINX_LOG" > /tmp/offset; exit
fi
echo "" > $ALL
if $(stat -c '%s' "$NGINX_LOG") -lt $(cat /tmp/offset) 2>/dev/null
 then echo 0 > /tmp/offset
fi
tail -c +$(cat /tmp/offset) "$NGINX_LOG" |grep -E "$LABEL" |cut -d' ' -f1 |sort |uniq -c |sort -nk1 > $ALL
stat -c '%s' "$NGINX_LOG" > /tmp/offset
if [ ! -z "$ALL" ]; then
  while read reqs addr; do
    if [ $reqs -ge $THRESHOLD ]; then 
#      /sbin/iptables -I INPUT -s $addr -p tcp -m tcp --dport 80 -j DROP
#      /sbin/iptables -I INPUT -s $addr -p tcp -m tcp --dport 80 -m limit --limit 6/min --limit-burst 10 -j ACCEPT
      echo $reqs $addr >> $BORED
    fi
  done < $ALL
  if [ ! -z $BORED ]; then echo "Suspicious ip-addresses. $(cat $BORED)" | mail $MAILTO ; fi
  rm "$BORED" &> /dev/null
fi

В скрипте закоментированы строки которые позволяют сходу добавлять "злодеев" в iptables. Скрипт парсит лог nginx'а за последние 5 минут, на предмет наличия шаблонной строки, по которой можно определить что клиенту выдали 403. В ходе формируется список с такими клиентами, и в самом конце формируется письмо содержащее список злодеев превысивших планку определенную в THRESHOLD и отправляется на указанную в MAILTO почту.

Вот собственно и все. Важно следить и своевременно обновлять whitelist'ы чтобы туда не попадали поисковые боты, площадки CDN если таковые используются и т.п.

На главную "Аппаратная виртуализация"

4 комментария:

  1. В правилах рерайта Вы просто проверяете наличие куки и автоматически считаете, что кука приехала правильная. А если неправильная - всё равно добро пожаловать? И параметр проверяется вместе с "=1". А если первая попытка была неудачной и приехало "=2"?

    ОтветитьУдалить
    Ответы
    1. да, давным давно это было))) на тот момент это работало и нас все устраивало.

      Удалить
  2. Привет!У меня установлен модуль, но /etc/nginx/proxy_headers.conf и прочего у меня нет, где это найти? Устанавливал в /usr/src/nginx-1.12.0 , debian 8

    ОтветитьУдалить
    Ответы
    1. Привет! Этому посту 5 лет скоро исполнится, весьма вероятно что за это время инструкции по установке давно изменились. Я бы порекомендовал следовать тем инструкциям что есть сейчас.

      Удалить

Популярные сообщения

Профиль в Google+ Яндекс цитирования Яндекс.Метрика