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
如何確認是否受到DDOS攻擊?
執行:
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n
執行後,將會顯示伺服器上所有的每個IP多少個串連數。
以下是我自己用VPS測試的結果:
li88-99:~# netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n
1 114.226.9.132
1 174.129.237.157
1 58.60.118.142
1 Address
1 servers)
2 118.26.131.78
3 123.125.1.202
3 220.248.43.119
4 117.36.231.253
4 119.162.46.124
6 219.140.232.128
8 220.181.61.31
2311 67.215.242.196
每個IP幾個、十幾個或幾十個串連數都還算比較正常,如果像上面成百上千肯定就不正常了