#! /bin/sh
# 防止SYN_RECV攻击的脚本
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
# 设置某个ip需要被列入黑名单的时间,单位是分钟
TIME_ZONE=30
DATE_ZONE=30
SYN_critical=500
DATE=$(date +%Y%m%d)
HOUR=$(date +%H)
MINITE=$(date +%M)
DATE_AGO=$(date -d "$DATE_ZONE days ago" +%Y%m%d)
DATE_1D_AGO=$(date -d "1 days ago" +%Y%m%d)
TIME_AGO=$(date -d "$TIME_ZONE min ago" +%H%M)
# 正常日志
LOG=/home/tools/syn_flood/syn.log
# 30分钟前的ip记录
LOG_ago=/home/tools/syn_flood/syn.log.ago
# 30分钟以内的ip记录
LOG_current=/home/tools/syn_flood/syn.log.current
#设置轮转日志的大小范围,单位k
LIMIT=16384
export PATH TIME_ZONE DATE_ZONE SYN_critical DATE HOUR MINITE DATE_AGO DATE_1D_AGO TIME_AGO
# 将proc文件系统的文件放入tmp
cat /proc/net/tcp6 /proc/net/tcp 2>/dev/null > /tmp/syn_recv
# 取到黑名单列表
awk '{print $2,$3,$4}' /tmp/syn_recv | awk '
BEGIN { # set fs
FS = "[ ]*|:" ; }
# 只处理第五列是SYN_RECV的行
( $5 ~ /03/ ){
# get ipv4addr from file /proc/net/tcp
if (length($1) == 8)
{
rem_addr_ip4 = strtonum("0x"substr($3,1,2)) ;
rem_addr_ip3 = strtonum("0x"substr($3,3,2)) ;
rem_addr_ip2 = strtonum("0x"substr($3,5,2)) ;
rem_addr_ip1 = strtonum("0x"substr($3,7,2)) ;
}
else
# get ipv6addr from file /proc/net/tcp6
{
rem_addr_ip4 = strtonum("0x"substr($3,25,2)) ;
rem_addr_ip3 = strtonum("0x"substr($3,27,2)) ;
rem_addr_ip2 = strtonum("0x"substr($3,29,2)) ;
rem_addr_ip1 = strtonum("0x"substr($3,31,2)) ;
}
printf("%d.%d.%d.%d\n",rem_addr_ip1,rem_addr_ip2,rem_addr_ip3,rem_addr_ip4);
}' | uniq -c | grep -v '192.168.' | awk 'BEGIN{
SYN_critical = ENVIRON["SYN_critical"];
DATE = ENVIRON["DATE"];
HOUR = ENVIRON["HOUR"];
MINITE = ENVIRON["MINITE"];
}
( $1 >= SYN_critical ){ printf("[ %d %d%d ] %s %d\n",DATE,HOUR,MINITE,$2,$1) }' >> $LOG
# 得到旧的ip列表
if [ $HOUR = "00" ] && [ $MINITE -le 30 ]
then
export DATE=$DATE_1D_AGO
fi
awk 'BEGIN{
DATE = ENVIRON["DATE"];
time_ago = ENVIRON["TIME_AGO"];
}
( $2 == DATE && $3 < time_ago ){ print $5 }' $LOG | uniq > $LOG_ago
# 得到30分钟内的ip列表
awk 'BEGIN{
DATE = ENVIRON["DATE"];
time_ago = ENVIRON["TIME_AGO"];
}
( $2 == DATE && $3 > time_ago ){ print $5 }' $LOG | uniq > $LOG_current
NUM_LOG_ago=$(wc -l $LOG_ago | awk '{print $1}' )
NUM_LOG_current=$(wc -l $LOG_current | awk '{print $1}' )
# 看iptables是否运行
IPTAB_STATUS=$(service iptables status | grep -c 'Firewall is stopped.')
# 如果30分钟以前有ip被列入黑名单,则删除掉
if [ $NUM_LOG_ago -ge 1 ]
then
if [ $IPTAB_STATUS -eq 0 ]
then
for IP in $(cat $LOG_ago)
do
# 确认此ip是否在当前iptables列表中,在则删除该ip
NUM_ip=$(grep -c $IP /etc/sysconfig/iptables)
if [ $NUM_ip -ge 1 ]
then
iptables -D INPUT -s $IP -i eth0 -j DROP
service iptables save
fi
done
fi
fi
# 处理30分钟内的ip列表,如果不在iptables里,则判断iptables的状态,并加入黑名单
if [ $NUM_LOG_current -ge 1 ]
then
# 看iptables是否打开
if [ $IPTAB_STATUS -eq 1 ]
then
service iptables start
fi
# 把ip加进去
for IP in $(cat $LOG_current)
do
# 确认此ip是否在当前iptables列表中,不在则加进去
NUM_ip=$(grep -c $IP /etc/sysconfig/iptables)
if [ $NUM_ip -eq 0 ]
then
iptables -A INPUT -s $IP -i eth0 -j DROP
service iptables save
fi
done
fi
# 如果iptables列表为空,判断其状态,如果是start ,则stop它
if [ $IPTAB_STATUS -eq 0 ]
then
# 判断iptables列表是否为空
NUM=$(iptables -L -n | wc -l)
if [ $NUM -eq 8 ]
then
service iptables stop
fi
fi
# 按大小轮转日志,注意,日志轮转都放在晚上1点,这样节省脚本每次运行的if判断开销
################################## 日志轮转 #######################################
#if [ $HOUR == "01" ]
# then
# size=$(ls -l -k $LOG | cut -d " " -f 6)
#
# echo "$LOG size is $size, limit is $LIMIT"
#
# if [ $size -ge $LIMIT ]
#
# then
#
# echo "rolling log file"
# awk 'BEGIN{
# DATE = ENVIRON["DATE"];
# time_ago = ENVIRON["TIME_AGO"];
# }
#
# ( $1 == DATE && $3 > time_ago ){ print }' $LOG > /tmp/syn_log_tmp
# cp $LOG $LOG.$DATE
# mv -f /tmp/syn_log_tmp $LOG
#
# else
#
# echo "not big enough"
#
# fi
#
# # 按日期轮转日志
# # 如果30天前有日志,说明是该到轮转的时候了
# DATE_NUM=$(grep "$DATE_AGO" $LOG | head | wc -l | awk '{print $1}')
# if [ $DATE_NUM -ge 1 ]
# then
# awk 'BEGIN{
# DATE = ENVIRON["DATE"];
# time_ago = ENVIRON["TIME_AGO"];
# }
#
# ( $1 == DATE && $3 > time_ago ){ print }' $LOG > /tmp/syn_log_tmp
# cp $LOG $LOG.$DATE
# mv -f /tmp/syn_log_tmp $LOG
#
# fi
#
#fi
################################## 日志轮转 #######################################