mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
feat(插件代理): 同源反向代理 —— VNC 不再暴露额外端口
问题:登录返回的 view_url 是 http://127.0.0.1:60100/vnc.html,WebUI 直接把它 嵌进 iframe。远程运维打开必白屏 —— 127.0.0.1 在客户端指向客户端自己,且 websockify/x11vnc 只绑回环,换服务器 IP 也连不上(实测 LAN IP 访问 000)。 ★ 教训:只验证"服务端截图有画面"就宣布 VNC 可用是不充分的。四层链路里 ①X 渲染 ②noVNC 客户端渲染 一直是好的,白屏真因在第④层「客户端如何拿到地址」。 内核新增通用「插件前缀代理」能力(不是为 channels 写的专用反向代理): - plugin.http 支持 prefix = true:声明前缀路由,承接所有子路径 - handler 返回 { proxy = "http://127.0.0.1:PORT" },内核每次请求都问一遍 ⇒ 会话停止/过期后插件不再给目标,旧地址自然失效(撤销点唯一) - 鉴权与角色门禁复用现有链路,无旁路 - statusRecorder 增加 Unwrap(),让 Hijack 穿透访问日志中间件(否则 WebSocket 升级失败) - 目标只允许带显式端口的**字面回环 IP**(用户拍板方案 A):拒绝 https、localhost 主机名、无端口、带 userinfo/path/query;理由是主机名可能被 hosts/DNS 改写 - 拒绝跨站 Origin(WebSocket 带环境 Cookie,否则第三方网页可借管理员会话开隧道) - 不向插件后端转发 Authorization/Cookie/api_key channels 接入: - 新增 channels/browser/proxy/:session(prefix=true) - 会话代号 = seq-随机8位,防猜到活跃会话 - view_url 改为网关同源路径,noVNC 的 WebSocket 走同前缀 /websockify - 会话过期即回收浏览器栈并返回 404 判据(每条咬真实外部效应): - 静态资源转发 - 真实 HTTP 101 升级 + 双向数据回传(只断言静态转发不够 —— 升级是白屏真考点) - 网关凭据不泄漏(在服务端断言请求头) - 非法目标全拒(公网/localhost/https/无端口/带 path、query、userinfo) - 跨站 Origin 403 - 未鉴权 401、过期会话 404、非子路径 404 回归:go test -tags luajit ./... 9 包全绿
This commit is contained in:
@ -100,6 +100,8 @@ type handlerRequest struct {
|
||||
|
||||
// handlerResponse is what a Lua handler returns.
|
||||
type handlerResponse struct {
|
||||
// Proxy is a loopback HTTP origin. The gateway owns streaming and upgrades.
|
||||
Proxy string
|
||||
Status int
|
||||
Headers map[string]string
|
||||
Body string
|
||||
@ -180,6 +182,9 @@ func parseHandlerResponse(v interface{}) (*handlerResponse, error) {
|
||||
return nil, fmt.Errorf("status must be a number, got %T", s)
|
||||
}
|
||||
}
|
||||
if target, ok := m["proxy"].(string); ok {
|
||||
out.Proxy = target
|
||||
}
|
||||
if b, ok := m["body"].(string); ok {
|
||||
out.Body = b
|
||||
}
|
||||
@ -196,9 +201,9 @@ func parseHandlerResponse(v interface{}) (*handlerResponse, error) {
|
||||
// return. A table carrying ONLY a status is not: `{status=204}` is the normal
|
||||
// way to answer with no body, and treating it as an error would make the
|
||||
// correct spelling unusable.
|
||||
if out.Body == "" && out.JSON == nil && out.Headers == nil && len(m) == 0 {
|
||||
if out.Body == "" && out.JSON == nil && out.Headers == nil && out.Proxy == "" && len(m) == 0 {
|
||||
return nil, fmt.Errorf("handler returned an empty table; return " +
|
||||
"{status=…, body=…}, {status=…, json=…}, or {status=…} for no body")
|
||||
"{status=…, body=…}, {status=…, json=…}, {status=…, proxy=…}, or {status=…} for no body")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user