← Back to home@zzy2210

dsh-web-gate

DeepSeek Harness (dsh) Web 密钥门禁 / Access gate for dsh Web GUI — 替换出厂 webserver 行,HTTP/WebSocket/SPA 全部先过密钥:登录页 + HMAC Cookie + Bearer Token + 防爆破;host/port/key 环境变量或行内可配;0.0.0.0 无密钥拒绝启动(安全锁). Login page, HMAC cookie, bearer token, lockout; env/row config; refuses 0.0.0.0 without key.

Stars
0
Language
JavaScript
Created
Aug 14, 2026
Updated
Sep 16, 2026

Introduction

y1n-web-gate

dsh web profile 的网页密钥门禁(本地包)。把"需要密钥才能进入界面"落在真正的 HTTP 边界上: 所有 HTTP 路由(/api、插件包路由、HMR)、WebSocket upgrade (/api/remote.mux)以及 SPA fallback(页面与静态资源), 在任何业务逻辑之前先过鉴权。

安装

  1. 在 web profile 目录(~/.dsh/profiles/web)安装本包:

    pnpm add github:zzy2210/dsh-web-gate
    

    或手写 package.json dependencies: "y1n-web-gate": "github:zzy2210/dsh-web-gate" (包名以包内 package.json 的 name 为准,即 y1n-web-gate)。

  2. 把本仓库 install.patch.yml 里的两个条目合并进 ~/.dsh/profiles/web/cordis.patch.yml(禁用出厂 webserver 行 + 插入守卫行)。

  3. 按下方「配置」设置密钥,重启 dsh web。

机制

  • cordis.patch.yml 禁用出厂 webserver 行;本包继承 @deepseek-ai/dsh-host-webserver 的 WebServer,以同一个 webServer 服务键提供实例 —— 所有路由消费者(frontend-static、client-connection、 client-modules、HMR 等)透明拿到守卫实例。
  • register / registerUpgrade / registerFallback 在原型上覆盖为 "先鉴权再转发",覆盖随对象一同诞生、早于服务对外可见,不存在激活顺序竞态。
  • 首页通过 tapIndex 为 Web App Manifest 启用 use-credentials,使浏览器加载 /manifest.webmanifest 时携带门禁 cookie,避免被重定向到登录页。
  • DSH 0.1.2-rc.1 及更高版本自带浏览器 token 认证。DSH_WEB_GATE_KEY 仍是 本包的固定密码;密码登录成功后本包跳转到 DSH 当前进程生成的 /?token=..., 由 DSH 签发 authority 绑定的 dsh-auth-* cookie。这个 token 只是一次启动握手, 不是用户密码,也不需要用户预先配置或记忆。
  • 本包不改写 Host/Origin,让 DSH 自己的 trustedHosts 与浏览器认证保持有效; live reload 正在重建 connection 时登录会返回 503,浏览器稍后重试,避免 签发只有 gate cookie 而没有 DSH cookie 的半成品会话。
  • DSH 重启、升级或切换访问 authority 后,旧页面可能只剩 gate cookie;根页面会 自动跳转当前进程的 token URL,重新交换 dsh-auth-* cookie。若页面一直停留在 重启前,刷新一次即可触发这次握手;WebSocket 被拒绝时服务端日志只记录脱敏的 Host/Origin 与 cookie 是否存在,不记录 cookie 值。
  • 密钥不落盘:HMAC 会话 cookie 的签名密钥由门禁密钥本身派生,不引入第二个秘密。

配置

监听 IP、端口、密钥三者均可通过环境变量或配置文件(cordis.patch.yml 行内)配置,优先级:环境变量 > 行内配置。DSH 故意拒绝 .env 文件里的 DSH_* 变量,环境变量用 systemd Environment= 或 shell export 传入:

变量含义
DSH_WEB_GATE_KEY访问密钥(任意非空字符串,越随机越好)
DSH_WEB_GATE_HOST监听 IPv4 字面量(localhost/::1 归一为 127.0.0.1)
DSH_WEB_GATE_PORT监听端口:0-65535(0 = 系统分配)
DSH_WEB_GATE_MODEauto(默认)/ off(整体关闭门禁与安全锁,显式接受风险)
export DSH_WEB_GATE_KEY='<随机长密钥>'
dsh web --host 10.126.126.9 --port 3080 --trusted-host 10.126.126.9:3080

dsh@0.1.2-rc.1 打印的 dsh web: http://127.0.0.1:3080/?token=... 是当前进程的 启动握手 URL。它只在该进程存活期间有效;指定地址部署时以 ss -ltnp 显示的监听地址为准。

也可在 cordis.patch.yml 行内配置(不推荐把密钥写进文件):

config:
  host: !!js ctx.webStartup.host ?? '127.0.0.1'   # 监听 IP
  port: !!js ctx.webStartup.port ?? 3080          # 监听端口
  # key: 'change-me'
  # sessionTtlSeconds: 604800   # cookie 有效期,默认 7 天
  # maxFailedAttempts: 10       # 同 IP 连续失败阈值,默认 10
  # lockoutSeconds: 900         # 触发后锁定秒数,默认 15 分钟

行为

  • 配置了密钥: 一切请求都要凭证(含回环访问)。
    • 浏览器: 未登录导航 → /gate/login → 输入固定 DSH_WEB_GATE_KEY → 自动跳转 /?token=...;DSH 随后签发 dsh-auth-* cookie 并重定向到 /。gate cookie 默认 7 天,DSH 原生 cookie 默认 30 天;启用本包时两层都必须存在。登出: /gate/logout;健康检查: /gate/status。connection 尚未就绪时返回 503, 不会创建不完整会话;检测到 gate cookie 仍有效但 DSH cookie 失效时,根页面会 自动重新交换当前启动 token。
    • 脚本的 Authorization: Bearer <key> 只通过 gate,不能代替 DSH 原生 API cookie; 脚本应先用 token URL 建立 cookie jar,再携带该 jar 请求 /api。
    • 密钥持有者可使用远程设置、凭据和主机管理接口;该密钥是实例管理员凭据, 不是只读的页面访问口令。
    • /api 未鉴权一律 401 {"error":"unauthorized"},不重定向。
  • 未配置密钥: 门禁关闭,回环直通;安全锁在绑定任意非 127.0.0.0/8 地址时拒绝启动(fail loud),防止裸奔网络。绑定指定网卡地址需用 DSH_WEB_GATE_HOST=10.126.126.9 或行内 host: '10.126.126.9' —— 此时必须 配置密钥。浏览器通过该地址访问时,同时给 CLI 传入 --trusted-host 10.126.126.9:3080。
  • 防爆破: 同 IP 连续失败 maxFailedAttempts 次锁定 lockoutSeconds 秒, 两次尝试最小间隔 250ms。

启用 / 回滚

  1. cordis.patch.yml 中已包含 webserver 禁用 + y1n-web-gate 插入两个条目。
  2. 重启 dsh web 生效。
  3. 回滚: 注释/删除这两个条目并重启;或临时 DSH_WEB_GATE_MODE=off。
  4. 忘记密钥: 重启时换一个新的 DSH_WEB_GATE_KEY,全部旧 cookie 立即失效。

dsh-codex 兼容性

web profile 当前使用 dsh-codex@0.3.0,已包含旧版所需的 Cordis remote.session 注入修复,因此不再需要本仓库提供的 Codex patch。

远程页面首次访问仍需在 DSH 主机执行插件显示的精确命令,例如:

dsh plugin --profile web exec dsh-openai-codex trust-origin https://10.126.126.9:3080

修改补丁或重新安装依赖后,在 profile 目录执行 pnpm install --frozen-lockfile, 然后完全重启 dsh web 并对 https://<host>:<port> 做一次硬刷新 (Chrome/Firefox 为 Ctrl+Shift+R;仍显示旧状态时清理该站点的缓存与 Cookie 后重新登录)。 Firefox 开启“在异常上暂停”时, RemoteStreamCarrierError: api gateway: Remote stream WebSocket failed 可能只是 连接断开后自动重连路径抛出的已捕获异常;先确认 gate 日志没有持续的 WebSocket 401/503 拒绝。上游 dsh-codex 发布包含此注入修复的版本后,可删除该补丁及 锁文件中的 patchedDependencies 项,再重新安装。

已知限制(v1)

  • dsh 发布包通常不携带前端 source map;DevTools 中 *.js.map 的 404 只影响源码调试, 不影响页面或 WebSocket 运行。
  • 不支持反向代理场景的 X-Forwarded-For(防爆破按直连 socket 地址计数); 经反代部署时请在反代层加 basic auth / IP 限制。
  • cookie 未带 Secure(默认 HTTP 部署);经 TLS 反代时可在反代层处理。