fobe 0.1.13
fobe 0.1.13
安全:一轮认证 / 请求 / 出站 / 部署加固(本次发布主体)
运维侧需要留意的变化逐条列出:
- XFF 改为「从右往左取第一个不在
FOBE_TRUSTED_PROXIES内的地址」:旧版取最左,配 nginx 的$proxy_add_x_forwarded_for(追加)等于让每个客户端自选来源 IP,伪造私网地址还能落进 §4.3 的「永不加黑」豁免。README 的 nginx 示例改用$remote_addr(替换式)。服务端的右起取址让追加式写法也不再能被伪造,但替换式才是推荐配置。 /api/login前置一道与来源身份无关的全局闸(突发 30 / 每秒 10 / 并发 4):黑名单按 IP 计数而身份可以无限多,每次校验固定吃 64 MiB,内存上界必须不可伪造。- 失败计数随封禁一起过期:成功登录无条件删行,
RecordLoginFail发现存量行已过期先归零。此前一次手滑之后「每次再错一次就重封 30 分钟」,且残留行在面板上不可见。 - 会话新增 30 天服务端绝对有效期(
security.SessionExpired+ 清理任务);GET /api/sessions只回id_short指纹、不再回显 cookie 值;改密码 / revoke-all 会关闭对应的活动 WS(WS 只在握手时认证一次)。 must_change在服务端强制:除/api/me、/api/password、/api/logout外一律 409。- 请求体上限:默认 8 MiB,
/api/import与 MMDB 上传 64 MiB;http.Server补ReadTimeout/IdleTimeout(刻意不设WriteTimeout)。 /sub/{token}访问日志脱敏;/api/health去掉initialized,登录失败不再区分not_initialized。Sec-Fetch-Site跨站拒绝,只作用于变更类方法与两个有副作用的 GET(/api/export、/api/nodes/{id}/forwards?live=1)。- 安全响应头:CSP + X-Frame-Options + nosniff + Referrer-Policy + Permissions-Policy,https 时加 HSTS;Cookie 的
Secure由「请求自身是 https」或「server.public_url是 https」推导。 - 出站守卫(
security.CheckOutboundHost/GuardClient):AI providerbase_url与通知 webhook 拒绝 link-local(含云元数据 169.254.169.254)/未指定/组播,放行 loopback 与 RFC1918,并逐跳复检重定向;geoip.url补入同一守卫(写侧与下载侧,302 劫持引下载器进内网被拒)。上游响应体不再进入面板可见的错误文本。 - 主密钥解不开的密钥不再静默失效:启动记 error +
secret_unreadable审计,GET /api/settings回unreadable_secrets,测试按钮回notify_secret_unreadable,通知页顶部列出这些键。 cmd_result的 WHERE 补node_id与状态条件(曾可由任一探针终结他节点的排队命令);构建 VERSION 过字符集校验(agentupdate.SafeVersionPart,启动即拒非法值);sing-box 产物下载加解压总量上限;终端 WS 每节点上限 4。- 容器改以
fobe(10001) 提供服务:入口脚本只为接管/data属主短暂以 root 运行后su-exec降权;compose 加no-new-privileges+cap_drop: ALL并只回补入口所需能力。升级提示:首次启动会把/data卷内文件属主改成 uid 10001;用仓库 compose 的请一并git pull拿新的security_opt/cap_drop。
新增:面板登录失败告警
- 新增
security事件组(设置 → 通知里可开关):登录校验失败推login_failed(ip / count / max_fails),跨过 3 次阈值推login_blacklisted(ip / count / block_seconds);受保护网段的失败也推送但不计数(payloadprotected:true),一次失败只产生一条告警。 - 去重是全局 1 小时而非按 IP:
/api/login无需认证,按来源去重等于把消息条数交给换 IP 的攻击者;逐条来源地址仍在audit_logs。 - 新增
TestEveryEventGroupSwitchIsWritable钉住「每个事件组开关都可写」——此前allowedKeys手写,漏加新组会渲染出 PUT 被拒、GET 也不返回的开关。
修复
- 节点设置保存后「已保存」不可见:成功提示被表单的 key 重挂载清掉(key 绑节点数据是有意设计),现在提示上移到父级卡片标题行,跟随基础设置页 3 秒自动清除,并用 ref 存定时器句柄(连续保存不会被旧定时器提前清掉)。
- 通知测试的 i18n 键:页面调
feishu_test_secret_unreadable,字典里加的却是零引用的err_notify_secret_unreadable——tsc全绿,运行时把键名原样显示给用户。已改名对齐(zh/en 同步)。 - Debug 访问日志也脱敏:此前只有 5xx 分支走
logPath(),日志级别开到 Debug 时/sub/<token>仍明文入库。 - 出站守卫补拦一般组播:原条件只挡两个组播子类,实测
239.1.2.3、ff05::1被放行,与函数注释和 design §12.5 不符(只更严不更松)。
重构(行为不变)
internal/agent/singbox.go(1526 行)按 §9 缝线拆为singbox_local/singbox_firewall/singbox_install/singbox_gates/singbox_proc五个文件;删死代码compareVersions/versionPart/atoiSafe/runQuiet/service.InstallAgent;fetchChecksum委托同包fetchExpectedSHA256,同一二进制里不再有两份 sidecar 解析器。httpapi新增writeStoreErr/decodeReqhelper(替换 34 + 30 处样板,语义非统一的 22 处刻意保留);store的 scan 族推广到 ai_models / alerts / commands / templates;validateSettingValue成为PUT /api/settings与 §17 导入共用的单值校验源——导入侧因此变严(坏 theme / webhook URL 现在会被丢弃)。- 前端新增
useAsyncAction,收敛 EditServer / SettingsAI / Settings 三页 29 处 busy/err/msg 样板;新增ErrorState、chartShared.tsx共享件;删 26 个零引用 i18n 键与若干死代码。
无 schema / 无 wire 变更
- 数据库 schema 未动(仍 16),
internal/protocol未动,无新增必填环境变量。 - VERSION = 0.1.13,镜像仅 linux/amd64。