wingsky-1/dsh-plugin-hub

packages/dsh-lan-proxy

局域网访问 dsh web UI:HTTP/HTTPS/WS 转发 + TLS(自签名/自定义证书);HTTP 自适应 Brotli/gzip 与 WebSocket permessage-deflate 双压缩;WS 半开探活,移动端切后台不僵死;启动令牌自动注入,LAN 设备免手工拿 token;DNS 重绑定防护 + 回环-only 上游白名单。

远程与移动端deepseek-harnessdsh-webmcp

@wingsky-1/dsh-lan-proxy

局域网访问 dsh web UI:在 0.0.0.0: 监听,把 HTTP/HTTPS 与 WebSocket/wss 转发到回环 web 服务器(默认 127.0.0.1:3080)。

简体中文 | English

一键安装

dsh plugin --profile web add @wingsky-1/dsh-lan-proxy
安装 / 卸载 / 更新后都需重启一次 dsh web(bundle 层只在启动时组合)生效。

快速导航

使用前须知 · 安全模型 · 最短上手 · 常用配置 · 验证与排障 · 详细参考 · 开发与架构

使用前须知

前提:已安装 DeepSeek Harness 且 dsh web 可正常启动(未全局安装 dsh 见下方「未全局安装 dsh」)。

  • 重写 Host/Origin 以通过 /api 浏览器信任围栏
  • 仅接受 IP 字面量或 localhost 的 Host 头(DNS 重绑定防护)
  • HTTPS 默认并存(3443),证书可配置或自动生成自签名

安全模型

  • 出站目标白名单(L1):targetHost 仅允许回环地址(localhost / 127.0.0.1 /

::1),配置层与运行时入口双重校验——防止开放转发/SSRF

  • DNS 重绑定防护:只接受 Host 为 IP 字面量或 localhost 的请求,域名一律

403/断开;IP 字面量在任何端口都安全

  • 凭据面:dsh settings RPC 仅回环可读写;通过本插件访问的远程设备在

浏览器信任围栏内可读写服务器 settings(含凭据类数据)——确保你的局域网 可信,或禁用该插件

  • 桥接头透传:WS 桥接的上游连接透传入站头(Cookie 等认证凭据——dsh

0.1.2 起 /api/remote.mux 升级需 Cookie 认证,丢弃即 401 连不上),仅覆盖 Host/Origin 为回环目标、剥离 hop-by-hop 与 WS 握手专有头;上游被 targetHost 强制约束为回环,凭据只发往本机回环上游(不承诺跨进程隔离)。压缩炸弹面:桥接浏览器段 permessage-deflate 解压存在放大点(LAN 内恶意客户端高压缩比帧 → 代理进程 解压)——在「LAN 信任」威胁模型内可接受(与 injectToken 同一信任边界)

  • 私钥权限:自动生成的自签名私钥落盘 0600;一键 CA 的 CA 私钥 + 叶子私钥 + .bak 一律 0600(write + chmodSync 双保险),certs/ 目录 0700
  • 证书下发面(issue #911):下发路由只读给公钥(loopback 围栏 + 仅 GET + 无 Cookie 要求)。只伺服首个 CERTIFICATE 块,误指私钥一律 404;响应禁缓存。无 CA 时一律 404(自签/孤叶子装了建不起信任,不下发)
  • 一键 CA 动作面(issue #930):POST 只写(loopback 围栏 + POST 白名单 + settings 可写才可用)。CA 私钥文件名固定(ca-key.pem),永不进下发源;失败响应只出固定码(ca-generate-failed 等),路径与私钥原文只进服务端日志。默认轮换仅换叶子(CA 续用,已装设备零操作);轮换 CA 为独立危险动作(已装设备信任全部失效,需逐台重装)。旧材料搬时间戳 .bak 只留最近 1 个
  • 开放端口提醒:0.0.0.0 监听对局域网所有设备可见
  • HTTP 响应压缩:压缩在转发层完成,只作用于「本插件与局域网客户端之间」

的链路,不触碰 dsh web 的响应生成;不新增可达数据面,仅增加少量 CPU 开销 (可经 httpCompressEnabled: false 关闭)。health/标记路由的 loopback 围栏 对直连回环 web 的请求生效;经本插件转发的请求按设计视为受信(见凭据面)。 注意:health 响应携带的诊断元数据——configDir 绝对路径与压缩协商计数—— 会随转发原样到达局域网设备,对其可见

injectToken 自动注入(issue #380)

dsh web 的浏览器会话认证(launch token

  • 持久签名 cookie)无法关闭,token 每次重启变化且只打印在本机终端——固定

局域网设备拿不到实时 token。本插件经官方 connection 服务的公开 API authenticatedUrl() 动态读取当前 token,仅在铸造入口(GET /、无会话 cookie) 自动补上:LAN 设备零操作进入。安全取舍与缓解:

  • 等效「信任整个局域网」:开启后任何能网络到达本端口的客户端都免 token

获得完整 dsh 控制权(bash 直通宿主机)。仅在可信家庭/办公内网开启; 不可信网段务必关闭(设置卡片开关)。

  • 默认开启(维护者决策):业界同类先例(Home Assistant trusted_networks

认证 provider、qBittorrent WebUI 「Bypass authentication for clients」)默认 需显式配置;本插件按家用固定内网场景默认开启,以「启动横幅警示行 + 设置 卡片常驻警示 + 本节说明」作缓解。

  • 关闭不吊销已发 cookie:会话 cookie 有效期内(默认 30 天)已登录设备

在关闭后仍可直接进入;需立即收回访问时清空 dsh credentials 存储。

  • 失效 cookie 自愈:cookie 失效(credentials 重置等)的设备原本会陷入

401 死锁(Max-Age 内浏览器不删 cookie、又拿不到新 token);转发层在检测到 上游 401 后自动带 token 重放一次,上游直接重铸 cookie,设备无感恢复。

  • cross-site 残余面:公网恶意页面对 http://:3081/ 发起的跨站

GET 可被白铸 cookie,但读不到响应(CORS opaque)、后续请求因 SameSite=Strict 不携带 cookie、POST /api 被 sec-fetch-site 围栏拒绝—— 链条闭合;token 与 cookie 均不出现在浏览器地址栏/历史。

  • 不注入范围:仅 GET / 且无 token 参数的请求;WebSocket、非根路径、

已带 token 的请求、provider 不可用时全部原样透传(行为与未开启一致)。 自建口令页(信任收窄为「知道口令」)为后续演进方向。

ownsHostCompat 兼容注入(issue #856,默认关)

经官方 webServer 注入钩子向非回环页面声明 ownsHost,恢复 Host 设置持久化面与 「打开配置文件」动作。它伪造的是页面侧拓扑事实,不是服务端授权变化:/api 围栏、 launch token 与会话 cookie 认证保持不变;兼容开启后,LAN 页面与回环页面在界面上不再可区分。

  • 配置入口:Plugin Manager → dsh-lan-proxy → Configure。canonical row id 为

dsh-lan-proxy,keyed row 为 @wingsky-1/dsh-lan-proxy#dsh-lan-proxy, 官方 settings namespace 使用 dsh-lan-proxy。

  • 挂载边界:配置页由 plugins.row.config 渲染,仅在 Host 服务 canonical settings 条目时

注册。compat-off 或上游契约漂移时,非回环页面可能无法进入配置行。

  • 可见性:启动横幅与 /api/dsh-lan-proxy/health 显示宿主侧开关;页面加载时若兼容未生效

或契约漂移,浏览器控制台给出独立告警,不依赖配置页。

  • 恢复旁路(仅在行详情不可达时):可直接编辑 DSH 当前设置文档中的

dsh-lan-proxy.ownsHostCompat(宿主使用 settings.yaml 时,以「打开配置文件」显示的 路径为准),或执行 ssh -L 3080:127.0.0.1:3080 后访问 http://127.0.0.1:3080/。两者都是恢复旁路,不是常规配置路径。

最短上手

安装即开端口:本插件会在 0.0.0.0:3081(HTTP)与 0.0.0.0:3443(HTTPS)监听,局域网内所有设备都可访问你的 dsh web。不需要时请卸载(见后文卸载)。

安装插件(add)

dsh plugin --profile web add @wingsky-1/dsh-lan-proxy
安装 / 卸载 / 更新后都需重启一次 dsh web(bundle 层只在启动时组合)生效。

访问

在可信局域网设备打开 https://:3443/(或 http://:3081/)。自签名证书首次访问需手动「继续访问」,详见 HTTPS 支持。

验证

在宿主机检查健康端点:

curl -s http://127.0.0.1:3081/api/dsh-lan-proxy/health

配置

键 · 默认 · 说明 enabled · true · 总开关(关闭后转发器停止监听) host · 0.0.0.0 · 监听地址 port · 3081 · HTTP 监听端口 httpsPort · 3443 · HTTPS 监听端口 targetHost · 127.0.0.1 · 回环上游主机(仅允许回环地址) targetPort · 自动 · 上游端口(默认取 web 服务器实际绑定端口) httpsEnabled · true · 是否并存 HTTPS tlsCertFile / tlsKeyFile · 无 · 自定义证书(mkcert 等) tlsCaCertFile · 无 · 自建 LAN CA 公钥(PEM,仅读) printBanner · true · 启动时是否在终端打印监听横幅(LAN 访问地址等) wsBridgeEnabled · true · 桥接总开关;关闭会失去保活与压缩。 wsCompressEnabled · true · 是否对命中 wsCompressPaths 的 WebSocket 做压缩桥接(仅控制压缩,不影响桥接保活) wsCompressPaths · /api/remote.mux · 参与 WebSocket 压缩的路径白名单(清空 = 桥接不压缩,保活不受影响) wsDeflatePolicy · {browser:true, uaDeny:[iPhone…]} · 浏览器压缩策略与 UA 排除规则。 httpCompressEnabled · true · 转发层 gzip/Brotli 压缩开关。 httpCompressLevel · 1 · gzip 与 Brotli 共用的 0..3 档位。 injectToken · true · LAN 免 token 直入,默认开;见安全模型。 ownsHostCompat · false · 伪造页面侧宿主事实位,默认关;见安全模型。

配置页入口:Plugin Manager → dsh-lan-proxy → Configure(保存即热更新)。

非回环 LAN 地址默认不提供持久化设置面,Configure 按钮按 DSH 安全策略隐藏;请从 127.0.0.1:3080 / 127.0.0.1:3081 或 SSH 回环隧道管理。只有在可信 LAN、明确接受共享控制面风险时,才开启 ownsHostCompat 恢复远程设置入口。

配置存储(单一通道)

  • 全部配置存于 dsh 官方 settings 存储;settings namespace 为 dsh-lan-proxy,

落盘位置由宿主统一管理。组合层 cordis.patch.yml 的 config 作为 base 层生效; 热更新由官方 settings/document-updated 事件驱动,无需重启。

Settings namespace

DSH 官方 importer 会按同名 section 直接导入 dsh-lan-proxy;本插件不再运行第二套 namespace migration。settings.yaml.imported 仅为已消费文档的审计副本,不应整体复制。

旧 config.json 迁移

  • 本插件不再维护自建 ~/.dsh/lan-proxy/config.json。升级后首次启动会自动把

存量 config.json 一次性迁移进官方存储:原文件原子改名保留为 config.json.migrated.bak 备份;若写入官方存储失败会自动还原改名、下次 启动重试;若进程在写入完成前退出(中断),下次启动检测到备份残留会自动 从备份重放写入并以日志提示。确认运行正常后可手动删除该备份。 此后手改 config.json 不再生效。

配置项细则

wsBridgeEnabled

WebSocket 桥接总开关(issue #552):true = 所有 WS 升级一律走「终结 + 桥接」(保活基座:ws 库自动代答上游 Ping + 半开探活);false = TCP 字节透传(显式放弃保活与压缩,移动端切后台可能被上游心跳判死而频繁断连重连,见「WebSocket 压缩」节)

wsDeflatePolicy

WS 压缩协商策略:browser 为 false 全局关压缩;uaDeny 为不协商压缩的 UA 片段列表(iOS Safari 默认拦截,见「WebSocket 压缩」节)

httpCompressEnabled

HTTP 响应压缩总开关(转发层对可压缩响应协商 gzip/Brotli;Brotli 生效条件见「HTTP 响应压缩」节,合并自 dsh-gzip)

httpCompressLevel

压缩档位预设 0..3:0 默认 / 1 低(gzip 1 / br 2,最快)· 2 中(gzip 5 / br 5,均衡)/ 3 高(gzip 9 / br 9,最高压缩比),gzip 与 Brotli 两侧参数同时按下发;旧配置整数 4..9 自动迁移为 3

injectToken