DSH · 局域网访问配置 ← 返回入口

DSH Web GUI 局域网访问(源码核实版)

要厘清一个常见误区:dsh web--host 选项不支持填局域网 IP(如 192.168.31.24),连 0.0.0.0 也会被 CLI 拦截。真正能用的路径是覆盖配置文件或前置反向代理。 以下每一条都有源码出处。

dsh web --host 受限 反向代理

01结论

做法能否让局域网设备访问原因
dsh web --host 192.168.31.24 不能 webserver 配置 schema 只接受 127.0.0.1 / 0.0.0.0,填具体 IP 组合加载失败。
dsh web --host 0.0.0.0 不能 web 应用启动时对 0.0.0.0 主动 program.error,明确"暂不支持,会向网络暴露远程代码执行"。
cordis.patch.yml 覆盖 webserver 的 host0.0.0.0 可以 webserver schema 允许 0.0.0.0;web-app 会据此自动枚举局域网 IP、加入 /api 信任围栏并打印 LAN 地址。
反向代理(nginx / Caddy)在 127.0.0.1 前方 + --trusted-host 可以(官方推荐) dsh 保持绑 127.0.0.1,由代理监听局域网转发;--trusted-host/api 信任围栏放行。
一句话 别在 --host 上跟它较劲——CLI 这条路被设计成了只允许本机。局域网访问要通过配置覆盖反向代理来做。

02源码证据

2.1 webserver 插件:host 只收两个字面量

@deepseek-ai/dsh-host-webserver/lib/index.js 第 24 行:

static Config = z.object({
  host: z.union([z.const("127.0.0.1"), z.const("0.0.0.0")]).required(),
  port: z.natural().max(65535).required()
});

host 只能是 "127.0.0.1""0.0.0.0",其它字面量(含局域网 IP)直接校验失败。

2.2 web 应用启动:主动拦截 0.0.0.0

@deepseek-ai/dsh-web-app/lib/startup.js 第 39 行:

if (options.host === "0.0.0.0") program.error(
  "error: --host 0.0.0.0 is intentionally not supported yet for safety: "
  + "it would expose remote code execution to the network; use 127.0.0.1 instead");

2.3 web-app 运行时:预留 0.0.0.0 的 LAN 自适应

@deepseek-ai/dsh-web-app/lib/index.jsresolveLanTrust

function resolveLanTrust(bindHost, extra) {
  const lanAddresses = bindHost === "0.0.0.0"
    ? Object.values(networkInterfaces()).flat()
        .filter(i => i !== void 0 && i.family === "IPv4" && !i.internal)
        .map(i => i.address)
    : [];
  return { lanAddresses, trustedHosts: [...lanAddresses, ...extra] };
}

当 host 是 0.0.0.0 时自动枚举所有非内部 IPv4、作为 /api 信任围栏的可信来源,并在启动时打印 dsh web: http://127.0.0.1:3080 (LAN: http://192.168.31.24:3080)

2.4 信任围栏

--trusted-host 的用途是"额外可信来源":dsh-client-connection 里非回环来源要命中 trustedHosts 才会被 /api 接受(主要用于反向代理场景)。官方 README 明确:v1 不提供 TLS、认证或来源策略,面向部署的加固"有意放在前方的真正反向代理"。

03为什么默认不行

默认 host127.0.0.1(仅回环)。回环地址只有本机自己可达,网卡上的局域网 IP 收不到连接, 所以其他设备访问不到。加上上面 2.1 / 2.2 的两道拦截,--host 这一层实际被限制为"只能本机"。

04做法 A:覆盖 webserver 配置为 0.0.0.0

这是代码里支持的路径。编辑 web profile 的用户补丁层(本机是 C:\Users\whxme\.dsh\profiles\web\cordis.patch.yml,当前内容为 []),覆盖 webserver 行:

- id: webserver
  config:
    host: '0.0.0.0'
    port: 3080
⚠️ 为什么 host 和 port 都要写 补丁层会整段替换目标行的 config(注释原文:"A patch replaces the targeted row's whole config")。 webserver 行原本读 ctx.webStartup.host ?? '127.0.0.1'?? 3080;你只写 host 会把 port 一起丢掉。

改完重启 dsh web,它会自动发现 192.168.31.24 并打印 (LAN: http://192.168.31.24:3080)。别忘了放行防火墙 3080 端口(见第 06 节)。

05做法 B:反向代理(官方推荐)

保持 dsh 绑 127.0.0.1:3080 不动,在前方放一个监听局域网的反向代理,把请求转发到 127.0.0.1:3080。 这是 README 里"有意留给部署"的加固方式(能顺便加 TLS / 认证)。

例如 nginx(监听 3080,代理到本机 3080):

server {
  listen 3080;
  location / {
    proxy_pass http://127.0.0.1:3080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

同时用 --trusted-host 把代理来源加进 /api 信任围栏(可用 host 或 host:port,可重复):

dsh web --trusted-host 192.168.31.24:3080
为什么需要 --trusted-host /api 的 browser-trust fence 默认只接受回环来源;从局域网进来的是非回环 Host,必须命中 trustedHosts 才会放行。这也正是该参数存在的场景。

06防火墙端口放行(Windows)

管理员身份打开 PowerShell,添加入站规则放行 3080:

netsh advfirewall firewall add rule name="DSH Web 3080" dir=in action=allow protocol=TCP localport=3080

查看是否已存在:

netsh advfirewall firewall show rule name="DSH Web 3080"

撤销:

netsh advfirewall firewall delete rule name="DSH Web 3080"

07验证

  1. 本机netstat -ano | findstr :3080,看到 0.0.0.0:3080 ... LISTENING(做法 A)说明已对全网开放。
  2. 同机用局域网 IP:浏览器打开 http://192.168.31.24:3080,能开说明绑定已生效。
  3. 其他设备:手机 / 另一台电脑(同一 WiFi)打开 http://192.168.31.24:3080
如果第 2 步能开、第 3 步打不开 基本是防火墙拦了入站连接,见第 06 节放行 3080。

08安全注意

⚠️ 向网络开放 = 暴露能力 DSH 的 Agent 能执行命令、读写文件、联网。绑到 0.0.0.0(或反向代理对外)意味着同一网段任何设备都能连上并可能控制这台电脑的 Agent。 官方之所以拦截 --host 0.0.0.0,就是因为这会"向网络暴露远程代码执行"。
  • 只在可信的局域网内开放,避免在公共 / 不受控 WiFi 里开启。
  • 优先用反向代理 + 认证/TLS(做法 B)而不是裸绑 0.0.0.0
  • 用完恢复 127.0.0.1,并删除防火墙放行规则。

09参考来源