Files
Nexus/docs/release/20260708/Nexus-宝塔一键登录掉线深查-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

15 KiB
Raw Blame History

Nexus 宝塔一键登录 2 小时掉线阶段诊断报告(2026-07-07)

1. 本轮结论

针对服务器 42.193.182.190Nexus server_id 约为 395)排查后,目前结论是:

  1. Nexus 生成的一键登录临时 token TTL 是 24 小时,不是 2 小时。
  2. 目标宝塔最近生成的 temp_login 记录 ttl_seconds86399/86400 秒。
  3. 宝塔面板登录超时配置文件 /www/server/panel/data/session_timeout.pl 缺失,按宝塔源码 public.get_session_timeout() 默认应为 86400 秒。
  4. 宝塔 task.py 已经是按 public.get_session_timeout() 清理 session,不再是旧的硬编码 3600 秒清理。
  5. 目标宝塔未开启面板 SSL/www/server/panel/data/ssl.pl 缺失。
  6. 未开启 SSL 时,宝塔源码 public.check_client_hash() 会启用客户端 HASH 校验;一旦客户端 HASH 变化,宝塔会清空 session 并要求重新登录。这个更符合“同时打开 10 个宝塔,过一段时间刷新陆续掉线”的现象。

2. 已验证证据

2.1 最新 temp_login TTL

实际表位置:

/www/server/panel/data/db/default.db

最新记录示例:

id=108
state=0
add_local=2026-07-07 13:56:57
expire_local=2026-07-08 13:56:56
ttl_seconds=86399
seconds_left≈86377

说明 Nexus 生成的临时登录有效期约等于 24 小时。

2.2 宝塔登录超时配置

目标机:

/www/server/panel/data/session_timeout.pl = missing

宝塔源码逻辑:文件缺失时默认:

86400 seconds = 24 hours

2.3 宝塔 task.py session 清理

目标机 /www/server/panel/task.py 已存在:

session_cleanup_timeout = 86400
try:
    session_cleanup_timeout = int(public.get_session_timeout() or 86400)
except Exception:
    pass

if f_time > session_cleanup_timeout:
    os.remove(filename)

所以当前不是旧版每 1 小时清理 session 的问题。

2.4 宝塔 SSL/client_hash 风险

目标机:

missing ssl.pl

宝塔源码中:

def is_ssl():
    return os.path.exists(get_panel_path() + '/data/ssl.pl')

def check_client_hash():
    if is_ssl(): return True
    ...
    if session[skey] != client_hash:
        WriteLog('用户登录', '客户端HASH验证失败,已强制退出登录...建议开启面板SSL或关闭IP-HASH验证!')
        return False

推断:如果浏览器/代理/出口 IP/UA/客户端特征变化,或者多个宝塔窗口处于不同访问链路,刷新时可能触发 client_hash 校验失败,被宝塔强制退出。

3. 本轮已修 Nexus 问题

发现当前 POST /api/btpanel/servers/395/login-url 曾返回 502

ModuleNotFoundError: No module named 'public'

根因:Nexus SSH fallback 用系统 Python 或未完整设置宝塔源码路径执行临时登录脚本。

已修改文件:

/opt/nexus-dev-current/server/infrastructure/btpanel/ssh_login.py

提交:

fa44439 fix btpanel ssh temp login python env

改动要点:

  1. Python 优先级调整为:
    • /www/server/panel/pyenv/bin/python3
    • /www/server/panel/pyenv/bin/python
    • python3
  2. 临时登录脚本执行前:
    • os.chdir('/www/server/panel')
    • 加入 /www/server/panel
    • 加入 /www/server/panel/class
  3. 已热更新到运行中容器并重启。
  4. 验证:
    • tests/test_btpanel_login_url.py: 6 passed
    • 一键登录接口再次返回 200 OK

注意:当前容器是单文件热更新;源码已提交。如果后续重新创建容器但不 rebuild 镜像,热更新会丢失。建议下一次部署时执行正式 build。

4. 为什么仍可能“2 小时后刷新掉线”

目前不是以下原因:

  • 不是 Nexus temp token 只有 2 小时。
  • 不是 public.get_session_timeout() 配置成 2 小时。
  • 不是 task.py 仍按 3600 秒删除 session。

更可能是:

  1. 宝塔未开启面板 SSLcheck_client_hash() 生效。
  2. 多个宝塔窗口、代理链路、出口 IP、浏览器 UA 或客户端指纹发生变化。
  3. 宝塔刷新请求时发现当前 session 内保存的 client_hash 与本次请求不同,于是清 session。
  4. 宝塔日志里这类错误通常是“客户端 HASH 验证失败,已强制退出登录”。

5. 下一步修复方案(待选择)

方案 A:开启宝塔面板 SSL(推荐优先验证)

  • 原理:宝塔源码 if is_ssl(): return True,开启 SSL 后跳过 client_hash 校验。
  • 好处:遵循宝塔自身逻辑,不改源码。
  • 风险:需要处理证书、访问协议、端口和浏览器信任问题。
  • 影响:可能需要 reload/restart 宝塔。

方案 B:Nexus 增加宝塔登录诊断提示

  • 内容:一键登录返回/审计中记录 ttl_secondspanel_sslclient_hash_risk 等非敏感信息。
  • 好处:以后看到某台机器掉线,可直接确认风险点。
  • 风险:要严格避免返回 token/cookie/API key。
  • 影响:小改动。

方案 C:只对临时登录 session 放宽 client_hash

  • 内容:patch 宝塔源码,若 session['tmp_login'] == True 则跳过或降低 client_hash 校验。
  • 好处:针对 Nexus 一键登录最直接。
  • 风险:改宝塔源码,升级可能覆盖;安全性低于开启 SSL。
  • 影响:需要 reload/restart 宝塔。

方案 D:关闭宝塔 IP-HASH/client_hash 校验

  • 好处:最直接。
  • 风险:全局安全性下降,不建议默认。
  • 影响:所有宝塔登录会话受影响。

方案 E:Nexus 自动定期生成新临时登录 URL

  • 内容:掉线时重新点一键登录;或前端提供刷新登录入口。
  • 好处:不改宝塔。
  • 风险:不能解决当前会话刷新被踢,只是降低恢复成本。
  • 影响:小到中。

方案 F:前端/代理层保活

  • 内容:定时请求宝塔保持 session 活跃。
  • 好处:对 session_timeout 问题有帮助。
  • 风险:对 client_hash 失败基本无效;跨域限制多。
  • 影响:不推荐作为主方案。

方案 GNexus 代理式宝塔访问

  • 内容:Nexus 托管/代理宝塔访问链路,减少客户端 HASH 变化。
  • 好处:长期可控。
  • 风险:大改;安全边界复杂。
  • 影响:开发量最大。

6. 当前推荐

推荐顺序:

  1. 先选 方案 A:开启宝塔面板 SSL,验证多开 10 个宝塔 2 小时后刷新是否仍掉线。
  2. 同时做 方案 BNexus 诊断提示,以后每台宝塔能直接看 TTL/SSL/client_hash 风险。
  3. 如果 A 不方便或仍失败,再考虑 方案 C:仅对临时登录放宽 client_hash

7. 待办

  • 用户确认选 A/B/C/D/E/F/G 哪个方向。
  • 若选 A:在测试机上开启面板 SSL 并记录回滚方式。
  • 若选 B:实现 Nexus 只读诊断,不返回敏感 token/cookie。
  • 若选 C/D:先备份宝塔源码,再 patch,并保留一键回滚脚本。
  • 正式重建 Nexus Docker 镜像,让 fa44439 固化进镜像。

8. 补充:get_client_hash 精确逻辑

后续只读查看 /www/server/panel/class/public.py 后确认:

def get_client_hash():
    is_tmp_login = session.get('tmp_login')
    is_session_safe_mode = session.get('session_safe_mode')
    if is_tmp_login or is_session_safe_mode:
        client_hash = md5(request.remote_addr)
    else:
        ...

因此对 Nexus 一键登录这种 tmp_login=True 的会话,宝塔主要绑定的是:

request.remote_addr

也就是宝塔看到的客户端来源 IP。

这进一步解释了“同时打开多个宝塔,约 2 小时后刷新陆续掉线”:如果浏览器代理、网络出口、反代、CDN、VPN 或宝塔前面的转发链路导致 request.remote_addr 变化,宝塔刷新时会认为客户端 HASH 不一致,然后清空 session。

所以最优先验证项变为:

  1. 开启宝塔面板 SSL,让 check_client_hash() 直接返回 True。
  2. 或保证访问宝塔时来源 IP 固定。
  3. 或只对 Nexus 临时登录会话放宽 tmp_login 的 IP 绑定。

2026-07-07 诊断字段落地与验证

本次代码改动

已在 Nexus 后端增加宝塔一键登录的非敏感诊断字段 diagnostics

  • temp_login_ttl_seconds:Nexus 申请宝塔临时登录链接的 TTL,当前为 86400 秒。
  • session_cleanup_ttl_ok:宝塔 task.py 会话清理阈值巡检/修复是否成功。
  • session_cleanup_ttl_patched:本次/最近一次 SSH bootstrap 是否修补过会话清理阈值。
  • session_cleanup_ttl_checked_at:最近一次巡检时间。
  • session_cleanup_ttl_last_error:最近一次巡检错误码,非敏感。
  • panel_ssl:宝塔面板是否启用面板 SSL(检测 /www/server/panel/data/ssl.pl),与 Nexus 的 verify_ssl 不是一个概念。
  • client_hash_risk:当 panel_ssl=false 时为 true,表示宝塔临时登录 session 仍有来源 IP 变化后被 check_client_hash() 清掉的风险。

改动文件

远端测试部署机 /opt/nexus-dev-current

  • server/application/services/btpanel_service.py
  • server/infrastructure/btpanel/bootstrap_state.py
  • server/infrastructure/btpanel/ssh_bootstrap.py
  • tests/test_btpanel_login_url.py

安全边界

  • 没有返回或记录 api_key、cookie、tmp_token、宝塔密码、SSH 密钥等敏感值。
  • diagnostics 只包含 TTL、布尔状态、时间、短错误码。
  • 审计日志里新增的也是同一份非敏感 diagnostics
  • 没有直接修改宝塔 SSL 开关或宝塔源码;只是把已知风险显式化。

测试结果

在 Nexus 测试部署机执行:

python -m py_compile server/infrastructure/btpanel/ssh_bootstrap.py \
  server/infrastructure/btpanel/bootstrap_state.py \
  server/application/services/btpanel_service.py \
  tests/test_btpanel_login_url.py \
  tests/test_btpanel_ssh_bootstrap.py

pytest tests/test_btpanel_login_url.py tests/test_btpanel_login_url_route.py tests/test_btpanel_ssh_bootstrap.py -q
# 24 passed

pytest -q
# 748 passed, 1 skipped

部署验证

已热更新 Nexus 测试容器:

docker cp server/application/services/btpanel_service.py nexus-nexus-1:/app/server/application/services/btpanel_service.py
docker cp server/infrastructure/btpanel/bootstrap_state.py nexus-nexus-1:/app/server/infrastructure/btpanel/bootstrap_state.py
docker cp server/infrastructure/btpanel/ssh_bootstrap.py nexus-nexus-1:/app/server/infrastructure/btpanel/ssh_bootstrap.py
docker restart nexus-nexus-1
curl http://127.0.0.1:18600/health
# ok

注意:当前是测试机容器热更新,正式固化仍建议 rebuild 镜像并按发布流程部署。

当前结论

代码层面已能区分:

  1. Nexus 一键登录 token TTL24h)。
  2. 宝塔 session 清理阈值是否已修复到 24h。
  3. 宝塔面板 SSL 是否开启。
  4. 未开启面板 SSL 时,tmp_login 绑定来源 IP 导致“刷新后掉线”的风险。

下一步如果要彻底解决“多开 10 个宝塔,过 2 小时刷新陆续掉线”,优先建议在用户确认方案后处理宝塔侧:启用面板 SSL,或统一浏览器访问出口/反代来源 IP;不建议默认改宝塔 check_client_hash()

宝塔官方文档补充

宝塔官方文档「面板基础设置」中说明:面板“超时时间”默认设置为 24 小时,可选 1小时、6小时、12小时、24小时、3天、7天;超过该时间未操作面板会自动退出登录。参考:https://docs.bt.cn/10.0/user-guide/config/panel-settings

这与目标机源码 public.get_session_timeout() 在缺少 session_timeout.pl 时默认 86400 秒的结果一致,因此目前“约 2 小时刷新掉线”的主因仍更像是未开启面板 SSL 时的 client_hash 来源 IP 绑定,而不是正常超时时间到了。

2026-07-07 正式环境 api.synaglobal.vip 最小热更新记录

部署方式

正式主机 /opt/nexus 当前工作树存在大量本地修改和未跟踪前端构建文件,因此没有执行 git pullgit reset 或整仓覆盖。

本次采用最小风险热更新:只把测试机已验证通过的 3 个后端 Python 文件复制进正式容器 nexus-prod-nexus-1

  • /app/server/application/services/btpanel_service.py
  • /app/server/infrastructure/btpanel/bootstrap_state.py
  • /app/server/infrastructure/btpanel/ssh_bootstrap.py

正式容器旧文件已先备份到:

/tmp/nexus-bt-diag-backup-20260707-153452

部署校验

容器内 py_compile 通过,容器已重启,健康检查通过:

container: nexus-prod-nexus-1
health: ok
status: Up About a minute (healthy)

部署后 3 个文件在正式容器内的 SHA256:

eacf143745b14727fa66de99551e88e26c2f7ece3b4b0f82a071484da8b2dbd8  btpanel_service.py
3f84246b6b0635c3a8f74848fe606b23cac407e9a775feb729d9c0f9185ffb4a  bootstrap_state.py
c4a2a1010392249dec8de68fc89def5935440df18c5c7c24a79be87763708806  ssh_bootstrap.py

正式接口验证

通过 https://api.synaglobal.vip 调用 POST /api/btpanel/servers/395/login-url 验证成功:

auth_status: HTTP/1.1 200 OK
login_url_status: HTTP/1.1 200 OK
method: api
bootstrapped: false
url_sanitized: https://42.193.182.190:31701/login?tmp_token=***

正式接口返回的非敏感诊断字段:

{
  "client_hash_risk": false,
  "panel_ssl": true,
  "session_cleanup_ttl_checked_at": "2026-07-07T07:35:19Z",
  "session_cleanup_ttl_last_error": null,
  "session_cleanup_ttl_ok": true,
  "session_cleanup_ttl_patched": false,
  "temp_login_ttl_seconds": 86400
}

说明:

  • temp_login_ttl_seconds=86400:Nexus 一键登录临时链接仍是 24 小时。
  • panel_ssl=trueclient_hash_risk=false:当前正式记录里这台宝塔面板已检测到面板 SSL 开启,因此“未开启 SSL 导致 tmp_login 绑定来源 IP”的风险在这次返回里不成立。
  • session_cleanup_ttl_ok=true:宝塔会话清理阈值检查成功。
  • session_cleanup_ttl_patched=false:表示本次检查没有新改动 task.py;不是失败。是否已经被旧补丁覆盖,要结合 session_cleanup_ttl_ok 和目标机源码再判断。

风险与后续

本次只是正式容器热更新,不是镜像固化发布。后续如果 1Panel/Docker 重新构建或重新拉镜像,这 3 个文件可能被镜像内容覆盖;建议下一步按正式发布流程把提交 b5aaf5a feat: add btpanel login diagnostics 合入正式镜像构建链路。

当前现网诊断结果显示 42.193.182.190 这台宝塔的 panel_ssl=trueclient_hash_risk=false,如果用户仍复现“多开 10 个宝塔,约 2 小时后刷新陆续掉线”,下一步应优先抓以下现场:

  1. 掉线前后重新调用 login-url,比较 diagnostics 是否变化。
  2. 在宝塔机上检查 session 文件创建/删除时间和 request.remote_addr 来源变化。
  3. 检查浏览器/代理/反代是否对不同标签页或不同时间切换出口。
  4. 检查宝塔自身是否还有其它 session 清理逻辑或安全插件清理逻辑。
  5. 检查 Nexus 返回的 method 是否一直为 api,是否在某些机器上触发 SSH bootstrap 或 fallback。