Files
Nexus/docs/release/20260708/Nexus-安全巡检阶段4-settings-auth-btpanel-20260707.md
T
r 9b583ccc91
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / e2e (push) Blocked by required conditions
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
docs(release): 主与 release 分支合并功能清单 + 导入 release 文档目录
便于 Gitea 上 main ↔ release/20260708-axs 合并评审与冲突对照。
2026-07-09 01:14:32 +08:00

11 KiB
Raw Blame History

Nexus 安全巡检阶段 4Settings/Auth 与宝塔长期会话边界审查

时间:2026-07-07 对象:测试机 /opt/nexus-dev-current,分支 audit/security-review 最新提交:524acd5 fix-stop-exposing-global-api-key-in-settings

本报告不输出任何密码、API Key、Token、Cookie、PEM 内容。


1. 本轮确认结果

1.1 全局 API Key 不显示:已完成代码层修复

只读检查结果:

  • 后端 /api/settings/api-key/reveal 已删除。
  • 前端 SettingsPage 中全局 API Key 显示/复制逻辑已删除。
  • 源码残留仅有:
    • server/api/servers.py 两处注释仍提到旧名字 reveal_api_key
    • frontend/src/utils/auditLabels.ts 仍有旧审计 label reveal_api_key: 查看 API 密钥

安全判断:

  • 残留注释/label 不会造成 API Key 暴露。
  • 建议后续做一次小清理,避免误导维护者和审计日志翻译。

1.2 当前代码没有全局 API 限速

沿用上一份报告结论:

  • 没有 slowapi / fastapi-limiter / nginx limit_req 一类全局限速。
  • 429 主要来自:
    • 管理员登录失败锁定:5 次 / 15 分钟。
    • Agent 脚本回调:job 10 次 / 60 秒,IP 60 次 / 60 秒。
  • 大多数“速度限制”是远程任务并发保护,不是 HTTP API 限速。

2. 宝塔 2 小时后刷新掉线:当前代码仍有一个关键边界

2.1 当前一键登录链路

sequenceDiagram
    participant UI as Nexus 前端
    participant API as /api/btpanel/servers/{id}/login-url
    participant SVC as BtPanelService.create_login_url
    participant Redis as Redis login_url_lock
    participant SSH as ssh_bootstrap_panel
    participant BT as 宝塔面板

    UI->>API: 请求一键登录 URL
    API->>SVC: create_login_url(server_id)
    SVC->>Redis: 获取每台服务器分布式锁
    SVC->>SSH: best-effort 检查/patch 宝塔 task.py session cleanup
    SVC->>BT: config?action=set_temp_login(expire_time=now+86400)
    BT-->>SVC: 返回 tmp_token 或登录 URL
    SVC-->>UI: /login?tmp_token=...

2.2 已修复的问题

当前代码已经解决:

  1. tmp_token 使用 86400 秒 TTL,而不是宝塔默认较短 TTL。
  2. 登录 URL 不缓存,因为 tmp_token 是一次性链接。
  3. 对同一台服务器生成登录 URL 时使用 Redis 锁,避免并发覆盖。
  4. 登录前会 best-effort patch 宝塔 /www/server/panel/task.py:把硬编码 if f_time > 3600: 改成读取 public.get_session_timeout() or 86400

2.3 仍可能导致“2 小时后刷新掉线”的关键点

当前 patch 的语义是:

session_cleanup_timeout = int(public.get_session_timeout() or 86400)
if f_time > session_cleanup_timeout:
    删除 session 文件

这意味着:

  • 如果宝塔面板里已经配置了 session timeout,例如 7200 秒(2 小时),patch 会尊重这个值。
  • or 86400 只在没有配置值时生效。
  • 所以用户遇到“打开 10 个宝塔,2 小时后陆续刷新掉线”,仍可能是目标宝塔的 session_timeout 本身就是 7200 秒。

结论:

现在的修复主要解决 task.py 硬编码 3600 秒清理问题,但不一定强制把宝塔会话生命周期提升到 24 小时。如果目标面板配置仍是 2 小时,会继续 2 小时后掉线。

2.4 并发打开 10 个宝塔的边界

当前 Redis 锁可以保证同一台服务器不会同时生成多个互相覆盖的 tmp_token。但用户说的是“同时打开 10 个不同宝塔”,这属于不同 server_id:

  • 每台服务器独立锁,不互相等待。
  • 各自的 session 生命周期取决于各自宝塔面板配置和 task.py patch 状态。
  • 10 台里有的先掉、有的后掉,符合“每台服务器 session_timeout/task.py/定时清理状态不一致”的表现。

3. Settings/Auth 发现的问题与建议

3.1 中风险:Telegram Token reveal 不需要二次认证

位置:

  • server/api/settings.py /telegram/reveal-token
  • server/api/settings.py /telegram/offline/reveal-token

现状:

  • 需要 JWT。
  • 会写审计日志。
  • 但不像密码预设 reveal、SSH Key reveal 那样要求当前密码二次认证。

风险:

  • 如果管理员 access token 被盗,在 token 有效期内可直接读取 Telegram Bot Token。
  • 虽然这是管理员功能,但与同文件中“密码预设/SSH 私钥 reveal 需要二次认证”的策略不一致。

建议:

  • 若继续允许显示 Telegram Token,建议改成 current_password 二次认证。
  • 如果你倾向于“全局 API Key 一样不显示”,也可以直接删除 Telegram Token reveal,只允许覆盖写入。

3.2 中风险:TOTP setup/enable 不要求当前密码

位置:

  • server/api/auth.py /totp/setup
  • server/api/auth.py /totp/enable

现状:

  • 需要 JWT。
  • 只允许对自己操作。
  • enable 前会校验 TOTP code。
  • 但不要求当前密码。

风险:

  • 如果 access token 被盗,攻击者可以生成自己的 TOTP secret 并启用,造成账号被锁或持久化控制风险。

建议:

  • setup 或 enable 至少其中一步要求当前密码。
  • 更严格方案:setup、enable、disable、password change 都统一走“敏感操作二次认证”。

3.3 低风险:全局 API Key 相关审计 label/注释残留

位置:

  • server/api/servers.py 注释里仍有 same pattern as reveal_api_key
  • frontend/src/utils/auditLabels.ts 仍有 reveal_api_key: 查看 API 密钥

风险:

  • 不暴露 secret。
  • 但会误导后续开发,以为还有 reveal_api_key 功能。

建议:

  • 后续清理为通用描述,例如 same pattern as sensitive operation re-auth
  • 删除前端旧 label 或保留但标注 legacy。

3.4 低风险:refresh 接口仍接受 body refresh_token fallback

位置:

  • server/api/auth.py /api/auth/refresh

现状:

  • 优先读取 HttpOnly cookie。
  • 没有 cookie 时允许从 body 读取 refresh_token。

风险:

  • 当前注释解释为兼容 fallback。
  • 如果没有明确的历史客户端需要,body fallback 增加了一条 token 传输路径。

建议:

  • 保守:保留但加审计/废弃期。
  • 严格:只允许 HttpOnly cookie refresh。

4. 下一步修改方向:至少 6 个方案供选择

按你的要求,涉及安全策略/代码改动前先给多个选项,不直接替你定。

方案 A:只修宝塔长期会话,强制把宝塔 session timeout 提升到 24 小时

改动:

  • ssh_bootstrap.py 里增加写入/修正宝塔 session timeout 配置的逻辑。
  • 登录前 best-effort 确保目标面板的 session timeout >= 86400。
  • 保留现有 task.py patch。

好处:

  • 最直接解决“2 小时后刷新掉线”。
  • 不影响 Nexus 自身安全策略。

风险:

  • 会修改宝塔面板自身配置。
  • 如果用户希望宝塔短会话,这会改变预期。

是否需要重启/部署:

  • 需要重建/重启 Nexus 后端容器。
  • 对目标宝塔可能需要 reload bt。

推荐度:高,针对当前痛点最直接。

方案 B:宝塔 session timeout 只做检测和告警,不自动修改

改动:

  • Nexus 登录前 SSH 检测宝塔当前 session timeout。
  • 如果低于 86400,在 UI 或接口响应中返回 warning。
  • 不自动改宝塔。

好处:

  • 风险低,不擅自改目标面板。
  • 可解释每台为什么 2 小时掉。

风险:

  • 不自动解决问题,仍需要人工确认/点击修复。

推荐度:中。

方案 C:给宝塔登录 URL 接口增加“会话保活/会话诊断”返回字段

改动:

  • /api/btpanel/servers/{id}/login-url 返回:
    • session_cleanup_ttl_ok
    • session_cleanup_ttl_patched
    • session_timeout_seconds
    • warnings
  • 前端一键登录时提示“当前宝塔会话约 2 小时后失效”。

好处:

  • 用户能看到问题原因。
  • 便于排查 10 台里哪台会掉。

风险:

  • 需要补前端显示。
  • 只诊断,不一定修复。

推荐度:中高,适合和 A/B 搭配。

方案 D:敏感 Token 全部不显示,只允许覆盖写入

改动:

  • 删除 Telegram Token reveal。
  • 保留 token 是否已设置状态。
  • 修改前端:显示“已设置,留空不改”,不提供查看/复制。

好处:

  • 和“全局 API Key 不显示”策略一致。
  • 降低 access token 被盗后的进一步泄露面。

风险:

  • 管理员无法从 Nexus 找回 Telegram Token,只能重填。

推荐度:中高,安全更干净。

方案 E:保留 Telegram/密码/SSH reveal,但统一要求二次认证

改动:

  • Telegram reveal 请求体增加 current_password
  • 前端弹窗输入当前密码。
  • 可以同时把 reveal 审计统一标准化。

好处:

  • 兼顾可用性和安全。
  • 与密码预设、SSH Key reveal 保持一致。

风险:

  • 使用上多一步。

推荐度:高,如果你还想保留“查看 Telegram Token”。

方案 F:统一敏感操作二次认证框架

改动范围更大:

  • TOTP setup/enable/disable。
  • 修改密码。
  • reveal 密码预设/SSH Key/Telegram Token。
  • 宝塔 API Key 修改/显示类操作。
  • 服务器凭据 reveal/更新。

好处:

  • 安全策略统一,不会每个接口散落实现。
  • 后续审计更清晰。

风险:

  • 改动较大,测试量更大。
  • 前端交互也要统一。

推荐度:长期推荐,短期可分阶段做。

方案 Grefresh token 只允许 HttpOnly Cookie,不再接受 body fallback

改动:

  • /api/auth/refresh 删除 body refresh_token fallback。
  • 如有历史客户端,先加配置开关或废弃日志。

好处:

  • Token 传输路径更少。
  • 更符合当前注释里的安全目标。

风险:

  • 如果有脚本/旧前端依赖 body refresh,会受影响。

推荐度:中,先确认没有兼容需求再做。


5. 我建议的执行顺序

如果目标是先解决“宝塔 2 小时掉线”,建议:

  1. 先做方案 C 的诊断字段或 SSH 检测脚本,确认目标面板 session timeout 是否为 7200。
  2. 再做方案 A:自动把宝塔 session timeout 提升到 86400。
  3. 回归测试:同一台宝塔一键登录后刷新不掉;多台并发生成登录 URL 不互相覆盖。
  4. 然后再做 settings/auth 安全收口:优先方案 D 或 E。

如果目标是先收安全面,建议:

  1. 方案 D 或 E 处理 Telegram Token。
  2. 方案 F 分阶段统一敏感操作二次认证。
  3. 方案 G 作为兼容性确认后的增强项。

6. 待用户选择

请在以下方向里选一个或多个:

  1. A:自动强制宝塔 session timeout >= 86400,优先解决掉线。
  2. B:只检测告警,不自动改宝塔。
  3. C:一键登录返回会话诊断字段,前端提示原因。
  4. DTelegram Token 像全局 API Key 一样完全不显示。
  5. ETelegram Token 继续可显示,但必须当前密码二次认证。
  6. F:做统一敏感操作二次认证框架。
  7. Grefresh token 只允许 HttpOnly Cookie。