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