DDoS deflate其实是一个Shell脚本,使用netstat和iptables工具,对那些链接数过多的IP进行封锁,能有效防止通用的恶意扫描器,但它并不是真正有效的DDoS防御工具。

工作过程描述:
同一个IP链接到服务器的连接数到达设置的伐值后,所有超过伐值的IP将被屏蔽,同时把屏蔽的IP写入ignore.ip.list文件中,与此同时会在tmp中生成一个脚本文件,这个脚本文件马上被执行,但是一运行就遇到sleep 预设的秒,当睡眠了这么多的时间后,解除被屏蔽的IP,同时把之前写入ignore.ip.list文件中的这个被封锁的IP删除,然后删除临时生成的文件。

一个事实:如果被屏蔽的IP手工解屏蔽,那么如果这个IP继续产生攻击,那么脚本将不会再次屏蔽它(因为加入到了ignore.ip.list),直到在预设的时间之后才能起作用,加入到了ignore.ip.list中的IP是检测的时候忽略的IP。可以把IP写入到这个文件以避免这些IP被堵塞,已经堵塞了的IP也会加入到ignore.ip.list中,但堵塞了预定时间后会从它之中删除。

安装:
 wget http://www.inetbase.com/scripts/ddos/install.sh
 chmod 0700 install.sh
 ./install.sh
卸载:
 wget http://www.inetbase.com/scripts/ddos/uninstall.ddos
 chmod 0700 uninstall.ddos
 ./uninstall.ddos

安装完成后在/usr/local/ddos目录下产生了ddos.conf、ddos.sh、ignore.ip.list和LICENSE这四个文件,ddos.conf是配置文件,ddos.sh是一个Shell文件,ignore.ip.list是存放忽略IP的文件,LICENSE是版权声明文件,安装完成后还在/etc/cron.d/下生产了ddos.cron文件,内容如下:
SHELL=/bin/sh
0-59/1 * * * * root /usr/local/ddos/ddos.sh >/dev/null 2>&1
意思是每隔一分钟执行一下/usr/local/ddos/ddos.sh

这个cron任务是依赖ddos.conf文件中的NO_OF_CONNECTIONS变量产生的,如果修改了此值,可以通过运行如下命令更新(实际也是在安装是运行了如下命令):
/usr/local/ddos/ddos.sh -c 或 /usr/local/ddos/ddos.sh –cron

以下主要针对ddos.conf和ddos.sh进行分析:

ddos.conf内容:

##### Paths of the script and other files
PROGDIR="/usr/local/ddos"
PROG="/usr/local/ddos/ddos.sh"
IGNORE_IP_LIST="/usr/local/ddos/ignore.ip.list"
CRON="/etc/cron.d/ddos.cron"
APF="/etc/apf/apf"
IPT="/sbin/iptables"

##### frequency in minutes for running the script
##### Caution: Every time this setting is changed, run the script with --cron
#####          option so that the new frequency takes effect
# 设置检测时间间隔,默认是分钟,由于系统使用crontab功能,最小单位是分钟
FREQ=1

##### How many connections define a bad IP? Indicate that below.
# NO_OF_CONNECTIONS默认是150,这是一个经验值,如果服务器性能比较高,可以设置200以上,以避免误杀
NO_OF_CONNECTIONS=150

##### APF_BAN=1 (Make sure your APF version is atleast 0.96)
##### APF_BAN=0 (Uses iptables for banning ips instead of APF)
# 使用APF屏蔽IP,如果设置为0就使用iptables,如果使用APF则需要先安装,比如centos中默认就没有安装
APF_BAN=0

##### KILL=0 (Bad IPs are'nt banned, good for interactive execution of script)
##### KILL=1 (Recommended setting)
KILL=1

##### An email is sent to the following address when an IP is banned.
##### Blank would suppress sending of mails
# 如果不希望发送邮件,设置为空
EMAIL_TO=""

##### Number of seconds the banned ip should remain in blacklist.
# 解锁的时间,单位为秒,可以设置更长时间
BAN_PERIOD=86400

ddos.sh内容:

# 载入配置文件
load_conf()
{
        CONF="/usr/local/ddos/ddos.conf"
	# $CONF是文件,使用source载入
        if [ -f "$CONF" ] && [ ! "$CONF" ==     "" ]; then
                source $CONF
        else
                head
                echo "\$CONF not found."
                exit 1
        fi
}

# 头部输出
head()
{
        echo "DDoS-Deflate version 0.6"
        echo "Copyright (C) 2005, Zaf <zaf@vsnl.com>"
        echo
}

# 显示帮助,比如如果要收到干掉当前超过N个连接的IP,使用sh ddos.sh -k 150, sh ddos.sh -h 显示帮助, sh ddos.sh -c创建cron job
showhelp()
{
        head
        echo 'Usage: ddos.sh [OPTIONS] [N]'
        echo 'N : number of tcp/udp     connections (default 150)'
        echo 'OPTIONS:'
        echo '-h | --help: Show this help screen'
        echo '-c | --cron: Create cron job to run this script regularly (default                 1 mins)'
        echo '-k | --kill: Block the offending ip making more than N connections                '
}

# 解除对IP的封锁
unbanip()
{
        UNBAN_SCRIPT=`mktemp /tmp/unban.XXXXXXXX`	# 产生随机文件,解除IP封锁时
        TMP_FILE=`mktemp /tmp/unban.XXXXXXXX`		# 临时文件
        UNBAN_IP_LIST=`mktemp /tmp/unban.XXXXXXXX`	# 将被解除封锁的IP
        echo '#!/bin/sh' > $UNBAN_SCRIPT		# 产生解除IP封锁的脚本内容
        echo "sleep $BAN_PERIOD" >> $UNBAN_SCRIPT	# $BAN_PERIOD睡眠时间,表示$UNBAN_SCRIPT睡眠多久后继续,这个变量在配置文件中定义
        if [ $APF_BAN -eq 1 ]; then			# 使用APF堵塞IP
                while read line; do
                        echo "$APF -u $line" >> $UNBAN_SCRIPT
                        echo $line >> $UNBAN_IP_LIST
                done < $BANNED_IP_LIST
        else						# 使用iptables封锁IP
                while read line; do
                        echo "$IPT -D INPUT -s $line -j DROP" >> $UNBAN_SCRIPT		# 解除IP封锁
                        echo $line >> $UNBAN_IP_LIST	# 把将要解除堵塞的IP写入$UNBAN_IP_LIST,跟当前的$BANNED_IP_LIST是对应的
                done < $BANNED_IP_LIST			# 输入重定向,行对于$line变量,是当前需要堵塞的IP,脚本运行过程中产生
        fi
        echo "grep -v --file=$UNBAN_IP_LIST $IGNORE_IP_LIST > $TMP_FILE" >> $UNBAN_SCRIPT   # 从$IGNORE_IP_LIST中去掉$UNBAN_IP_LIST,把结果写入$TMP_FILE
        echo "mv $TMP_FILE $IGNORE_IP_LIST" >> $UNBAN_SCRIPT    # 移动$TMP_FILE到$IGNORE_IP_LIST,$IGNORE_IP_LIST在配置文件中定义,则本次操作是覆盖
        echo "rm -f $UNBAN_SCRIPT" >> $UNBAN_SCRIPT	# 删除$UNBAN_SCRIPT
        echo "rm -f $UNBAN_IP_LIST" >> $UNBAN_SCRIPT	# 删除$UNBAN_IP_LIST
        echo "rm -f $TMP_FILE" >> $UNBAN_SCRIPT 	# 删除$TMP_FILE,经过上面的移动操作,$TMP_FILE其实已经不存在
	. $UNBAN_SCRIPT &				# 在后台运行$UNBAN_SCRIPT
}

# 添加到计划任务
add_to_cron()
{
        rm -f $CRON				# 删除/etc/cron.d/ddos.cron
        sleep 1
        service crond restart			# 计划任务重启
        sleep 1
        echo "SHELL=/bin/sh" > $CRON		# 创建计划任务
        if [ $FREQ -le 2 ]; then		# $FREQ在配置文件中定义,表示多少分钟执行一次,如果小于2,Linux最小是每分钟
		# 以下语句得到 0-59/1 * * * * root /usr/local/ddos/ddos.sh >/dev/null 2>&1,每分钟以root执行ddos.sh,把输出结果丢弃
                echo "0-59/$FREQ * * * * root /usr/local/ddos/ddos.sh >/dev/null 2>&1" >> $CRON
        else					# 大于1分钟设置
                let "START_MINUTE = $RANDOM % ($FREQ - 1)"	# $RANDOM是环境变量随机数  let是shell内建命令,就是执行计算
                let "START_MINUTE = $START_MINUTE + 1"
                let "END_MINUTE = 60 - $FREQ + $START_MINUTE"
                echo "$START_MINUTE-$END_MINUTE/$FREQ * * * * root /usr/local/ddos/ddos.sh >/dev/null 2>&1" >> $CRON
        fi
        service crond restart
}

# 加载配置
load_conf

# 判断$1,是第一个参数,提供帮助,如果没有提供则不会进入循环
while [ $1 ]; do
        case $1 in
                '-h' | '--help' | '?' )
                        showhelp		# 显示帮助
                        exit
                        ;;
                '--cron' | '-c' )
                        add_to_cron		# 添加到计划任务
                        exit
                        ;;
                '--kill' | '-k' )
                        KILL=1			# KILL在配置文件中指定,用来指定是否堵塞IP
                        ;;
                 *[0-9]* )
                        NO_OF_CONNECTIONS=$1	# 第二个参数,指定阀值,NO_OF_CONNECTIONS在配置文件中指定,这里覆盖
                        ;;
                * )
                        showhelp
                        exit
                        ;;
        esac
        shift					# 减少变量,就是现在$1是原来的$2,shift后可以指定减少几个
done

TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
BANNED_IP_MAIL=`$TMP_FILE`			# 产生临时文件,发送邮件,邮件内容
BANNED_IP_LIST=`$TMP_FILE`			# 产生临时文件,存放已经被堵塞的IP
echo "Banned the following ip addresses on `date`" > $BANNED_IP_MAIL  构建邮件内容
echo >> $BANNED_IP_MAIL
BAD_IP_LIST=`$TMP_FILE`				# 产生临时文件,存放当前可能被堵塞的IP
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST
cat $BAD_IP_LIST				# 输出这个列表
if [ $KILL -eq 1 ]; then			# 如果配置为需要堵塞
        IP_BAN_NOW=0
        while read line; do
                CURR_LINE_CONN=$(echo $line | cut -d" " -f1)			# 当前这个IP有多少个连接
                CURR_LINE_IP=$(echo $line | cut -d" " -f2)			# 当前IP
                if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then		# 如果这个IP链接数小于预设,终止(因为数据经过排序)
                        break
                fi
                IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`		# 计算当前IP在$IGNORE_IP_LIST出现了多少次
                if [ $IGNORE_BAN -ge 1 ]; then					# 如果当前IP已经在$IGNORE_IP_LIST,跳过,可以通过这个方式设置永不堵塞某些IP
                        continue
                fi
                IP_BAN_NOW=1							# 进入到这里表示当前必定有IP要被堵塞
                echo "$CURR_LINE_IP with $CURR_LINE_CONN connections" >> $BANNE_IP_MAIL  # 把堵塞信息写入邮件内容
                echo $CURR_LINE_IP >> $BANNED_IP_LIST				# 添加堵塞了的IP到当前堵塞列表,$BANNED_IP_LIST会应用到下面的unbanip函数
                echo $CURR_LINE_IP >> $IGNORE_IP_LIST				# 添加堵塞了的IP到$IGNORE_IP_LIST
                if [ $APF_BAN -eq 1 ]; then
                        $APF -d $CURR_LINE_IP
                else
                        $IPT -I INPUT -s $CURR_LINE_IP -j DROP			# 开始iptables封锁
                fi
        done < $BAD_IP_LIST
        if [ $IP_BAN_NOW -eq 1 ]; then						# $IP_BAN_NOW等于1表示有IP被封锁了
                dt=`date`
                if [ $EMAIL_TO != "" ]; then					# $EMAIL_TO设置不为空则发邮件,留空则不发邮件
                        cat $BANNED_IP_MAIL | mail -s "IP addresses banned on $dt" $EMAIL_TO    # $EMAIL_TO在配置文件中指定
                fi
                unbanip								# 同时开始运行解除堵塞程序
        fi
fi
rm -f $TMP_PREFIX.*								# 清除临时产生的文件

可以看到如果有堵塞IP,将调用mail发送邮件,但是作为前端代理,视乎没有必要特意去安装sendmail,所以我们可以使用curl把数据推送到远程,由另一台专门的服务器发送这些信息,改造如下:
if [ $EMAIL_TO != "" ]; then
cat $BANNED_IP_MAIL | mail -s "IP addresses banned on $dt" $EMAIL_TO
else
curl --data "d=IP addresses banned on $dt -- $(cat $BANNED_IP_MAIL)" "http://domain.com/blockip.php" >> /dev/null
fi
除此,尽管默认到达150才堵塞,但你可能希望把那些超过100链接的IP都记录下来,可以在cat $BAD_IP_LIST之后添加如下代码:
NUM_CONNECTIONS=100 # 超过100就记录
# 按照日期存放
CNTS_LOG="/usr/local/ddos/$(date +%Y)/$(date +%m)/"
mkdir -p $CNTS_LOG
CNTS_LOG="$CNTS_LOG$(date +%Y%m%d).log"
while read line; do
CURR_CONN=$(echo $line | cut -d" " -f1)
CURR_IP=$(echo $line | cut -d" " -f2)
if [ $CURR_CONN -lt $NUM_CONNECTIONS ]; then
break
fi
echo "$CURR_IP with $CURR_CONN connections at `date`" >> $CNTS_LOG
done < $BAD_IP_LIST

Last modification:April 11th, 2020 at 09:18 pm
如果觉得我的文章对你有用,请随意赞赏