局域网访问为何少一半配置页:DSH 的安全边界设计复盘

局域网访问为何少一半配置页:DSH 的安全边界设计复盘

1. 引言

一个诡异现象困扰了我很久:同一台机器上的同一个应用,用本机地址访问和用局域网地址访问,看到的配置页数量不一样------回环地址 7 个,局域网地址只有 3 个,少的全是敏感能力(终端、Agent 循环、子智能体、网页搜索)。

先后怀疑过缓存、窗口宽度、CSS 样式,最后才发现:这不是 bug,是应用的安全边界设计。本文从源码层复盘这个机制,帮你在以后再遇到"局域网少功能"时少走弯路。

2. 概念/架构:访问地址决定功能面

访问地址 判定结果 持久化策略 插件面板「官方」组
http://localhost:3080/(本机) 回环(isLoopback=true) host 7 个:智能体团队/自动授权审查/语音输入/终端/Agent 循环/子智能体/网页搜索
http://192.168.x.x:3080/(局域网) 远程(isLoopback=false) memory 3 个 :智能体团队/自动授权审查/语音输入(终端/Agent 循环/子智能体/网页搜索 隐藏)

反复排查后确认:不是缓存、不是窗口宽度、不是样式插件------是访问地址决定的。

3. 问题/现象:真实对比

同一浏览器、同一会话、同一构建,只换 hostname:

检查项 回环地址 局域网地址
插件面板「官方」组 7 个配置页 3 个配置页
终端 / Agent 循环 / 子智能体 / 网页搜索 显示 隐藏
已安装插件组 正常 正常(不受降级影响)

4. 根因(源码级)

4.1 触发点:persistence 判定

设置客户端(dsh-client-ui-settings/lib/client.js L1509)的关键一行:

js 复制代码
const persistence = ctx.remote.$host.isLoopback ? "host" : "memory";
  • isLoopback = true (本机回环)→ persistence = host → 完整
  • isLoopback = false (局域网 IP)→ persistence = memory → 降级

4.2 回环判定:只认 localhost / 127.x

连接客户端(dsh-client-connection/lib/client.js L1333-L1337)的判定函数:

js 复制代码
function isLoopbackHostname(hostname) {
  if (hostname === "localhost" || hostname === "[::1]") return true;
  const parts = hostname.split(".");
  return parts.length === 4 && parts[0] === "127"
    && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
}

局域网 IP 不满足(首段不是 127)→ 判为远程。

⚠️ 关键:trusted-host 不影响此判定。 就算启动时加了 --trusted-host 192.168.x.x:3080,它只影响浏览器信任的 API 权限,不影响 isLoopback 的持久化降级。这是两个独立的机制------我当时就栽在这里。

从「访问地址」到「两套持久化策略」的完整判定流程如下:

text 复制代码
访问 hostname
      │
      ▼
isLoopbackHostname(hostname) 判定
      │
      ├── "localhost" 或 "[::1]" ────────────────▶ isLoopback = true
      ├── 4 段且首段为 "127"(如 127.0.0.1)─────▶ isLoopback = true
      └── 其余(如 192.168.x.x)───────────────▶ isLoopback = false
                                                          │
                                                    persistence =
                                                    isLoopback ? "host" : "memory"
                                                          │
                           ┌──────────────────────────────┴──────────────┐
                           ▼                                             ▼
                     host 持久化                                    memory 持久化
                  (完整功能面)                              (敏感配置页隐藏)

4.3 降级效果:敏感配置页按"服务是否 serve"隐藏

设置壳客户端(dsh-client-ui-settings-shell/lib/client.js L182,终端/Agent 循环/子智能体/网页搜索同款):

js 复制代码
ctx.effect(() => ctx.configForms.whileServed([BASH_NS, PWSH_NS], (served) =>
  ctx.slots.inject("plugins.item", () => ctx.slots.register({...}))))

whileServed(dsh-client-ui-settings/lib/client.js L1330-L1349)逻辑:

js 复制代码
const served = new Set(mirror.getSnapshot().view?.namespaces.map(v => v.ns) ?? [])
const watched = namespaces.some(ns => served.has(ns))
if (watched) off = register(served)   // serve 了才注册卡片
else if (!watched && off) { off(); off = undefined }  // 没 serve 就卸载

memory(LAN)模式下 bash / pwsh / subagent / web-search 的 namespace 不被 serve → 终端 / Agent 循环 / 子智能体 / 网页搜索 4 个配置页不注册 → 只显示 3 个无需服务条件的。

从「LAN 访问」到「少一半配置页」的降级链路如下:

text 复制代码
memory 模式(LAN 访问触发降级)
      │
      ▼
bash / pwsh / subagent / web-search 的 namespace 未被 serve
      │
      ▼
whileServed 判定 watched = false → 不注册卡片(已注册则卸载)
      │
      ▼
终端 / Agent 循环 / 子智能体 / 网页搜索 4 个配置页隐藏
      │
      ▼
插件面板「官方」组只剩 3 个:
智能体团队 / 自动授权审查 / 语音输入

5. 安全设计意图:为什么这么设计

隐藏的配置页 为什么 LAN 下隐藏
终端(shell) Bash/PowerShell 执行能力,远程暴露风险高
Agent 循环 控制 Agent 派发工具调用,敏感
子智能体 递归层级/模型,敏感
网页搜索 搜索提供方配置,敏感

核心设计:默认把"非本机回环访问"视为不可信远程 ,避免 shell 等能力从局域网被滥用。这是刻意的安全护栏,思路与"禁止监听 0.0.0.0"完全一致------能力越危险,暴露面越要收窄。

6. 配置/方案:实际使用指南

场景 用哪个地址 效果
在本机桌面操作 http://localhost:3080/ 完整 7 配置页 + 全部功能
从局域网访问 http://192.168.x.x:3080/ 3 非敏感配置页,功能正常
管理自制插件 两个地址都行 已安装插件组始终显示,不受降级影响

token 与 cookie:

项 行为
token 两个地址共用同一 token(进程级)
cookie 首次访问写 30 天 cookie,之后直接开根路径
失效 清浏览器缓存 / 换机器需重新兑换

7. 陷阱/根因:常见误区(我的踩坑记录)

# 误判 排除方法
1 以为是缓存问题 无痕窗口测试还是 3 个 → 排除
2 以为是窗口宽度 调试测矮窗口下回环地址 7 个全可见 → 排除
3 以为是样式插件误伤 禁用后单列但仍 3 个 → 排除
4 以为 trusted-host 能解决 不影响 isLoopback 判定 → 排除

8. 总结/改进

这次排查最大的收获:现象奇怪时,先怀疑设计而非故障。

结论 内容
1 地址决定身份------应用通过 hostname 判断"你是不是本机",本地 vs 远程两套策略
2 降级是故意的------危险能力在远程访问时隐藏,是安全护栏不是 bug
3 两个机制别混淆------trusted-host 管 API 信任,isLoopback 管功能面,互不影响

产品层面的思考:这类"按访问来源降级"的设计在本地优先应用里越来越常见(如浏览器扩展的 host 权限)。它把安全决策从"开发者自觉"变成"机制强制"------远程就是低权限,没有例外。理解它,比"修复"它更重要。

9. 参考代码位置

文件 位置 内容
dsh-client-ui-settings/lib/client.js L1509 persistence = isLoopback ? "host" : "memory"
dsh-client-connection/lib/client.js L1333-1337 isLoopbackHostname 判定
dsh-client-ui-settings-shell/lib/client.js L182 终端配置页 whileServed 注册
dsh-client-ui-settings/lib/client.js L1330-1349 whileServed 实现
dsh-web-app/lib/index.js L83-89 resolveLanTrust(trusted-host,与 isLoopback 无关)
相关推荐
衡石科技1 小时前
同一句业务问题,怎样持续问对数?衡石 ChatBI 的语义准备与回归验证
人工智能·数据挖掘·回归·chatbi
新新学长搞科研1 小时前
【会议征稿 | 杭州站】第七届机器学习与计算机应用国际学术会议 (ICMLCA 2026)
人工智能·机器学习·计算机
找方案1 小时前
从用户画像到智能定价:81页方案拆解“大数据决策与支撑平台”怎么落地
大数据·人工智能·数据分析·智慧城市·用户画像
紧跟先前的步伐2 小时前
Go AI Agent 实战 05|Temperature 与生成参数:让模型输出可控
java·人工智能·golang
一只爱撸猫的程序猿2 小时前
我让 Agent 干活,自己只做审核:一个真实项目的六次"人审拦截"
人工智能
镜子AI2 小时前
镜子AI工程实践:用 Python 做 GEO 实体一致性审计,并自动生成 JSON-LD 结构化数据
人工智能·python·json
liliangcsdn2 小时前
DeepSeek DeepInfer开源项目的功能分析和探索
大数据·人工智能
燕落南枝3 小时前
CSS 学习:类型选择器与 AI 注释实战
css·人工智能·学习
今天的砖头有点烫手啊3 小时前
GPT-6 Pro 拉起“AI 小队”、ChatGPT 长出“双手”:OpenAI 把 Agent 干活的方式重写了
人工智能·agi