31 марта 2009 г. Apache FreeBSD IPFW Nginx Безопасность

FreeBSD. Боремся с HTTP-флудом средствами IPFW

freebsd_shield

HTTP-flood - это массированная атака на веб-сервер, которая сопровождается посылкой огромного кол-ва http-запросов.

Три ступени борьбы с HTTP-флудом

  1. Активируем IPFW

  2. Пишем наш файерволл

  3. Пишем парсер логов и добавляем их в черный список

Активируем IPFW

Добавим в /etc/rc.conf

firewall_enable="YES"
firewall_script="/etc/fw.sh"
firewall_logging="YES"

Внимание! Не перезагружайтесь сразу же, так как получите бан по сети и не сможете влезть в ssh. Тем у кого есть физический доступ - это не касается естественно! :)

Пишем наш файерволл

Создаем наш файерволл в файл /etc/fw.sh

#!/bin/sh

FwCMD="/sbin/ipfw"

${FwCMD} -f flush
${FwCMD} 10 add check-state

${FwCMD} 40 add allow all from "table(1)" to me
cat /etc/fw_white_list | awk '{ if ($1!="") {system("ipfw table 1 add "$1"")} }' > /dev/null

${FwCMD} 50 add deny all from "table(2)" to me
cat /etc/fw_black_list | awk '{ if ($1!="") {system("ipfw table 2 add "$1"")} }' > /dev/null

${FwCMD} 100 add allow ip from any to any


с правами файлов надеюсь сами разберетесь...

В table(1) будет содержаться список IP, которые могут заходить в любом случае (для админов :).
В table(2) будет содержаться список IP, которым вы запрещаете заходить к вам.



Теперь потестим наши правила!

# ipfw show

Будет что-то типа того:

00040     0      0 allow ip from table(1) to me
00050     0      0 deny ip from table(2) to me

Если есть table(1) и table(2), то все в порядке, если нет, то попробуйте создать их вручную:

# ipfw 40 add allow all from "table(1)" to me
# ipfw 50 add deny all from "table(2)" to me



Тестим содержимое наших table

# ipfw table 1 list
# ipfw table 2 list

Должны быть списки вида:

111.111.111.111/32 0
222.222.222.222/32 0

Если ipfw не запускается, то используйте полный путь /sbin/ipfw.
Теперь можно и перезагрузить сервер, для применения настроек.

Пишем парсер логов и добавляем их в черный список

Я использую nginx для фронтенда, поэтому буду анализировать его access-логи, но вы можете сами написать регулярку для парсинга логов apache например...

Для nginx надо создать дополнительный файл access-лога "/var/log/nginx/access-ddos.log", просто мне так удобнее, но вы можете не заморачиваться, а использовать один лог. Мне просто удобно с двумя, один имеет в себе полный лог, а второй я порой подчищаю.

Создадим файл /etc/banip.sh, который будет собирать IP и добавлять их в бан!

#!/bin/sh

# зачищаем fw_black_list
echo '' > /etc/fw_black_list

# выберем с помощью регулярок IP, у которых нет Useg-Agent'а, отсортируем с уникальным ключом
cat /var/log/nginx/access-ddos.log | awk ' match($0, /(\d*.\d*.\d*.\d*).*"-" "-"/) {print $1}' | sort | uniq >> /etc/fw_black_list

# чистим лог
# echo '' > /var/log/nginx/access-ddos.log

# сбрасываем таблицу с забаненными IP
/sbin/ipfw table 2 flush

# добавляем в таблицу список забанненых IP
cat /etc/fw_black_list | awk '{ if ($1!="") {system("/sbin/ipfw table 2 add "$1"")} }' > /dev/null



Вот пример лога с http-флудом + нормальные запросы от пользователей (162.115.149.232)

111.111.111.111 - - [31/Mar/2009:14:00:00 +0000] "GET / HTTP/1.1" 304 0 "-" "-"
162.115.149.232 - - [31/Mar/2009:14:01:00 +0000] "GET / HTTP/1.1" 200 8960 "http://example.com./" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322)"
111.111.111.111 - - [31/Mar/2009:14:02:00 +0000] "GET / HTTP/1.1" 304 0 "-" "-"
222.222.222.222 - - [31/Mar/2009:14:03:00 +0000] "GET / HTTP/1.1" 304 0 "-" "-"

С помощью регулярки

/(\d*.\d*.\d*.\d*).*"-" "-"/

можно и без регулярки см. каммент

получим

111.111.111.111
111.111.111.111
222.222.222.222

после чего переведем поток и отсортируем уникальные IP в списке

sort | uniq

и получим в результате:

111.111.111.111
222.222.222.222

вот этот результат и пойдет в черный список!

Добавить в cron задание

Ставим в cron задание, пусть сам проверяет теперь и добавляет IP в бан.
С установкой времени думаю сами разберитесь, потому что зависит от конкретной ситуации.

0 * * * * /etc/banip.sh| mail -s"`uname -n` backup report" root

Шпаргалка по крону



P.S. Написал этот пост как можно проще, для того чтобы каждый мог легко разобраться с этой напастью и решить проблему. Рекомендую изучить или хотябы ознакомится с AWK прежде чем что-то менять, так же без регулярных выражений здесь никуда...

UPD: Забыл сказать, думаю назревает вопрос зачем нам "/etc/fw.sh"? Это для того чтобы перезагружать файерволл и изменять правила.

Комментарии

Прекольно

Прикольно? А что ава тогда грусная? :)

/d.d.d.d.*"-" "-"/

шо за кривая регулярка?

Вот так я и узнал, что у меня на серваке с надписью FreeBSD 7 на самом деле крутится FreeBSD 5.2.1, в которой ipfw ещё команду table не знает %)

Mabp, отличная регулярка, что работает то лучше не менять :P

Zhilinsky, будешь обновлять систему?

врешь ты все (с)
у тебя под нее вот такая строка пройдет как дети в школу
0000 text "-" "-"

И пройдет туда "0000", в чем проблема то? У меня IP пишет nginx, считаешь он может туда добавить "0000"?

Заключил выборку в круглые скобки, может ты поэтому паришься?

бля, напиши как человек
/\d+.\d+.\d+.\d+.*"-" "-"/

Мавр, так регулярка не будет работать, проверено. Почитай про AWK...

Неа, мне лень... Да и работает. Может ipfw обновлю.

Zhilinsky, ты просто список IP через запятую добавляешь чтоли?

Сейчас сделано несколько разрешающих и запрещающих правил на подсети, а я хотел прикрутить по такой схеме блокировку IP-адресов, с которых основной поток спама идёт, чтобы exim не обрабатывал этот мусор и не грузил машину.

херня какаято

Zhilinsky, можешь просто так же парсить, потом склеивать в одну строку через запятую и добавлять в правило... Есть конечно и ограничение, но не помню сколько именно...

CTAPbIu_MABP, попробуй потестить сам, если не веришь...

верю, читал, просто странно что там не pcre

Что-то мне подсказывает, что это обходится, но сформулировать алгоритм обхода я не могу :)

IP-адрес хранится в пакете, приходящем снаружи, а не определяется на месте. Следственно, его можно хитро подделать :-)

По крайней мере, меня так учили =)

dallone, от любого ддоса нельзя защитится на 100%, даже с железками.

А в моем алгоритме учитываются юзеры без User-Agent, если боты начнут засылать UA, то я поменяю алгоритм :)

Mabp, ты был прав, что-то странно в регулярке, парсер вордпресса съел бекслеши пере "d"

я ж говорил.
но перед точкой по идеи тоже надо она же за любой символ идет

я ж говорил.
я знаю что парсер бесится из-за плагина keywords... только пока не знаю что поправить.

но перед точкой по идеи тоже надо она же за любой символ идет

думаю так как нет за точкой литерала указывающего кол-во, то она и не срабатывает как маска.

но когда использую .\d*
то не работает...

литералы количество не указывают, его указывают квантификаторы (от слова quantity - количество)

угу, все время забываю это слово...

хммм.. странно...
Почему рег. /(\d.\d.\d.\d).*"-" "-"/ ?
А разве проблема сделать запрос 162.115.149.232 - - [31/Mar/2009:14:01:00 +0000] "GET / HTTP/1.1" 200 8960 "http://example.com./" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322)" ?
Что то не пому как он отличает ддос бота от человека = )))))

Макака, читайте внимательнее пост, я рассказывал про свою проблему и в следствии анлиза выяснилось что боты не передавали UA. И камменты.

да ты дарагой см не знаешь авк. и видимо не понимаешь как он работает))

В результате обсуждений с дудом в скайпе - awk, пришел к выводу что я awk знаю плохо...

if( $12=="-") { print $1; }

решит мою проблему без регулярки

кросавчеги :)

Вы еще не поняли, насколько это охуенная статья!
http://epsyl.ru/web/dos-xyu/

прогнал парсер через свой лог и не получил на выходе ничего, хотя посещалка на ресурсе порядка 200к уников в сутки. Решил телнетом запросить страницу, поскольку он никаких UA передавать не должен. В ответ получил странную картину:
[root@travka /usr/home/alp]# cat /var/log/httpd/xyy*x-access.log| awk ' match($0, /(\d.\d.\d.\d)."-" "-"/) {print $1}' | sort | uniq
Тут пустота. И тут же делаю выборку своего телнетного запроса:
[root@travka /usr/home/alp]# grep 188.
..99 /var/log/httpd/xyyandex-access.log
188.
.*.99 - - [13/Jul/2009:17:31:53 +0000] "GET / HTTP/1.0" 200 83576

Никаких "-" "-" в конце нет, странно.

Какой лог ты анализируешь? nginx'a и дефолтный, не перенастраивал?
покажи кусок лога, с ддосерами

Апач и нгинкс валят лог в один и тот же файл. В нгинксе указана такая строка:
access_log /var/log/httpd/xyyandex-access.log main;

Апач и нгинкс валят лог в один и тот же файл. В нгинксе указана такая строка:

так не должно быть? ты для себя так настроил? в чем удобство?

access_log /var/log/httpd/xyyandex-access.log main;

ты настроил фильтр main прежде чем его юзать? при ребуте нгинкса ошибки есть?

Удобство в остутсвии стопицот мильёнов логов и присутствии всего одного.
Я думал, что main, это некий пресет, который уже настроен определённым образом.

alpha_Qu4z4r, ну и оставь один от nginx :)
Мой анализатор работает только с логом нгинска, не перенастроенным...
Если хочешь и свой чудо-лог оставить, то создай еще один только для nginx и юзай его в анализаторе!

крут. и вобще все на этом блоге круто =) так держать

Ага, спасибо :)

у меня заработало с одним равно

if( $12="-") { print $1; }

Илья, вы уверены что результат верный? Просто в основном, при

$12 = "-"
будет выполняться присваивание, и "if" получит от операции - TRUE

А в моем алгоритме учитываются юзеры без User-Agent, если боты начнут засылать UA, то я поменяю алгоритм :)

Интересно какой же алгоритм вы будете использовать? Кроме количества запросов или количества соединений за установленный интервал времени, по которым можно отличить легитимный пользователь или нет в голову ничего не приходит.
Реально очень интересно ваше мнение.

Ну "количества запросов или количества соединений за установленный интервал времени" - это я и подразумевал видимо когда писал пост. Но как вариант можно использовать куки, к примеру.

У ботов тоже бывает UA. Можете сделать скрипт по принципу: если посетитель совершает 200 запросов в минуту, то его забанить.

Так слишком много IP попадает нормалных,даже с моего домашнего ПК IP попал в список забаненных

Владимир, ну и лох :)

Так слишком много IP попадает нормалных,даже с моего домашнего ПК IP попал в список забаненных

А еще системным администратором себя называешь:) для начала бы научился использовать чужие скрипты и допиливать до своего=) а потом уж гордо писал что ты мегатру(успешный системный одминестратор).
У автора блога извиняюсь за срачь и спасибо за заметку:-), нужен был пример использования таблиц в ipfw Мигрирую с iptables+ipset на ipfw+freebsd :)

Можно было оставаться на Linux, а досеров банить не фаерволом, а немного не традиционным способом - через

ip route add blackhole ${ip}/32

Меня такой вариант просто выручил, когда начали досить сайт на хостинге под OpenVZ, где iptables урезанный, а к ядру не подобраться... :) Но суть та же самая, по крону дергается скрипт, парсит логи nginx, и по результатам парсинга выполянет команду добавления маршрута для конкретного ip. Вот только мне очень интересно, насколько "резиновая" таблица маршрутизации у этого OpenVZ линукса :) Сейчас в ней порядка 13000 записей уже накопилось! Машинка пока "резво" откликается по ssh и по веб.

Есть в FreeBSD пакет bruteblock.
Можно его настроить на работу с nginx.
Замечательно работает.

Оставьте свой комментарий

Markdown