路径探测自动封禁方案(临时黑名单)
项目:igozhang(Go 单二进制 Web 文件服务)
实施日期:2026-07-03
方案:滑动窗口 +ip-auto-ban.json临时封禁(方案一)
ENV
| 类别 | 版本 / 信息 |
|---|---|
| 操作系统 | Windows 10(10.0.19045) |
| Shell | PowerShell 5.1(Build 19041) |
| Go(构建机) | go1.26.0 windows/amd64 |
| 模块 | github.com/igokorea/igozhang,go.mod 声明 go 1.22 |
| 应用 | igozhang 单二进制,默认监听 :3003 |
| 数据目录 | DATA_ROOT/secret07/(配置、黑名单、自动封禁 JSON) |
| 部署形态 | 容器 / K8s(hostPath 挂载 DATA_ROOT);探针 GET /health |
| 相关既有能力 | 永久黑名单 ip-blacklist.conf;API 分钟桶限流 light_rl.go;运维看板 /ops |
现象
站点对外提供目录浏览、文章、MP3、文件中转等能力。运维侧观察到(或预期存在)典型路径扫描行为:
- 同一来源 IP 在极短时间内连续请求大量不同 URL;
- 其中包含大量不存在路径(404)或敏感路径试探(如
/wp-admin、/.env等); - 现有 永久黑名单需人工在
/ops看板维护,无法自动应对突发扫描; - 既有 browse/stash 按 IP 限流仅覆盖单个 API,无法识别「跨路径广度探测」。
结论与根因
结论:已在最早 HTTP 中间件 requestGate 落地路径探测自动封禁——同一 IP 在 60 秒内访问 ≥20 个不同路径时,自动临时封禁 1 小时(全站 403),状态持久化至 secret07/ip-auto-ban.json,不写入永久黑名单。
根因:
- 防护层缺口:永久黑名单 + 单接口限流,缺少「单位时间内不同路径数」这一扫描特征检测;
- 访客看板
recordVisitor只记录可识别页面/功能,无法覆盖大量 404、多段路径扫描; - 临时封禁若仅内存实现,Pod 重启后失效,对 K8s 滚动发布不可靠。
问题形成原因推测:公网暴露的 Web 服务长期会遭遇自动化漏洞扫描器;扫描器策略是「广撒网、低单路径 QPS」,恰好绕过按单 API 的分钟限流,直到人工发现异常访问日志才拉黑,响应滞后。
定位与推导过程
1. 确认现有拦截链路与缺口
cd e:\gitee\code\igozhang
rg "requestGate|isIPBlacklisted|allowBrowseRL" cmd/igozhang -n
关键输出(摘要):
main.go:http.ListenAndServe(..., requestGate(dataRoot, logRequests(handler)))— 全站最早中间件;ip_blacklist.go:永久黑名单,命中 403;light_rl.go:browseRLPerMinute=150、stashRLPerMinute=80,仅覆盖/api/browse、/api/stash;visitor_board.go:recordVisitor经visitorActionFromRequest过滤,多段路径 / 纯 404 不计入看板 trail。
推导:检测逻辑必须独立于访客看板,且在 requestGate 内、业务 handler 之前执行;临时封禁应与 ip-blacklist.conf 分离。
2. 参考既有「临时锁定 + JSON 持久化」模式
rg "opsAuthFailRL|persistOpsAuthFailRL" cmd/igozhang -n
ops_auth_rl.go 已实现:内存 map + secret07/ops-auth-rl.json 落盘,重启可恢复。路径探测封禁复用同一模式,文件为 ip-auto-ban.json。
3. 单元测试验证阈值与持久化
cd e:\gitee\code\igozhang
go test ./cmd/igozhang/ -run "TestPathProbe" -count=1 -timeout 15s
关键输出:
ok github.com/igokorea/igozhang/cmd/igozhang 0.256s
覆盖:阈值触发、同路径去重、排除前缀、JSON 持久化重载、requestGate 返回 403。
方案
架构
请求 → 永久黑名单(ip-blacklist.conf)? → 自动封禁(ip-auto-ban.json)? → 记录路径并判定 → 业务
↓403 ↓403 ↓触发则403
中间件接入点(visitor_board.go):
if ip != "" && isIPBlacklisted(ip) {
writeIPForbidden(w)
return
}
if ip != "" && !checkPathProbeGate(w, r, dataRoot) {
return
}
核心模块:cmd/igozhang/path_probe_ban.go
配置段:igo.conf [security]
运维:/ops 看板展示 autoBans,POST /api/ops/auto-ban 手动解除。
配置(DATA_ROOT/secret07/igo.conf)
修改后须重启 Pod(无热加载)。源码:cmd/igozhang/igo.conf,同步命令:
cd e:\gitee\code\igozhang
go generate ./cmd/igozhang/...
当前生产建议配置:
[security]
path_probe_enabled = on
path_probe_window = 1m
path_probe_max_paths = 20
path_probe_ban_duration = 1h
path_probe_exclude_prefix = /stream/mp3/,/assets/doc-pic/
参数对照
| 参数 | 当前值 | 建议值 | 原因 | 预期效果 |
|---|---|---|---|---|
path_probe_enabled |
on |
on |
公网站点建议常开 | 自动拦截扫描,无需人工盯日志 |
path_probe_window |
1m |
1m(可调 30s–2m) |
与需求「一分钟内 20 路径」一致 | 60s 滑动窗,边界无分钟桶漏洞 |
path_probe_max_paths |
20 |
20(高误伤时可试 25–30) |
正常用户极少 1 分钟内访问 20 个不同页面 | 扫描器广撒网时快速触发 |
path_probe_ban_duration |
1h |
1h(反复扫描可改 2h) |
临时惩罚,避免永久误伤 | 封禁期内全站 403,到期自动解除 |
path_probe_exclude_prefix |
/stream/mp3/,/assets/doc-pic/ |
保持;若 /files/ 误伤可加前缀 |
MP3 连播、文章多图会短时间产生多 URL | 降低正常用户误封概率 |
内置不计入(代码固定,无需配置):/health、/favicon*、/api/ops/*。
构建与部署
cd e:\gitee\code\igozhang
go build -o igozhang.exe ./cmd/igozhang
本地验证(示例):
$env:DATA_ROOT = "E:\temp\igozhang.cn"
.\igozhang.exe
容器发版按项目惯例:
docker build -t krccr.ccs.tencentyun.com/igokorea/igozhang:$(date +%Y%m%d-%H%M%S) .
验证方法
1. 自动化测试
go test ./cmd/igozhang/ -run "TestPathProbe|TestRequestGate" -count=1
2. 运行日志确认触发
封禁触发时写入 secret-log/igozhang.log:
auto-ban: ip=203.0.113.x reason=path_probe paths=20 until=2026-07-03T16:xx:xx+08:00
3. 手工模拟扫描(勿对生产他人 IP 测试)
$base = "http://127.0.0.1:3003"
1..21 | ForEach-Object { Invoke-WebRequest -Uri "$base/probe-$_" -UseBasicParsing -ErrorAction SilentlyContinue }
Invoke-WebRequest -Uri "$base/" -UseBasicParsing
# 预期:最后一次或第 21 次后,同 IP 请求返回 403 Forbidden
4. 持久化文件
Get-Content "$env:DATA_ROOT\secret07\ip-auto-ban.json"
示例结构:
{"203.0.113.x":{"until":1719999999,"reason":"path_probe","paths":21,"bannedAt":1719996399}}
5. 运维看板
- 登录
/ops→ 「自动封禁」列表可见 IP 与剩余时间; - 手动解除:
POST /api/ops/auto-ban,body{"token":"…","action":"remove","ip":"x.x.x.x"}。
后续预防与运维建议
- 误封处理:优先在
/ops看板「自动封禁」解除;反复误伤则调高path_probe_max_paths或增加exclude_prefix; - 永久拉黑:对确认的恶意源,仍用看板「黑名单」写入
ip-blacklist.conf(自动封禁到期后会再次访问); - 监控:对
igozhang.log中auto-ban:行做简单告警(如 1h 内 >5 次); - 维护:每小时维护任务会自动清理过期
ip-auto-ban.json条目,无需 cron; - 备份:
ip-auto-ban.json不进文章 ZIP 导出,属运行时状态;永久策略仍以ip-blacklist.conf为准。
涉及文件清单
| 文件 | 作用 |
|---|---|
cmd/igozhang/path_probe_ban.go |
滑动窗统计、封禁判定、JSON 持久化 |
cmd/igozhang/visitor_board.go |
requestGate 接入 |
cmd/igozhang/ops_config.go |
[security] 配置解析 |
cmd/igozhang/igo.conf |
配置真源(embed) |
cmd/igozhang/ops.html / ops_page.go |
看板展示与解除 API |
secret07/ip-auto-ban.json |
运行时临时封禁(自动生成) |
附录:与永久黑名单的区别
| 维度 | 永久黑名单 | 路径探测自动封禁 |
|---|---|---|
| 文件 | ip-blacklist.conf |
ip-auto-ban.json |
| 触发 | 人工 / ZIP 导入 | 自动(路径数超阈) |
| 时长 | 永久直至删除 | 默认 1h TTL |
| ZIP 备份 | 非空时导出 | 不导出 |