diff --git a/docs/cli.md b/docs/cli.md index 8154572..183c332 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -298,7 +298,7 @@ pg_dump -Fc > before-$(date +%F-%H%M).dump | 子命令 | 作用 | 改数据 | |---|---|---| | `hits` | 列出捕获到的凭据(时间倒序,密码默认脱敏) | 否 | -| `stats` | 来源 IP / 路径排行(库)+ 凭据排行(文件) | 否 | +| `stats` | 来源排行(IPv6 按 /64 聚合)+ 路径排行(库)+ 凭据排行(文件) | 否 | | `paths` | 列出当前生效的诱饵路径 | 否 | | `size` | 表行数、磁盘占用、凭据文件体积 | 否 | | `prune` | 按保留期清元数据 + 凭据文件 | 是 | @@ -320,6 +320,8 @@ pg_dump -Fc > before-$(date +%F-%H%M).dump (目录 0700、文件 0600),数据库里只有 IP / 路径 / UA / 是否封禁这些元数据。 所以 `hits` / `paths` **不连数据库**,DB 挂了也能查。 - 密码默认脱敏(保留首尾各 1 位)。终端输出常被贴进工单或截图,`--show-password` 只在确有取证需要时开。 +- `hits` / `stats` 里的 `ip` 是**精确**客户端地址;封禁用的是 `shared.LimitKey` 折叠后的键 + (IPv6 为 `/64` 前缀)。两者在 IPv6 下不一致是正常的,别拿邮件里的精确地址去对封禁键。 - `purge-creds` 用于清掉早期版本存在 `honeypot_hits` 上的明文列(AutoMigrate 不删既有列)。 执行前先 `pg_dump`;它只处理当前库,**历史备份与从库里的明文需要自行清理**。 - 封禁语义:GET 探测走常规阈值(默认 10 次 / 60 秒),**提交凭据才一次即封**。 diff --git a/docs/honeypot.md b/docs/honeypot.md index f1193f8..eb895b2 100644 --- a/docs/honeypot.md +++ b/docs/honeypot.md @@ -74,6 +74,34 @@ honeypot_cred_dir = data/honeypot-creds 纯探测也不会被刷爆:元数据落库按「每 IP 每分钟一条」采样,凭据则是「有就存」——那才是要保的东西。 +### 封的是一把「键」,不是一个人 + +`autoban` 的表就是内存里的 `map[键]到期时间`,键由 `shared.LimitKey` 生成: + +- IPv4 → 原样(`203.0.113.7`) +- IPv6 → 折叠到 `/64`(`2408:8207:1234:5678::/64`),防止在单个 /64 内轮换地址绕过阈值 + +所以告警邮件里**可能有两个地址**: + +| 字段 | 含义 | +|---|---| +| 来源 IP | 精确客户端地址,取证与对账用 | +| 封禁范围 | 实际写进封禁表的键;IPv6 下是 /64 前缀,整段一起封(IPv4 下与来源 IP 相同,不重复显示) | + +两个直接后果,排障时先记住: + +1. **封禁只在内存里** —— 重启进程即解除,邮件不会撤回; +2. **封的键与请求算出来的键必须一致** —— 换地址族(IPv6 访问 / IPv4 回源)或换 /64 前缀就不会被拦。 + +每次建封都会打一条 WARN,键、原因、时长、到期时间都在里面: + +```bash +journalctl -u tamabox -n 50 | grep "已自动封禁来源" +# ban_key=2408:8207:1234:5678::/64 reason=蜜罐提交凭据 duration=24h0m0s expires_at=... +``` + +`ban_key` 就是被封的键,与邮件里的「封禁范围」一致。若这里没有记录,说明当时根本没建封(邮件可能是别的通道发的);若有记录但仍能访问,就去比对请求算出来的键。 + --- ## 4. 凭据存在哪里 @@ -103,6 +131,9 @@ data/honeypot-creds/creds-2026-10-21.jsonl 数据库里只保留「发生过一次带凭据的提交」这个事实(`honeypot_hits.had_fields`),以及 IP / 路径 / UA / 是否封禁这些元数据。 +> `ip` 两处存的都是**精确客户端地址**(IPv6 保留完整地址),不是封禁用的 `/64` 聚合键 —— +> 取证要能定位到具体主机。想知道「封了多大范围」,看告警邮件的「封禁范围」或日志里的 `ban_key`。 + > ⚠️ 这些是**外部攻击者提交的真实账号密码**,很可能是从别处泄露后复用的。请按敏感数据对待:不要提交进版本库(`/data/` 已在 `.gitignore`)、不要参与备份、不要贴到公开群组。 --- @@ -158,7 +189,7 @@ pg_dump -Fc > before-purge-$(date +%F-%H%M).dump ```bash ./tamabox honeypot hits # 最近 24h 的凭据(密码默认脱敏) ./tamabox honeypot hits --ip 1.2.3.4 --show-password -./tamabox honeypot stats --days 7 # 来源 IP / 路径排行(库)+ 凭据排行(文件) +./tamabox honeypot stats --days 7 # 来源排行(IPv6 按 /64 聚合)+ 路径排行 + 凭据排行 ./tamabox honeypot paths # 当前生效的诱饵路径 ./tamabox honeypot size # 表行数 + 磁盘占用 + 凭据文件体积 ./tamabox honeypot prune --days 30 # 手动按保留期清(元数据 + 凭据文件) @@ -172,6 +203,7 @@ pg_dump -Fc > before-purge-$(date +%F-%H%M).dump | 现象 | 原因 | 处理 | |---|---|---| +| **邮件说封了,但还能正常访问** | ① 进程重启过(封禁是内存态)② 后续请求换了地址族 / 换了 /64 前缀 ③ `trust_proxy` 未配,封的是反代地址 | 看日志里 `已自动封禁来源` 的 `ban_key`,与 `honeypot hits` 里的 IP、以及本机 `curl -6 ifconfig.co` 比对;见 §3「封的是一把键」 | | `/wp-admin` 还是 302 | `enable_honeypot` 未开 / 配置未重启生效 | 看启动日志;`honeypot paths` 确认开关状态 | | 记录里 `ip` 是 `127.0.0.1` | `trust_proxy` 未配 | 见 §5;**注意此时封禁会封全站** | | 整站都返回 403 | 蜜罐把反代 IP 封了(上一条的后果) | 重启进程(封禁是内存态)或等 `honeypot_ban_minutes` 到期 | @@ -186,6 +218,7 @@ pg_dump -Fc > before-purge-$(date +%F-%H%M).dump - **验证二进制里有没有某个中文字符串,不能用 `strings`** —— 它默认只输出可打印 ASCII,UTF-8 中文整段被丢弃,`strings x | grep 中文` 恒为 0。用 `grep -ac "中文字符串" x`。 - **替换二进制 ≠ 生效**,进程必须重启;前台跑 `./tamabox web` 若立刻返回,通常是端口被旧进程占着。 +- **邮件里的「来源 IP」不是封禁键**:IPv6 下前者是精确地址、后者是 /64 前缀,拿前者去查后者永远对不上。判「封没封」只看 `ban_key`。 ---