Однажды мой знакомый написал мне, что его сайт перестал открываться сказал, что на сайт обрушилась DDoS атака.
Полез я разбираться, в чем дело и в итоге суть проблемы была найдена - куча ip
адресов с различными User Agent'ами приходили на сайт и делали очень много различных запросов...
В общем нужно было этих ботов отлавливать и блокировать им доступ к сайту, сперва подумал об iptables
и ConfigServer Firewall
(csf
), но хостинг был внутри OpenVZ,
а это означало невозможность использования более 120
правил, а так же невозможность установки модулей ядра и вообще каких либо изменений опций ядра.
Потому стандартные подходы для решения проблемы сразу отпадали.
А время шло, и переписываться с тех поддежкой хостинга небыло времени, а платить огромные суммы за защиту от такой примитивной атаки небыло никакого желания.
Решение пришло в голову неожиданно, при перечитывании man ip
, вспомнить зачем я его читал затрудняюсь, но решение было на редкость извращенским, но вполне рабочим :)
Решением было добавление маршрута проблемного IP
в blackhole
. В следствие чего пакеты от этих адресов будут молча отбрасываться (the rule prescribes to silently drop the packet.).
Сперва конфигурируем nginx
:
...
worker_rlimit_nofile 200000;
events {
worker_connections 1024;
use epoll;
}
....
http {
index index.html index.htm index.php;
##
# Basic Settings
##
sendfile on;
send_timeout 5;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30 15;
types_hash_max_size 2048;
server_tokens off;
client_header_timeout 15;
client_body_timeout 15;
# лимитируем для зоны one 10 конектов в 1 сек.
limit_req_zone $binary_remote_addr zone=one:16m rate=10r/s;
# В ситуации когда сервер записал в сокет данные, но клиент не хочет
# их забирать, после таймаута по закрытию соединения в ядре
# данные будут держаться еще несколько минут. В nginx если директи ва
# для принудительного сброса всех данных после закрытия по таймаут у.
reset_timedout_connection on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging Settings
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
...
server {
listen 80;
server_name my.site;
open_file_cache max=100000 inactive=40s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# это значит что законектится с лимитом в 3 подключения за 1 сек можно 3 раза, а дальше 503 ошибка. Что и пишется в лог access.
limit_req zone=one burst=10;
# Default locations config.
include conf.std;
...
}
...
}
Файлик /etc/nginx/conf.std
:
####### STANDART
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# Deny access to .htaccess files, if Apache's document root concurs with nginx's one
location ~ /\.(ht|hg|git|svn) { deny all; }
location ~ /\< { deny all; }
С конфигурацией nginx
все, теперь скрипты.
Добавляем в файл /etc/crontab
строки:
# parse nginx logs and ban bad ip via nullroute
* * * * * root /root/ddos/parse_nginx.log.sh >/root/ddos/parse_nginx.log.log 2>&1
Содержимое скрипта /root/ddos/parse_nginx.log.sh
:
#!/bin/sh
ADMINS_IP='ip.ip.ip.ip'
echo $(date)
echo '--- запускаем систему парсинга nginx лога---'
echo ' ищем ботов'
cat /var/log/nginx/access.log \
| grep -E -e 'HTTP/1.(0|1)" (400|403|405|499|503)' -e '] "-" 400 0 "-" "-"' \
| awk '{print $1}' \
| sort -nr | uniq -c \
| awk '{if($1>10)print $1" "$2}' \
> /root/ddos/banlist.txt
cat /var/log/nginx/error.log \
| grep -E '(limiting requests|limiting connections)' \
| awk -F"client: " '{print $2}' \
| awk -F"," '{print $1}' \
| sort -nr | uniq -c \
| awk '{if($1>10)print $1" "$2}' \
>> /root/ddos/banlist.txt
# get unique ip
cat /root/ddos/banlist.txt \
| grep -v $ADMINS_IP \
| uniq | sort -nr \
> /root/ddos/banlist_uniq.txt
echo '------ очищаем tmp file бана-'
cat /dev/null > /root/ddos/banlist.txt
echo ' создаем DROP правила для 50 самых агрессивных ботов'
awk '{print $2}' /root/ddos/banlist_uniq.txt | uniq | head -n 150 > /root/ddos/banlist.txt
#т.к. iptables полнейшее УГ, особенно внутри OpenVZ, баним ip вот таким извращенским методом... через nullroute
#ip route flush type blackhole
for ip in $(cat /root/ddos/banlist.txt); do
ip route add blackhole ${ip}/32
done
#echo 'записываем злобных ботов в csf.deny'
#cat /etc/csf/csf.deny >> /root/ddos/banlist.txt
#cat /root/ddos/banlist.txt | uniq | sort -nr > /etc/csf/csf.deny
#echo 'csf релоад, внесение в iptables ботов'
#/usr/sbin/csf -r
sleep 5
echo '-- делаем ротацию лога--------'
test -x /usr/sbin/logrotate || exit 0
/usr/sbin/logrotate /etc/logrotate.conf
echo '=====злобные боты в списке бана====='
sleep 1
Для мониторинга состояния сервера используем такой скрипт:
#!/bin/sh
while true; do
netstat_str=$(netstat -an)
echo -n 'SYN_RECV: '
echo "$netstat_str" | grep 80 | grep -c SYN_RECV
echo -n 'TIME_WAIT: '
echo "$netstat_str" | grep 80 | grep -c TIME_WAIT
echo -n 'FIN_WAIT: '
echo "$netstat_str" | grep 80 | grep -c FIN_WAI1
echo -n 'ESTABLISHED: '
echo "$netstat_str" | grep 80 | grep -c ESTABLISHED
echo -n 'BLOCKED_IP to Black-route: '
ip route list | grep -c blackhole
sleep 2
echo '------------------------- for stop this script Press Ctrl+C'
done
Еще понадобится настроить ротацию логов, приводим файл /etc/logrotate.d/nginx
к такому виду:
/var/log/nginx/*.log {
size 1M
missingok
rotate 52
compress
delaycompress
notifempty
create 640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
Поменялся метод ротации вместо daily
теперь используется size 1M
, это позволит уменьшить объем лог файла,
т.к. за сутки он может очень сильно вырасти в объеме, что замедлит парсинг и увеличит нагрузку на сервер,
а использование ротации по размеру исправит эту ситуацию.
Еще я использовал встроенные средства iptables
для борьбы с DDoS:
#!/bin/sh
iptables -F
iptables -N syn_flood
iptables -A INPUT -p tcp --syn -j syn_flood
iptables -A syn_flood -m limit --limit 100/s --limit-burst 150 -j RETURN
iptables -A syn_flood -j DROP
Данный скрипт спокойно блокировал DDoS примерно в 10 000 ботов, при этом сайт был полностью доступен и атака вообще не чувствовалась.
Прошу обратить внимание на то, что скрипт запускается раз в минуту, и блокирует ip
адреса по заданным критериям в скрипте /root/ddos/parse_nginx.log.sh
.
В моем случае атака была "вялой" и одной минуты вполне хватало для сбора ip
адресов ботов.
В первые минуты сайт "туго" работал, но спустя минут 10, когда список заблокированных вырос сайт начал свою штатную работу,
а список блокированных с течением времени увеличивался все медленнее и в итоге совсем перестал расти - у атакующего закончились боты.
Comments
comments powered by Disqus