DSH Web GUI 局域网访问(源码核实版)
要厘清一个常见误区:dsh web 的 --host 选项不支持填局域网 IP(如
192.168.31.24),连 0.0.0.0 也会被 CLI 拦截。真正能用的路径是覆盖配置文件或前置反向代理。
以下每一条都有源码出处。
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 的 host 为 0.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.js 的 resolveLanTrust:
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为什么默认不行
默认 host 是 127.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
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
/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验证
- 本机:
netstat -ano | findstr :3080,看到0.0.0.0:3080 ... LISTENING(做法 A)说明已对全网开放。 - 同机用局域网 IP:浏览器打开
http://192.168.31.24:3080,能开说明绑定已生效。 - 其他设备:手机 / 另一台电脑(同一 WiFi)打开
http://192.168.31.24:3080。
08安全注意
0.0.0.0(或反向代理对外)意味着同一网段任何设备都能连上并可能控制这台电脑的 Agent。
官方之所以拦截 --host 0.0.0.0,就是因为这会"向网络暴露远程代码执行"。
- 只在可信的局域网内开放,避免在公共 / 不受控 WiFi 里开启。
- 优先用反向代理 + 认证/TLS(做法 B)而不是裸绑
0.0.0.0。 - 用完恢复
127.0.0.1,并删除防火墙放行规则。