修复1Panel 终端’disconnected’

环境:Ubuntu 24.04,面板: 1Panel v2.4.2,通过同一台宿主机上的 OpenResty 容器反向代理对外提供访问。

问题现象

一次升级之后,通过 1Panel 面板点击「主机 → 终端」连接服务器时突然失败,页面始终提示:

disconnected
The connection has been disconnected.

尝试重新配置服务器的连接信息(地址 127.0.0.1、正确的端口和密码),填写这些信息时1Panel提示测试通过,但在终端连接仍然失败。其他途径可以正常 SSH 登录同一台服务器,说明服务器本身没问题。

排查过程

第一轮:怀疑防火墙

首先查看了服务器的防火墙规则。SSH 实际端口和1Panel 面板端口都已放行。又检查了服务器本地的 UFW,同样是放行状态。Fail2Ban 确实封禁了 24 个外部 IP,但那些都针对公网地址,不影响 127.0.0.1 本地连接。

防火墙排除。

第二轮:怀疑服务异常

进入服务器检查:SSH 运行正常,监听在 0.0.0.0:SSH端口;1Panel 配置中的主机信息正确(地址 127.0.0.1、端口、root 用户、密码认证);用 ssh root@127.0.0.1 -p 端口 也能成功连接。

还发现 1panel-core 日志中有 terminal revocation ... connection refused 的告警,便重启了 1panel-agent 和 1panel-core。重启后告警消失,但终端依然 disconnected。

服务正常,配置正确,问题不在这里。

第三轮:发现关键线索——403 Forbidden

回头看 OpenResty 反向代理的访问日志,出现了两条形态完全相同、结果却截然不同的请求:

# 普通 API 请求(HTTP/2.0)→ 200
GET /terminal/local?... HTTP/2.0 200

# 终端 WebSocket 握手升级(HTTP/1.1)→ 403
GET /terminal/local?... HTTP/1.1 403

同一个 URL,HTTP/2.0 的请求返回 200,HTTP/1.1 的 WebSocket 升级请求返回 403。这说明不是 WAF 拦截了特定路径,而是 WebSocket 握手阶段被拒绝了。

第四轮:用 curl 做对照实验

为定位 403 由哪个请求头触发,在服务器上做了三组对照实验:

组合Host 请求头Origin 请求头结果
Aip地址:端口1Panel域名403
B1Panel域名1Panel域名200
Cip地址:端口无200

结论瞬间明朗:当 Host 与 Origin 不一致时,1Panel 后端拒绝 WebSocket 升级并返回 403。浏览器发起 WebSocket 请求时必定携带 Origin: 域名,而后端收到的 Host 取决于反向代理怎么传。

根本原因

查看反向代理配置文件 /opt/1panel/www/sites/***/proxy/root.conf:

proxy_set_header Host $proxy_host;   # 被改写成 ip地址:端口

$proxy_host 取的是 proxy_pass 目标的主机名,即ip地址:端口。于是浏览器携带 Origin: 域名,后端收到 Host: ip地址:端口,二者不一致 → 1Panel 执行同源校验 → 拒绝升级 → 前端显示 disconnected。

这也是为什么最开始会误判成 WAF 拦截:日志里就是一条干巴巴的 403,没有任何附加信息,看起来非常像安全组件的行为。

修复方案

将代理配置中的 Host 头改为 $host(即浏览器请求中的原始域名):

# 修改前
proxy_set_header Host $proxy_host;

# 修改后
proxy_set_header Host $host;

$host 取的是浏览器请求头中的 Host 字段,即域名。改动后 Host 与 Origin 一致,1Panel 的同源校验即可通过。

然后 openresty -s reload 使配置生效。

验证

用 curl 模拟浏览器附带 Origin 头发起 WebSocket 握手:

curl --http1.1 --resolve 域名:端口:127.0.0.1 \
  -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
  -H 'Origin: https://域名' \
  https://域名/terminal/local

# 结果:200(修复前同一命令返回 403)

刷新 1Panel 面板后终端恢复正常连接。

复盘

这次排查踩了一个典型的坑:看到 403 就习惯性地归因于 WAF 或防火墙。实际上返回 403 的环节很多——WAF、Nginx 自身规则、应用层同源校验都可能产生 403。关键区别在于:

  • 如果是 WAF,通常会在 403 响应的 body 或专属日志里留下规则命中记录
  • 如果 body 为空且没有规则记录,大概率是应用层直接拒绝的

另一个值得注意的点是curl实验:严格控制变量,一次只改一个请求头,锁定根因。

以上。

挠头的老狼

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注