宝子放假,转眼假期已经过了一半,想着找个周末带宝子回老家住几天,大姐家的孩子周日要办升学宴,既然都回来了,不去也不合适,刚好就可以周六回老家,周日去淄博。
对象说,周日青岛会下大暴雨,还是下班之后直接回吧,不然第二天很可能走不了。
就这样,马不停蹄,下班买点东西就直接接上孩子往回走。天气预报零点的雨,在九点多到潍坊的时候就开始逐渐大了。
对象开车的时候,自己打开手机想回复下评论,但是却发现一个问题,那就是页面非常卡。刚开始还以为是网络问题,但是切到短视频却异常流畅。
瞬间有种不祥的预感,那就是服务器的 cpu 肯定又跑满了。
之前看到威言威语发的《暂时停用腾讯EdgeOne了》 ,感觉自己的系统貌似没什么太大的问题。但是,实际上是在这之前,切换到 eo 之后,也出现过一次 cpu 跑满的情况,当时重启 php 之后一切正常了,以为是偶发事件。所以也没再关注,但是这次尝试远程重启服务之后,只是瞬间缓解。几分钟之后就又卡死了,所以昨天能发评论的宝子真的都是真爱,么么哒(๑•́₃•̀๑),因为我自己都打不开我的后台。😂
这种感觉还有一个前兆,那就是后台资源库突然多了个文件。
不过这个文件看起来是某个插件的压缩包,但是描述是另外一个域名,猜测是另外一个域名传上来的。但是。搜了下这个用户不存在,所以最后直接把插件和文件一起删除了。
但是这个 cpu 跑满的感觉还是感觉是 cc,不然不至于 php 能直接占满了全部资源。
到家之后,看了下 eo 的后台没什么能配置地方。但是,通过服务器的访问记录却发现,有几个地址在频繁请求登录页面:
当然,更诡异的是这里面好几个 ip 竟然都是腾讯的海外节点。这就有点奇怪了。
尤其是 163 这个,按照常理来说,如果是 cdn 节点回源,应该不会摁着这一个地址回源,并且这个请求频率大概率会被 cdn 拦截,然而,现在的现象是不单没被拦截,还高频访问,一个地址在一秒内发起了几十个请求。这尼玛就离谱了,查了一下加速域名的节点,发现这些都不在 eo 的节点列表内。
那么,此时会合理的猜的就是 eo 的机房内或者是在某些地方的节点回源会泄露原始服务器地址,这样这一系列的请求就说的通了。这些请求本身并没有结果 cdn,所以直接是从机房发起的请求。
在回源地址泄露之后,有人通过 bot 控制了大量机房内的机器,尝试对 wp 的登录地址进行暴力破解,而这告诉 cc 也导致了家里的 mac mini 的服务器资源瞬间被耗尽了。
所以这个问题不是 eo 本身导致的,但是确实是套了 eo 的国外节点之后,有 eo 的某些请求泄露回源地址导致出现了直接针对服务器的 cc 攻击。这个攻击目标很明确,就是破解登录密码。本来想直接封锁那几个 ip,但是封锁之后发现还是不断的有新的开始尝试,于是,最新版本的工具就出现了,直接写个脚本,通过检测 nginx 访问日志,实现对 ip 动态风控。
代码如下:
#!/bin/bash
# 实时监控日志文件并自动封锁恶意IP
# 使用方法: sudo ./realtime_block.sh
LOG_FILE="/home/wwwlogs/h4ck.org.cn.log"
BLOCKED_IPS_FILE="/tmp/blocked_ips.txt"
MAX_REQUESTS_PER_MINUTE=10
BLOCK_DURATION=3600 # 封锁时间(秒)
DEBUG_MODE=false # 设置为true启用调试模式
QUIET_MODE=false # 设置为true启用静默模式(只显示新增封锁)
ATTACK_HISTORY_FILE="/tmp/attack_history.txt" # 攻击历史记录文件
# 检查是否以root权限运行
if [ "$EUID" -ne 0 ]; then
echo "请使用sudo运行此脚本"
exit 1
fi
# 检查inotify-tools是否安装
if ! command -v inotifywait &> /dev/null; then
echo "请先安装inotify-tools:"
echo "Ubuntu/Debian: sudo apt-get install inotify-tools"
echo "CentOS/RHEL: sudo yum install inotify-tools"
exit 1
fi
# 检查日志文件是否存在
if [ ! -f "$LOG_FILE" ]; then
echo "错误: 日志文件 $LOG_FILE 不存在"
exit 1
fi
# 创建iptables链(如果不存在)
iptables -N BLOCK_MALICIOUS_IPS 2>/dev/null
# 确保BLOCK_MALICIOUS_IPS链在INPUT链的最前面
iptables -C INPUT -j BLOCK_MALICIOUS_IPS 2>/dev/null || iptables -I INPUT 1 -j BLOCK_MALICIOUS_IPS
# 初始化文件
touch "$BLOCKED_IPS_FILE"
touch "$ATTACK_HISTORY_FILE"
# 函数:检查并修复iptables配置
check_iptables_config() {
echo "检查iptables配置..."
# 检查BLOCK_MALICIOUS_IPS链是否存在
if ! iptables -L BLOCK_MALICIOUS_IPS >/dev/null 2>&1; then
echo "创建BLOCK_MALICIOUS_IPS链..."
iptables -N BLOCK_MALICIOUS_IPS
fi
# 检查INPUT链中是否包含BLOCK_MALICIOUS_IPS
if ! iptables -C INPUT -j BLOCK_MALICIOUS_IPS 2>/dev/null; then
echo "将BLOCK_MALICIOUS_IPS链插入到INPUT链的最前面..."
iptables -I INPUT 1 -j BLOCK_MALICIOUS_IPS
fi
# 显示当前配置
echo "当前iptables配置:"
iptables -L INPUT -n --line-numbers | head -10
echo "BLOCK_MALICIOUS_IPS链规则:"
iptables -L BLOCK_MALICIOUS_IPS -n
}
# 函数:重新加载已封锁的IP到iptables
reload_blocked_ips() {
echo "重新加载已封锁的IP到iptables..."
if [ -f "$BLOCKED_IPS_FILE" ]; then
local count=0
while read -r ip; do
if [ -n "$ip" ]; then
# 检查iptables中是否已有此规则
if ! iptables -C BLOCK_MALICIOUS_IPS -s "$ip" -j DROP 2>/dev/null; then
iptables -A BLOCK_MALICIOUS_IPS -s "$ip" -j DROP
count=$((count + 1))
fi
fi
done < "$BLOCKED_IPS_FILE"
echo "重新加载了 $count 个IP到iptables"
fi
}
# 检查并修复iptables配置
check_iptables_config
# 重新加载已封锁的IP
reload_blocked_ips
echo "开始实时监控日志文件: $LOG_FILE"
echo "最大请求频率: $MAX_REQUESTS_PER_MINUTE 次/分钟"
echo "封锁时间: $BLOCK_DURATION 秒"
# 函数:封锁IP
block_ip() {
local ip=$1
local reason=$2
# 检查IP是否已经被封锁
if ! grep -q "^$ip$" "$BLOCKED_IPS_FILE"; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - 封锁IP: $ip (原因: $reason)"
# 添加到iptables(确保规则正确添加)
iptables -A BLOCK_MALICIOUS_IPS -s "$ip" -j DROP
# 验证规则是否添加成功
if iptables -C BLOCK_MALICIOUS_IPS -s "$ip" -j DROP 2>/dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - 成功添加iptables规则: $ip"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - 警告: iptables规则添加失败: $ip"
fi
# 记录到文件
echo "$ip" >> "$BLOCKED_IPS_FILE"
# 记录日志
echo "$(date '+%Y-%m-%d %H:%M:%S') - 封锁IP: $ip (原因: $reason)" >> /var/log/realtime_block.log
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - IP $ip 已经被封锁"
fi
}
# 函数:记录攻击历史
record_attack() {
local ip=$1
local attack_type=$2
local timestamp=$(date +%s)
echo "$ip|$attack_type|$timestamp" >> "$ATTACK_HISTORY_FILE"
}
# 函数:检查攻击历史
check_attack_history() {
local ip=$1
local attack_type=$2
local current_time=$(date +%s)
local time_window=300 # 5分钟时间窗口
# 清理过期的攻击记录
local temp_file="/tmp/temp_attack_history.txt"
while IFS='|' read -r record_ip record_type record_time; do
if [ $((current_time - record_time)) -lt $time_window ]; then
echo "$record_ip|$record_type|$record_time" >> "$temp_file"
fi
done < "$ATTACK_HISTORY_FILE"
mv "$temp_file" "$ATTACK_HISTORY_FILE" 2>/dev/null
# 统计该IP在时间窗口内的攻击次数
local attack_count=$(grep "^$ip|" "$ATTACK_HISTORY_FILE" | wc -l)
echo "$attack_count"
}
# 函数:分析新日志条目
analyze_new_logs() {
local new_lines="$1"
if [ -n "$new_lines" ]; then
if [ "$DEBUG_MODE" = true ]; then
echo "DEBUG: 分析新日志行数: $(echo "$new_lines" | wc -l)"
fi
# 统计IP请求频率
local ip_counts=$(echo "$new_lines" | awk '{print $1}' | sort | uniq -c | sort -nr)
if [ "$DEBUG_MODE" = true ] && [ -n "$ip_counts" ]; then
echo "DEBUG: IP统计结果:"
echo "$ip_counts"
fi
# 检查每个IP的请求频率
while read -r count ip; do
if [ -n "$ip" ] && [ "$count" -gt "$MAX_REQUESTS_PER_MINUTE" ]; then
record_attack "$ip" "high_frequency"
local total_attacks=$(check_attack_history "$ip" "high_frequency")
if [ "$total_attacks" -gt 2 ]; then
block_ip "$ip" "请求频率过高: ${count}次/分钟 (累计${total_attacks}次)"
fi
fi
done <<< "$ip_counts"
# 检查WordPress登录尝试
local wp_login_attempts=$(echo "$new_lines" | grep "POST /wp-login.php" | awk '{print $1}' | sort | uniq -c | sort -nr)
if [ "$DEBUG_MODE" = true ] && [ -n "$wp_login_attempts" ]; then
echo "DEBUG: WordPress登录尝试统计:"
echo "$wp_login_attempts"
fi
if [ -n "$wp_login_attempts" ]; then
while read -r count ip; do
if [ -n "$ip" ]; then
record_attack "$ip" "wp_login"
local total_attacks=$(check_attack_history "$ip" "wp_login")
if [ "$total_attacks" -gt 1 ]; then # 只要超过1次WordPress登录尝试就封锁
block_ip "$ip" "WordPress暴力破解: ${count}次尝试 (累计${total_attacks}次)"
fi
fi
done <<< "$wp_login_attempts"
fi
# 检查404错误(可能的扫描行为)
local error_404_attempts=$(echo "$new_lines" | grep " 404 " | awk '{print $1}' | sort | uniq -c | sort -nr)
if [ -n "$error_404_attempts" ]; then
while read -r count ip; do
if [ -n "$ip" ] && [ "$count" -gt 5 ]; then # 降低阈值到5次404错误
record_attack "$ip" "error_404"
local total_attacks=$(check_attack_history "$ip" "error_404")
if [ "$total_attacks" -gt 2 ]; then
block_ip "$ip" "扫描行为: ${count}次404错误 (累计${total_attacks}次)"
fi
fi
done <<< "$error_404_attempts"
fi
# 检查可疑的扫描行为(访问不存在的文件)
local suspicious_requests=$(echo "$new_lines" | grep -E "(\.php|\.asp|\.jsp|\.exe|\.bat|\.cmd)" | awk '{print $1}' | sort | uniq -c | sort -nr)
if [ -n "$suspicious_requests" ]; then
while read -r count ip; do
if [ -n "$ip" ] && [ "$count" -gt 3 ]; then # 降低阈值到3次可疑请求
record_attack "$ip" "suspicious"
local total_attacks=$(check_attack_history "$ip" "suspicious")
if [ "$total_attacks" -gt 1 ]; then
block_ip "$ip" "可疑扫描: ${count}次可疑请求 (累计${total_attacks}次)"
fi
fi
done <<< "$suspicious_requests"
fi
fi
}
# 函数:监控日志文件变化
monitor_log_file() {
echo "开始实时监控..."
# 记录当前文件行数
local last_line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
# 使用inotifywait监控文件变化
inotifywait -m -e modify "$LOG_FILE" | while read -r directory events filename; do
# 获取当前文件行数
local current_line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
if [ "$current_line_count" -gt "$last_line_count" ]; then
if [ "$DEBUG_MODE" = true ]; then
echo "DEBUG: 检测到新日志行,从第 $((last_line_count + 1)) 行开始"
fi
# 获取新增的日志行
local new_lines=$(tail -n +$((last_line_count + 1)) "$LOG_FILE" 2>/dev/null)
if [ -n "$new_lines" ]; then
# 记录分析前的封锁数量
local old_blocked_count=$(wc -l < "$BLOCKED_IPS_FILE" 2>/dev/null || echo 0)
# 分析新日志
analyze_new_logs "$new_lines"
# 获取分析后的封锁数量
local new_blocked_count=$(wc -l < "$BLOCKED_IPS_FILE" 2>/dev/null || echo 0)
# 根据模式输出不同的信息
if [ "$new_blocked_count" -gt "$old_blocked_count" ]; then
# 有新增封锁
if [ "$QUIET_MODE" = true ]; then
# 静默模式:只显示新增数量
echo "$(date '+%Y-%m-%d %H:%M:%S') - 新增封锁 $(($new_blocked_count - $old_blocked_count)) 个IP"
else
# 正常模式:显示详细信息
echo "$(date '+%Y-%m-%d %H:%M:%S') - 新增封锁 $(($new_blocked_count - $old_blocked_count)) 个IP,总计 $new_blocked_count 个"
fi
elif [ "$QUIET_MODE" = false ] && [ "$DEBUG_MODE" = false ]; then
# 非静默且非调试模式:显示处理状态(可选)
echo "$(date '+%Y-%m-%d %H:%M:%S') - 处理日志,当前封锁 $new_blocked_count 个IP"
fi
fi
# 更新行数
last_line_count=$current_line_count
fi
done
}
# 函数:定期清理过期封锁
cleanup_expired_blocks() {
while true; do
sleep 3600 # 每小时执行一次
local current_time=$(date +%s)
local temp_file="/tmp/temp_blocked_ips.txt"
# 重新创建封锁文件(简化处理)
if [ -f "$BLOCKED_IPS_FILE" ]; then
# 这里可以根据需要实现更精确的时间管理
# 目前简化处理,每小时清理一次
echo "$(date '+%Y-%m-%d %H:%M:%S') - 清理过期封锁" >> /var/log/realtime_block.log
fi
done
}
# 函数:测试模式
test_mode() {
echo "测试模式: 模拟恶意请求..."
# 创建测试日志条目
local test_log_entries=(
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
"192.168.1.100 - - [$(date '+%d/%b/%Y:%H:%M:%S')] \"GET / HTTP/1.1\" 200 1234"
)
# 将测试数据写入日志文件
for entry in "${test_log_entries[@]}"; do
echo "$entry" >> "$LOG_FILE"
sleep 0.1
done
echo "测试完成,请检查是否封锁了IP 192.168.1.100"
}
# 检查命令行参数
if [ "$1" = "--test" ]; then
test_mode
exit 0
elif [ "$1" = "--status" ]; then
echo "当前封锁状态:"
if [ -f "$BLOCKED_IPS_FILE" ]; then
local blocked_count=$(wc -l < "$BLOCKED_IPS_FILE" 2>/dev/null || echo 0)
echo "已封锁IP数量: $blocked_count"
if [ "$blocked_count" -gt 0 ]; then
echo "封锁的IP列表:"
cat "$BLOCKED_IPS_FILE"
fi
else
echo "暂无封锁的IP"
fi
exit 0
elif [ "$1" = "--debug" ]; then
DEBUG_MODE=true
echo "调试模式已启用"
elif [ "$1" = "--quiet" ]; then
QUIET_MODE=true
echo "静默模式已启用(只显示新增封锁)"
elif [ "$1" = "--minimal" ]; then
QUIET_MODE=true
DEBUG_MODE=false
echo "最小输出模式已启用(只显示新增封锁)"
elif [ "$1" = "--check-iptables" ]; then
echo "检查iptables配置..."
check_iptables_config
echo ""
echo "当前封锁的IP列表:"
if [ -f "$BLOCKED_IPS_FILE" ]; then
cat "$BLOCKED_IPS_FILE"
else
echo "暂无封锁的IP"
fi
exit 0
elif [ "$1" = "--attack-history" ]; then
echo "攻击历史记录:"
if [ -f "$ATTACK_HISTORY_FILE" ]; then
echo "IP地址 | 攻击类型 | 时间戳"
echo "------------------------"
while IFS='|' read -r ip type timestamp; do
time_str=$(date -d "@$timestamp" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "未知时间")
echo "$ip | $type | $time_str"
done < "$ATTACK_HISTORY_FILE"
else
echo "暂无攻击历史记录"
fi
exit 0
fi
# 启动清理进程
cleanup_expired_blocks &
CLEANUP_PID=$!
# 启动监控
monitor_log_file
# 清理进程
kill $CLEANUP_PID 2>/dev/null
使用说明:
### 基本使用 ```bash sudo ./realtime_block.sh ``` ### 调试模式 ```bash sudo ./realtime_block.sh --debug ``` ### 静默模式(只显示新增封锁) ```bash sudo ./realtime_block.sh --quiet ``` ### 最小输出模式(最简洁的输出) ```bash sudo ./realtime_block.sh --minimal ``` ### 查看当前状态 ```bash sudo ./realtime_block.sh --status ``` ### 测试模式 ```bash sudo ./realtime_block.sh --test ``` ## 配置参数 在脚本开头可以修改以下参数: ```bash LOG_FILE="/home/wwwlogs/h4ck.org.cn.log" # 日志文件路径 BLOCKED_IPS_FILE="/tmp/blocked_ips.txt" # 封锁IP记录文件 MAX_REQUESTS_PER_MINUTE=10 # 每分钟最大请求数 BLOCK_DURATION=3600 # 封锁时间(秒) DEBUG_MODE=false # 调试模式 QUIET_MODE=false # 静默模式 ```
最终效果:
这,文正写完了,雨也停了。










47 comments
不是偶然,也不是必然,是高产
嗐,瞎叨叨而已
叨叨也有看点:灵妹妹越来越美了。
瘦了很多,可惜腿露得太少。
不能露太多,被举报就麻烦了
擦边的都不管,还管这些呀
举报者去死吧
我域名解析,迄今为止屏蔽了国外的访问,国外ip太乱了。
是的,垃圾太多
第一张照片好帅,最后一张照片也很帅,可是车是要撞到了树上吗
当然不是,那是御用停车场好吗,下雨水太多了不想再往里停了,弄一身水。
厉害。我直接放弃用了。哈哈,
已经这么多弃用的了 ?
我也想放弃,切回去,后来又想着折腾一下,不然浪费了。😂
我也在渐进地暂停境外及福建全境IP访问。
境外环境的确会复杂很多
看来动态的站点使用 EO 都或多或少会有点问题?
有这个可能
看起来都是用免费的套餐加速动态站点的问题,静态站点还没有这个问题
是的,这种 cc 就让人不胜其烦
这车怎么掉沟里了,还回家吃饭吗
瞎说,那是御用停车场,哼
自己使用的话,可以把里面的测试192.168.1.100那些代码删除掉吧?
嗯,那些都没啥用。
看到最后我还以为车想去里头采蘑菇呢(原来是临时在旁边停车啊哈哈哈)
哈哈哈,采蘑菇,哪有蘑菇啊。
这个季节采蘑菇最好的时候,下雨像那草窝窝里可能有哈哈哈
我说昨天评论的时候怎么那么卡,同时打开两个页面,还有一个54x的错误,原来如此
嗯嗯,服务器爆啦。
我直接弃用了,懒得折腾,反正属于一打就死那种

这个东西怎么说呢,还是管点用的。
个人感觉 腾讯EdgeOne的实际体验也就那样了 想要好的 还得掏钱😌
这几天感觉不大稳定
细节遮挡车牌号😽
专业的
从来就不信任腾讯。
这事儿有现成的工具啊,何必自己写呢。fail2ban。
貌似是这么个事,哈哈哈
遇到这种节点地址发起恶意请求的我第一反应会不会是没配置 x real ip,不过看到日志里也有正常的 ip,看来还是你分析得到位。
这些 ip 也有可能是机房的代理过来的。
不过,这被 cc 挂了,的确有点出乎意料。
我的站在eo后台每天都有很多扫目录的。不晓得以前有没有。
这种也正常毕竟各种工具成本太低了。
之前我的博客也老挂,好像是 MySQL 的问题,具体也搞不清楚,不过最近真的是稳如老狗。搞得我都不敢升级系统了,现在 pve 9.0 了, debian 13.0 了。
嗯嗯,稳定了就别瞎折腾,😂
还是直接把WP登录文件改个名 简单点
是的,也想过这个办法,哈哈哈
所以还是因为 EdgeOne 导致的?尽管是间接的。
我没用 EO,觉得我国内访问本身就挺好。我的图片是国内图床的,因为我域名不想搞备案了,就没用自己域名。
这个也不一定,也有可能是其他的地方泄露网站原地址或者回源 ip 了。
那些机房的地址,也有可能是纯粹的代理工具。
不备案也好,能省很多麻烦事。
之前那看到没备案用og导致切回来也无法访问的,总感觉国内大厂做的东西还是最好别用。
免费这个东西,最终很多都难长久。
越是厉害,越有对手