Это старая версия документа!
Простой DDoS анализ через CLI
Проблема
Исходные данные : канал 10Гбит, периодическая мощная DDoS атака на один из ip в сети приводит к деградации сервиса.
График DDoS атаки далее, видно, что мощность DDoS атаки в сумме с текущим трафиком превышает емкость канала.
Решение
Так как быстро расширить канал и увеличить мощность DPI не представлялось возможным, выбрали следующий путь: 1. вычисляем список IP которые подверглись DDoS 2. переводим IP в null route ( в blackhole )
Настройка
- создаем директорию /home/ddos_check
- устанавливаем ipfixreceiver2 на сервер, где будем собирать данные полный netflow используем следующую конфигурацию файл ipfixreceiverflow2.ini, не требуется гарантированная доставка поэтому используем UDP транспорт
[connect] #protocol=tcp protocol=udp host=0.0.0.0 port=1599 [dump] rotate_minutes=1 processcmd=/home/ddos_check/rcflowprocess %%s dumpfiledir=/home/ddos_check/flow/ [InfoModel] XMLElements = /etc/rcollector/xml/raw_flow.xml [Template] Elements = octetDeltaCount, packetDeltaCount, protocolIdentifier, ipClassOfService, sourceTransportPort, sourceIPv4Address, sourceIPv6Address, destinationTransportPort, destinationIPv4Address, destinationIPv6Address, bgpSourceAsNumber, bgpDestinationAsNumber, flowStartMilliseconds, flowEndMilliseconds, ingressInterface, egressInterface, ipVersion, session_id, host_cn, DPI_PROTOCOL, login, postNATSourceIPv4Address, postNAPTSourceTransportPort, frgmt_delta_packs, repeat_delta_pack, packet_deliver_time [ExportModel] Elements = session_id, octetDeltaCount, protocolIdentifier, DPI_PROTOCOL, sourceTransportPort, sourceIPv4Address : decode_unsigned, destinationTransportPort, destinationIPv4Address : decode_unsigned, bgpSourceAsNumber, bgpDestinationAsNumber, flowStartMilliseconds : decode_unsigned, flowEndMilliseconds : decode_unsigned, login, postNATSourceIPv4Address : decode_unsigned, postNAPTSourceTransportPort, packetDeltaCount, sourceIPv6Address, destinationIPv6Address [logging] loggers.root.level = information loggers.root.channel = fileChannel channels.fileChannel.class = FileChannel channels.fileChannel.path = /var/log/ipfixreceiverflow2.log channels.fileChannel.rotation = 1 M channels.fileChannel.archive = timestamp channels.fileChannel.purgeCount = 5 channels.fileChannel.formatter.class = PatternFormatter channels.fileChannel.formatter.pattern = %Y-%m-%d %H:%M:%S.%i [%P] %p %s - %t channels.fileChannel.formatter.times = local
сохраняем файл в /home/ddos_check
- вносим в iptables разрешение для порта 1599 - UDP, запускаем ipfixreceiver.
ipfixreceiver2 --daemon --umask=000 --pidfile=/var/run/ipfixreceiver.1599.pid -f /home/ddos_check/ipfixreceiverflow2.ini #проверяем, что порт процесс слушает netstat -anpl | grep 1599 udp 124968 0 0.0.0.0:1599 0.0.0.0:* 21820/ipfixreceiver
- создаем файл обработки потока /home/ddos_check/rcflowprocess
#!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/home/ddos_check export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib:/usr/local/lib gzip $1 echo "DDoS statistics" > /home/ddos_check/lastminute.txt date >> /home/ddos_check/lastminute.txt echo -e "\nSessions TOP20\t ip" >> /home/ddos_check/lastminute.txt /home/ddos_check/topcnt $1.gz >> /home/ddos_check/lastminute.txt echo -e "\n\nSummary bytes Only Destination TOP20\nsize(Mb) \t ip" >> /home/ddos_check/lastminute.txt /home/ddos_check/topsize $1.gz 2 | sort -n -r| head -20 | awk '{print $1/1024/1024 " " $2'} >> /home/ddos_checklastminute.txt echo -e "\n\nSummary bytes Only src IP TOP20\nsize(Mb) \t ip" >> /home/ddos_check/lastminute.txt /home/ddos_check/topsize $1.gz 1 | sort -n -r| head -20 | awk '{print $1/1024/1024 " " $2'} >> /home/ddos_check/lastminute.txt echo -e "\n\nSummary bytes by src+dsc TOP20\nsize(Mb)\tip" >> /home/ddos_check/lastminute.txt /home/ddos_check/topsize $1.gz 0 | sort -n -r| head -20 | awk '{print $1/1024/1024 " " $2'} >> /home/ddos_check/lastminute.txt mv -f /home/ddos_check/lastminute.txt /var/www/html/ddos_check/lastminute.txt chown apache /var/www/html/ddos_check/lastminute.txt chmod a+w /var/www/html/ddos_check/lastminute.txt chcon -v --type=httpd_sys_content_t /var/www/html/ddos_check/lastminute.txt
не забываем сделать chmod a+x /home/ddos_check/rcflowprocess
- создаем скрипт расчета к-ва сессий top-20 /home/ddos_check/topcnt
function getipv4() { s=`echo "obase=16; " $1 | bc | sed 's/../0x& /g'` ip=`printf '%d.%d.%d.%d' $s` echo -n $ip } zcat $1 | awk -F '\t' '{print $6 "\n" $8}' | sort | uniq -c -d | sort -n -r > tmp$$$ head -20 tmp$$$ > tmp2$$$ echo -e "ip\t\thits" while read p; do cnt=`echo $p | awk '{print $1}'` ipd=`echo $p | awk '{print $2}'` size=`/home/volja/cntsums $1 $ipd | awk '{print $2/1024/1024}'` getipv4 $ipd; echo -n -e "\t" $cnt; echo -e "\t s=" $size "(Mb)\t(" $ipd ")" done < tmp2$$$ rm -f tmp$$$ tmp2$$$
не забываем сделать chmod a+x /home/ddos_check/topcnt
- устанавливаем bc
yum -y install bc
- создаем скрипт /home/ddos_check/cntsums
zcat $1 | grep $2 | awk '{sum += $2; sum2 += $3} END {print "octets= " sum "\tpackets= " sum2}'
не забываем сделать chmod a+x /home/ddos_check/cntsums
- создаем скрипт ТОП-20 по максимальному объему трафика /home/ddos_check/topsize
#!/usr/bin/python # usage: # by source ip #./topsize arch/attak1/url_05102017_152100.dump.gz 1 | sort -n -r| head -20 | awk '{print $1/1024/1024 " " $2'} # by destination ip #./topsize arch/attak1/url_05102017_152100.dump.gz 2 | sort -n -r| head -20 | awk '{print $1/1024/1024 " " $2'} # import sys, os, logging, ConfigParser, gzip def main(): delim='\t' accumulater={} for line in openinfile(sys.argv[1]): acc=long(0) fields = line.rstrip('\n').split(delim) if(sys.argv[2]=="0" or sys.argv[2]=="1"): # by src try: acc=accumulater[fields[5]] accumulater[fields[5]]=acc+long(fields[1]) except KeyError: accumulater[fields[5]]=long(fields[1]) if(sys.argv[2]=="0" or sys.argv[2]=="2"): #by dsc try: acc=accumulater[fields[7]] accumulater[fields[7]]=acc+long(fields[1]) except KeyError: accumulater[fields[7]]=long(fields[1]) for key, value in accumulater.iteritems(): print str(value)+' '+ipv4str(long(key)) # open input file def openinfile(filename): if filename is None: inf = sys.stdin logging.debug("input file: stdin") else: if ".gz" in filename: inf = gzip.open(filename, "rb") else: inf = open(filename, "rb") logging.debug("input file: " + filename) return inf def ipv4str(ipv4): return str((ipv4 >> 24) & 0xFF) + '.' + str((ipv4 >> 16) & 0xFF) + '.' + str((ipv4 >> 8) & 0xFF) + '.' + str((ipv4 & 0xFF)) if __name__ == "__main__": main()
не забываем сделать chmod a+x /home/ddos_check/topsize
- добавляем строку в /var/spool/cron/root что бы через сутки данные flow удалялись
15 4 * * * /bin/find /home/ddos_check/flow/ -name url_\*.dump.gz -cmin +1440 -delete > /dev/null 2>&1
- устанавливаем параметры на DPI
netflow=8 netflow_full_collector_type=1 netflow_dev=eth2 netflow_timeout=10 #!!!здесь укажите ip адрес вашего приемника netflow_full_collector=127.0.0.1:1599 netflow_passive_timeout=20 netflow_active_timeout=60
данная настройка требует рестарта
Работа со скриптами
После перезагрузки fastdpi если все сконфигурировали правильно ipfixreceiver начнет принимать данные в директорию /home/ddos_check/flow, после появления *.gz файлов (1 раз в минуту интервал ротации был установлен в приемнике) проводим проверку скриптов.
cd /home/ddos_check ./topcnt flow/url_05102017_151800.dump.gz ip hits 77.XXX.XX.64 144889 s= 5379.99 (Mb) ( 1299787840 ) 77.88.8.8 14051 s= 2.26185 (Mb) ( 1297614856 ) 128.128.128.8 1642 s= 0.401568 (Mb) ( 134744072 ) 77.88.8.1 1578 s= 0.359544 (Mb) ( 1297614849 ) ... 77.XXX.XX.208 468 s= 1.45243 (Mb) ( 1299785168 )
видим 1 шт. ip в топе с очень большим отрывом, этот адрес ddos'ят из вне забивая канал.
Проверяем top по размеру по всем src и dsc IP:
./topsize arch/attak1/url_05102017_151800.dump.gz 0 | sort -n -r| head -20 | awk '{print $1/1024/1024 " " $2'}
5380.01 77.XXX.XX.64
165.881 81.XXX.XXX.79
…
27.2184 74.XXX.XXX.27
видим что в текущую минуту данный IP получил и отдал 5.4 Гбайт трафика, следующий только 165 Мбайт. Таким образом подтвердилось предположение о DDoS атаке на IP=77.XXX.XX.64.
Данный адрес отправляем в blackhole, так как мощности канала не хватает, т.е. блокировать можно только у вышестоящего провайдера с помощью null route.
Настройка httpd
Для оперативного доступа при наличии http на сервере где происходит прием IPFX можно прописать в конфигурации /etc/httpd/conf/httpd.conf:
<Directory "/var/www/html/ddos_check"> Options Indexes FollowSymLinks AllowOverride None Order allow,deny Allow from all </Directory> Alias /ddos_check/ "/var/www/html/ddos_check/"
Желательно еще добавить ограничение доступа, в данном примере не рассматривается.
Соответственно после рестарта httpd можно получить страницу по ссылке
http://<your_ip_http>/ddos_check/lastminute.txt
Что в остатке?
Интересно было понять м.б. можно заблокировать только ip извне? или их там пара десятков?
создаем скрипт для подсчета мощности атаки /home/ddos_check/topip:
#!/bin/bash function getipv4() { s=`echo "obase=16; " $1 | bc | sed 's/../0x& /g'` ip=`printf '%d.%d.%d.%d' $s` echo -n $ip } zcat $1 | grep $2 | awk -F '\t' '{print $8 "\n" $6}' | grep -v $2 | sort | uniq -c -d | sort -n -r > tmp$$$ echo -e "ip\t\thits" echo -n "unique ip="; wc -l tmp$$$ | awk '{print $1}' head -20 tmp$$$ > top20$$$ while read p; do cnt=`echo $p | awk '{print $1}'` ipd=`echo $p | awk '{print $2}'` getipv4 $ipd; echo -e "\t" $cnt done <top20$$$ rm -f tmp$$$ top20$$$
не забывает сделать chmod a+x /home/ddos_check/topip
запускаем, берем в качестве 2-го параметра десятичное представление IP из () в TOP-20 сессий:
./topip 'arch/attak1/url_05102017_15*' 1299787840 ip hits unique ip=58261 202.92.200.6 2626 110.76.131.6 2546 119.235.28.59 2150 38.70.202.194 1874 177.38.144.14 1830 138.204.18.18 1542 212.119.180.222 1452 88.220.134.2 1432 200.186.13.86 1389 ...
как видно из таблицы в атаке участвуют 58 тысяч адресов.