docs(memory): 记录 ini 键名静默失效的根因与配置约定

This commit is contained in:
tamakyi
2026-10-21 13:41:49 +08:00
parent d300167a30
commit bfdc5089c5
2 changed files with 56 additions and 0 deletions
+41
View File
@@ -154,3 +154,44 @@
- ⚠️ 库名带连字符要用双引号:`CREATE DATABASE "tamabox-cli-test"`。 - ⚠️ 库名带连字符要用双引号:`CREATE DATABASE "tamabox-cli-test"`。
- ⚠️ Edit 工具改 CRLF 文件会保持 CRLF,但**改写时我打的字会转码损坏**(出现过「实测指事/程定/不图」)→ 改完必须逐行读回复核。 - ⚠️ Edit 工具改 CRLF 文件会保持 CRLF,但**改写时我打的字会转码损坏**(出现过「实测指事/程定/不图」)→ 改完必须逐行读回复核。
- 仓库内统一存 LF,工作区 CRLF 由 `core.autocrlf=true` 自动转换 —— 提交时 warning 属正常,不是行尾被改。 - 仓库内统一存 LF,工作区 CRLF 由 `core.autocrlf=true` 自动转换 —— 提交时 warning 属正常,不是行尾被改。
---
## 配置键名静默失效的根治(commit d300167,已 push)
### 现场故障
用户配置 `honeypot_retention_days <= 30`,日志报「蜜罐自动清理未启用:honeypot_retention_days <= 0」。
### 根因(探针实测 ini v1.67.0,不是读源码猜的)
1. **键名多一个 t**(retention vs retain)→ ini 对未知键**静默忽略**,保留期成 0。
2. **`<= 30` 这种写法**让 ini 把键名解析成 `"honeypot_retention_days <"`(带尾随比较符),
实测输出:`key="honeypot_retention_days <" value="30"` —— 更匹配不上。
3. **我之前那条警告文案里写的也是错键名** `honeypot_retention_days`,
等于在误导运维往错的键上配 —— 教训:警告文案里的键名必须与代码读的键名逐字一致。
### 实测到的 ini 行为(重要,写 ini 相关代码前必读)
- `MapTo` 在**键缺失或值非法时不覆盖**目标字段 → 可以安全预置缺省值。
- 键名**大小写敏感**(`Honeypot_Auto_Clean` 不生效)。
- `HasKey` 能区分「缺失」与「显式 0」。
- 段不存在时 `MapTo` 返回 nil 且不修改目标。
- bool 的解析是「有键才赋值」→ **未配置与显式 false 无法用零值区分**,必须靠 HasKey。
### 修复三层
- `internal/conf/keynorm.go`(新):键名归一化(修剪空白、剥尾随 `<` `>` `=`、别名表纠错)。
⚠️ 归一化结果必须命中该段**合法键名白名单**才采用 —— 不认识的键不猜,交给未知键告警。
- `internal/conf/unknown.go`(新):启动逐段比对键名,未知键 WARN + 给出最接近的合法键名。
合法键名由**反射从结构体 ini tag 得出**(单一事实来源,新增配置项自动纳入)。
- `internal/conf/honeypot_defaults.go`(新):蜜罐缺省值语义修正
(保留期缺省 30、auto_clean 缺省 true 用 HasKey 判、max_rows 缺省 200000、threshold 缺省 1)。
- 归一化必须在 `MapTo` **之前**做 —— 等 MapTo 跑完,错拼的键早被丢掉了。
### 顺带清理
`app.sample.ini` 里两条死配置:`app.title`(代码从未读,站点标题走 i18n `site.title`)、
`server.xsrf_expire`(无对应字段)。留着会让新检测对官方模板误报。
### 踩坑
- ⚠️ **Python 脚本里写 Go struct tag 的反引号** → bash 命令替换报错。
改用 Edit 工具逐处改(Edit 对 CRLF 文件保持 CRLF,但**我打的字可能转码损坏** → 改完逐行读回复核)。
- ⚠️ `gofmt -l` 对 CRLF 文件一律报警(gofmt 要求 LF)—— 那是既有状态,不是我的改动。
- ⚠️ 我自己的测试预期写错过两次(别名生效后的期望值、static_version 不是 ini 键)。
**测试失败要先怀疑自己的预期**,别急着改产品代码。
+15
View File
@@ -37,3 +37,18 @@
`grep | cat -A` 不显示 `^M` 极易误判 → Python 替换用 `\r\n` 锚点,写后统计 `b'\r\n'` 复核。 `grep | cat -A` 不显示 `^M` 极易误判 → Python 替换用 `\r\n` 锚点,写后统计 `b'\r\n'` 复核。
- ⚠️ Go 里 `time.Duration(60)` = 60 **纳秒**,测试写裸数字会让封禁/冷却瞬间过期 → 一律 `60 * time.Minute`。 - ⚠️ Go 里 `time.Duration(60)` = 60 **纳秒**,测试写裸数字会让封禁/冷却瞬间过期 → 一律 `60 * time.Minute`。
- ⚠️ 测 `IsBanned` → 403 必须注册中间件(`f.Use(autobanMiddleware(ab, hp))`),只注册路由+NotFound 会被绕过。 - ⚠️ 测 `IsBanned` → 403 必须注册中间件(`f.Use(autobanMiddleware(ab, hp))`),只注册路由+NotFound 会被绕过。
## 配置(ini)相关约定
- ⚠️ **ini 对未知键静默忽略** → 键名写错 = 配置不生效且无任何现场线索。
已加两道防线(`internal/conf/keynorm.go` + `unknown.go`):
键名归一化(自动纠正常见误拼)+ 未知键启动告警(附最接近的合法键名)。
- ⚠️ **键名必须与代码逐字一致**,包括警告文案里提到的键名 —— 写错等于误导运维。
- ⚠️ `MapTo` 在**键缺失/值非法时不覆盖**目标字段 → 可安全预置缺省值。
- ⚠️ **bool 的 ini 解析是「有键才赋值」** → 「未配置」与「显式 false」无法用零值区分,
必须用 `HasKey` 判定。缺省为 true 的开关尤其要小心(否则未配置 = 关闭)。
- 归一化必须在 `MapTo` **之前**执行,否则错拼的键已被丢弃。
- 合法键名白名单由**反射从结构体 ini tag 得出**,是单一事实来源;新增配置项自动纳入。
- `TestSampleIniHasNoUnknownKeys` 回归检测:官方样例配置零误报。
改结构体字段名时必须同步 `conf/app.sample.ini`,否则这个测试会失败。
- app.sample.ini 里不要留「代码中从未读取」的死配置(`title` / `xsrf_expire` 已清理)。