拉黑 Nginx 日志中频繁访问服务器的IP

886 阅读1分钟
#/bin/bash

########################## 如何使用 ##########################
# #1) 修改对应的参数
# #2) 执行初始化 ./scan.sh init
# #3) 运行 ./scan.sh run
# #4) 查看IPset黑名单状态 ./scan.sh status 
##############################################################

######################## 脚本参数配置 #########################
LOGPATH="/home/wwwlogs/" #日志文件路径
LOGFILE="y.log" #需要扫描的文件
LAST_MINUTES=35 #统计时间范围【分钟】
MAX_REQUEST=200 #最大请求数
FIREWALL_SERVICE="firewall" #firewall|iptables 防火墙服务类型

 
##############################################################


CMD=$1
if [ "${CMD}" = "" ]; then
    CMD="run"
else
    CMD=$1
fi

#筛选IP并拉黑
function filterIP()
{
    logfile=$1

    echo '' > log_ip_ranking

    #过滤指定时间范围内的日志并统计最高ip数
    tac $logfile  | grep "${day}" | awk '{print $1,$4;}' | tr  '['  ' '  | tr ':' ' ' |  tr '/' ' ' \
    | awk -v st="$start_time" -v et="$stop_time"  '{ t=$2$5$6;if (t>=st && t<=et ) {print $1;}}' | sort | uniq -c | sort -nr > $LOGPATH/log_ip_ranking

    ip_top=`cat $LOGPATH/log_ip_ranking | head -1 | awk '{print $1}'`
    ips=`cat $LOGPATH/log_ip_ranking | awk -v max_num="${MAX_REQUEST}" '{if($1>=max_num) print $2}'`


    Log "====== ${logfile} ======"
    
    msg=""

    # 指定时间范围内达到限制次数的IP通过ipset封锁
    for ip in $ips
    do
        ret=`cat ${LOGPATH}blacklog | grep ${ip}`
        if [ -z "$ret" ];then
            cmd=`echo ${ADD_BLACKLIST_CMD}| sed "s/{{ip}}/${ip}/g"`
            read num ipstr < <(cat $LOGPATH/log_ip_ranking | grep ${ip})
            tmp="IP:${ipstr} 访问次数:${num}"
            msg="${msg}\n ${tmp}"
            $cmd 
            echo $tmp
            echo $ip >> ${LOGPATH}blacklog
        else
            Log "${ip},已存在处理历史记录中,跳过..."
        fi
    done

    #服务重启一下
    $RELOAD_SERVICE_CMD > /dev/null 2>&1


    if [ -n "$msg" ];then
        sendMsg "以下IP访问【${LAST_MINUTES}】分钟内访问超过【${MAX_REQUEST}】次已拉黑\n文件:${logfile}\n时间范围:${start_time} - ${stop_time}\n${msg}"
        Log ''
    fi

    Log "====== END ======"
}

function Log()
{
    echo $@
}

#开始扫描
function run()
{
    day=`date +"%d/%b/%Y"`

    #开始时间  19/Jul/2021:05:17  = 190517
    start_time=`date -d"${LAST_MINUTES} minutes ago" +"%d%H%M"`
    #Log $start_time
    #结束时间
    stop_time=`date +"%d%H%M"`
    #Log $stop_time
 
    file_list=`ls ${LOGPATH}${LOGFILE}`

    case "${FIREWALL_SERVICE}" in
        firewall)
            #firewall 的命令配置
            ADD_BLACKLIST_CMD='firewall-cmd --permanent --ipset=blacklist --add-entry={{ip}}' # {{ip}} 为IP替换占位符
            RELOAD_SERVICE_CMD='firewall-cmd --reload' #服务重启的命令
            ;;
        iptables)
            #iptables 的命令配置
            ADD_BLACKLIST_CMD='ipset add blacklist {{ip}}' # {{ip}} 为IP替换占位符
            RELOAD_SERVICE_CMD='' #服务重启的命令
            ;;
    esac

    for file in $file_list
    do
        filterIP $file
    done
}

function sendMsg()
{
    msg_content=`echo -e $@`
    #...code 定制你的消息通知
}

# 初始化 IPTable 的 IpSet
function initIpTables()
{
    yum -y install ipset
    # 创执行建黑名单
    ipset create blacklist hash:ip
    iptables -I INPUT -m set --match-set blacklist src -p tcp --destination-port 80:8080 -j DROP
    
    #移除黑名单
    #ipset del blacklist {ip}

    Log "IPTables init success."
}

# 初始化 Firewall 的 IpSet
function initFirewall()
{
    # 创执行建黑名单
    firewall-cmd --permanent --new-ipset=blacklist --type=hash:ip
    # 添加规则,禁用名单内的IP 访问 80 - 8080 端口
    firewall-cmd --permanent --add-rich-rule 'rule family="ipv4" source ipset="blacklist"   port port="80-8080"  protocol="tcp" drop'
    firewall-cmd --reload

    Log "Firewall init success."
}
 
#查看集合状态
function status()
{
    echo "###### Service:  ${FIREWALL_SERVICE} ######"

    case "${FIREWALL_SERVICE}" in
        firewall)
            firewall-cmd --permanent --info-ipset=blacklist
            ;;
        iptables)
            ipset list
            ;;
    esac
}

#程序初始化
function init()
{
    case "${FIREWALL_SERVICE}" in
        firewall)
            initFirewall
            ;;
        iptables)
            initIpTables
            ;;
    esac
}

#程序入口
function main()
{
    case "${CMD}" in
        init)
            init
            ;;
        run)
            run
            ;;
        status)
            status
            ;;
        *)
            echo "Usage: $0 {init|run|status}"
            ;;
    esac
}

main