Еще один вариант защиты от 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 если таковые используются и т.п.
На главную "Аппаратная виртуализация"
В правилах рерайта Вы просто проверяете наличие куки и автоматически считаете, что кука приехала правильная. А если неправильная - всё равно добро пожаловать? И параметр проверяется вместе с "=1". А если первая попытка была неудачной и приехало "=2"?
ОтветитьУдалитьда, давным давно это было))) на тот момент это работало и нас все устраивало.
УдалитьПривет!У меня установлен модуль, но /etc/nginx/proxy_headers.conf и прочего у меня нет, где это найти? Устанавливал в /usr/src/nginx-1.12.0 , debian 8
ОтветитьУдалитьПривет! Этому посту 5 лет скоро исполнится, весьма вероятно что за это время инструкции по установке давно изменились. Я бы порекомендовал следовать тем инструкциям что есть сейчас.
Удалить