删掉一些二次判定(加快封禁检测,随总请求数量速度增加逐级提高/降低封禁阈值;),以及封禁IP数量统计,去重;好就好在脚本很短,也很好理解;

I. Cloudflare 下 Nginx 获取用户真实IP 地址

如果你正在使用Cloudflare,则需要对Nginx进行相关设置;

server
    {
listen 443 ssl http2;
server_name limbopro.xyz ;
...
##$server 段 添加如下段落 开始
include /home/ip.blacklist.5minutes.conf; ##黑名单位置;
set_real_ip_from 103.21.244.0/22; 
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
real_ip_header CF-Connecting-IP;
##$server 段 添加如下段落 结束 #这些都是CF节点IP;
...
index index.html index.htm index.php default.html default.htm default.php;
...

参考 Cloudflare 下 Nginx 获取用户真实IP 地址

II. 新脚本(通俗易懂版本)

2020.12.24日更新,基于新配置的WAF;

Cloudlfare 强大的API:https://api.cloudflare.com/

执行脚本如下:供参考;

#!/bin/bash 

maxrequest=1 #最低封禁次数
maxtimes=1 #拉取最近N分钟的请求至临时日志

function define()
{
    #引入参数环节
    ori_log_path="/home/wwwlogs/limbopro.xyz/access.log" #原始日志存放位置
    rm /home/tnt/access.log.tmp >/dev/null 2>&1; #清除拉取的日志
    tmp_log_path="/home/tnt/access.log.tmp" #生成的临时日志存放位置
    date_stamp=`date -d "-"$maxtimes"min" +%Y:%H:%M:%S` #引入时间范围参数
    day_stamp=`date +%d` #日期
}

function gather()
{
    awk -F '[/ "[]' -vnstamp="$date_stamp" -vdstamp="$day_stamp" '$7>=nstamp && $5==dstamp' ${ori_log_path} > ${tmp_log_path}; #根据时间范围从原始日志处读取并写入临时日志
    log_num=`cat ${tmp_log_path} | wc -l`; #计算时间范围内的网络请求次数
}


function main()
{
    define
    gather
}
## 拉取日志结束

main

## 拉黑开始
date=$(env LANG=en_US.UTF-8 date "+%e/%b/%Y/%R")
error_log_path=/home/tnt/access.log.503 #503日志存储位置
> /home/tnt/access.challenge.iplist;
echocf=/home/tnt/access.challenge.iplist #Cloudflare 黑名单收集 会销毁

##从临时日志
##抓取符合503状态的日志并存入 
##/home/tnt/access.log.503

rm /home/tnt/access.log.503 >/dev/null 2>&1; #清除生成的日志
# ipv4 grep -oP "(\w{1,3}\.){3}\w{1,3}.*?\s503\s(?!(.*?Googlebot|.*?bot|.*?Bot)).*" ${tmp_log_path} >> ${error_log_path}; # 从临时日志抓取符合503状态的部分日志存入 /home/tnt/access.log.503
grep -oP "((\w{1,3}\.){3}\w{1,3}|(\w{1,4}\:){7}\w{1,4}).*?\s(503|499)\s(?!(.*?Googlebot|.*?bot|.*?Bot)).*" ${tmp_log_path} >> ${error_log_path}; # 从临时日志抓取符合503状态的部分日志存入 /home/tnt/access.log.503

for ip in $(awk '{cnt[$1]++;}END{for(i in cnt){printf("%s\t%s\n", cnt[i], i);}}' ${error_log_path} | awk '{if($1>'$maxrequest') print $2}') 
do  echo "${ip}" >> $echocf; 
done

##提交IP黑名单数据至 Cloudflare
##block, challenge, whitelist, js_challenge
##Cloudflare 配置文件

CFEMAIL="这里填写#邮箱"
CFAPIKEY="#这里填写#CF密钥"
ZONESID="这里填写#ZONESID"
IPADDR=$(</home/tnt/access.challenge.iplist)

cat /home/tnt/access.challenge.iplist;
date=$(env LANG=en_US.UTF-8 date "+%e/%b/%Y/%R")
ip_num=$(grep -c "" /home/tnt/access.challenge.iplist);
echo "$date 本次封禁IP数量为 $ip_num 个"

if [ $ip_num -lt 2 ];
then # 执行小封禁
event="恶意刷新";
blocktimes="300秒";

for IPADDR in ${IPADDR[@]}; do
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONESID/firewall/access_rules/rules" \
  -H "X-Auth-Email: $CFEMAIL" \
  -H "X-Auth-Key: $CFAPIKEY" \
  -H "Content-Type: application/json" \
  --data '{"mode":"block","configuration":{"target":"ip","value":"'$IPADDR'"},"notes":"60rates-block"}' >/dev/null 2>&1;
done

else # 否则执行大封禁
event="规模攻击";
blocktimes="每日凌晨解封";
for IPADDR in ${IPADDR[@]}; do
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONESID/firewall/access_rules/rules" \
  -H "X-Auth-Email: $CFEMAIL" \
  -H "X-Auth-Key: $CFAPIKEY" \
  -H "Content-Type: application/json" \
  --data '{"mode":"block","configuration":{"target":"ip","value":"'$IPADDR'"},"notes":"Xddos-block"}' >/dev/null 2>&1;
done

fi

以下为旧版本封禁方法,历史较为久远;

III. 提取Nginx.log日志

新建Shell脚本 /home/5minutes.log.sh,内容如下;(脚本存放位置与名字自己看着办)

#!/bin/bash

function define()
{
    ori_log_path="/home/wwwlogs/limbopro.xyz/access.log" #你的 Nginx 日志位置!!
    tmp_log_path="/home/5min.access.log" #提取最近5分钟Nginx日志
    date_stamp=`date -d "-5min" +%Y:%H:%M:%S` #时间提取(5分钟内)
    day_stamp=`date +%d` #日期提取
}

function gather()
{
    awk -F '[/ "\[]' -vnstamp="$date_stamp" -vdstamp="$day_stamp" '$7>=nstamp && $5==dstamp' ${ori_log_path} > ${tmp_log_path};
    log_num=`cat ${tmp_log_path} | wc -l`;
    request_time=`awk '{print $(NF-1)}' ${tmp_log_path} | awk '{sum+=$1}END{print sum}'`;
    ave_request_time=`echo | awk "{print ${request_time}/${log_num}}" `;
    /home/nginx.ban.5minutes.sh; #执行封禁脚本
    > /home/5min.access.log; #清除临时产生的5分钟日志
}

function output()
{
}

function main()
{
    define
    gather
    output
}

main

IV. 我的配置参考

#!/bin/bash

function define()
{
    ori_log_path="/home/wwwlogs/limbopro.xyz/access.log"
    tmp_log_path="/home/5min_limbopro.xyz.access.log"
    date_stamp=`date -d "-5min" +%Y:%H:%M:%S`
    day_stamp=`date +%d`
}

function gather()
{
    awk -F '[/ "\[]' -vnstamp="$date_stamp" -vdstamp="$day_stamp" '$7>=nstamp && $5==dstamp' ${ori_log_path} > ${tmp_log_path};
    log_num=`cat ${tmp_log_path} | wc -l`;
    request_time=`awk '{print $(NF-1)}' ${tmp_log_path} | awk '{sum+=$1}END{print sum}'`;
    ave_request_time=`echo | awk "{print ${request_time}/${log_num}}" `;
    ipcounts=$(awk '{print $1}' /home/5min_limbopro.xyz.access.log | sort -n | uniq | wc -l);
    date=$(env LANG=en_US.UTF-8 date "+%e/%b/%Y/%R")
    echo "${date}" "网站最近5分钟总请求数为 ${log_num}" 次 >> /home/5minutes.log;
    echo "${date}" "网站最近5分钟总请求数为 ${ipcounts}" 个"(IP数量)" >> /home/5minutes.log;
    echo " " >> /home/5minutes.log;
    /home/nginx.ban.5minutes.sh;
    > /home/5min_limbopro.xyz.access.log;
}

function output()
{
    date=$(env LANG=en_US.UTF-8 date "+%e/%b/%Y/%R")
    max=200000
    if [ ${log_num} -gt $max ];
then
    cp /home/attackcheck.ddos.sh.bak /home/attackcheck.sh
/home/attackcheck.sh;
else
    cp /home/attackcheck.normal.sh.bak /home/attackcheck.sh
/home/attackcheck.sh;
fi
}

function main()
{
    define
    gather
    output
}

main

V. 封禁脚本

新建 Shell脚本 /home/nginx.ban.5minutes.sh,内容如下;(脚本存放位置与名字自己看着办)

#!/bin/bash 
blockip=/home/ip.blacklist.5minutes.conf #黑名单位置,请把这个文件添加到 server 段下;
access=/home/5min.access.log 
for ip in $(awk '{cnt[$1]++;}END{for(i in cnt){printf("%s\t%s\n", cnt[i], i);}}' ${access} | awk '{if($1>500) print $2}') #五分钟内超过500次请求则拉黑

do
    echo "deny ${ip};" >> $blockip
    echo "deny ${ip};" >> $ipcount
    iptables -I INPUT -s ${ip} -j DROP
done
lnmp nginx reload ## nginx 重新加载配置文件

VI. 定时执行

crontab -e #命令行输入
* * * * * /home/5minutes.log.sh ##设置为1分钟执行一次

手动自定义封杀脚本

有时候遇到大规模IP请求?比如1万个代理ip,直接手动封杀即可;

脚本如下:

#!/bin/bash

## 清除缓存日志
#> /home/echo_limbopro.xyz.access.log
> /home/echo_limbopro.xyz.access.before.log;
> /home/echo_limbopro.xyz.access.log;
echo -n "输入需要读取的文档(默认回车):"
read iaccess # 把键盘输入放入变量access


## 赋值到最大日志读取时间并读取日志
echo -n "输入日志最大的读取范围(分钟内):"                   # 参数-n的作用是不换行,echo默认换行
read  imaxtime # 把键盘输入放入变量maxtime

function define()
{
    ori_log_path="/home/wwwlogs/limbopro.xyz/access."${iaccess}"log" #按实际位置进行修改
    tmp_log_path="/home/echo_limbopro.xyz.access.log" #提取的临时日志位置及名字
    date_stamp=`date -d "-"${imaxtime}"min" +%Y:%H:%M:%S`
    day_stamp=`date +%d`
}

function gather()
{
    awk -F '[/ "\[]' -vnstamp="$date_stamp" -vdstamp="$day_stamp" '$7>=nstamp && $5==dstamp' ${ori_log_path} > ${tmp_log_path};
    log_num=`cat ${tmp_log_path} | wc -l`;
    request_time=`awk '{print $(NF-1)}' ${tmp_log_path} | awk '{sum+=$1}END{print sum}'`;
    ave_request_time=`echo | awk "{print ${request_time}/${log_num}}" `;
    #ipcounts=$(awk '{print $1}' /home/5min_limbopro.xyz.access.log | sort -n | uniq | wc -l);
    date=$(env LANG=en_US.UTF-8 date "+%e/%b/%Y/%R")
    echo "${date}" "网站最近"${imaxtime}"分钟总请求数为 ${log_num}" 次 # >> /home/5minutes.log;
}


function main()
{
    define
    gather
 #   output
}

main


## 赋值到最大IP封禁值;
echo -n "输入最低可封杀请求数:" 
read  max                                   

## 清除上期封禁;
> /home/ip.blacklist.auto.echo.conf #如果不想清除可注释掉

## 新一轮封禁开始;
blockip=/home/ip.blacklist.auto.echo.conf
access=/home/echo_limbopro.xyz.access.log
for ip in $(awk '{cnt[$1]++;}END{for(i in cnt){printf("%s\t%s\n", cnt[i], i);}}' ${access} | awk '{if($1>'$max') print $2}') 
do 
    echo "deny ${ip};" >> $blockip 
    echo "deny ${ip};" >> /home/wwwroot/typecho/deny/index.log 
done
sleep 1s

## 重启nginx以生效;
lnmp nginx reload

##计算IP封禁数量;
catipblocks=$(nohup wc -l /home/ip.blacklist.auto.echo.conf > /home/ip.blacklist.auto.echo.conf.log);
ipblocks=$(awk '{print $1}' /home/ip.blacklist.auto.echo.conf.log);
> /home/ip.blacklist.auto.echo.conf.log;
echo ""${imaxtime}" 分钟内,符合"${max}"次请求则封禁的 IP 数量为${ipblocks}个;"
echo ""${imaxtime}" 分钟内,符合"${max}"次请求则封禁的 IP 地址如下:"
cat /home/ip.blacklist.auto.echo.conf;
echo ""${imaxtime}" 分钟内,符合"${max}"次请求则封禁的 IP 地址如上:"
echo ""${imaxtime}" 分钟内,符合"${max}"次请求则封禁的 IP 数量为${ipblocks}个;"

## 查看IP访问数量;
ipnumbers=$(awk '{print $1}' ${access} | sort -n | uniq | wc -l)
echo ""${imaxtime}" 分钟内,用户发起请求的 IP 数量为${ipnumbers}个;"

#echo "最受欢迎的3个页面:"
echo -n "查看最受欢迎的页面(输入数字):"
read  maxpages;
nohup awk '{print $7}' /home/echo_limbopro.xyz.access.log | sort |uniq -c | sort -rn | head -n "${maxpages}" >> /home/hotpages.log;
echo ""${imaxtime}" 分钟内,最受欢迎的"${maxpages}"个页面:"
cat /home/hotpages.log
> /home/hotpages.log


## 清楚缓存日志;
#> /home/echo_limbopro.xyz.access.log
> /home/echo_limbopro.xyz.access.before.log;
> /home/echo_limbopro.xyz.access.log;
最后修改:2020 年 12 月 24 日 01 : 09 AM