Skip to content

dsh-auth-gateway

Verified

dsh-auth-gateway · v0.8.7 · MIT · Web UI

Password login gateway plugin for dsh web: auto-generated initial password with forced onboarding, login, password change with real request interception, and OTP two-factor authentication.

Install

dsh plugin add dsh-auth-gateway

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Readme

dsh-auth-gateway

dsh-auth-gateway banner

npm version npm total downloads npm license

Language: 简体中文 | English

为 DeepSeek Harness Web 提供认证门禁的 Cordis 插件:密码认证 + TOTP 双因素认证 + 多层防爆破 + 会话管理 + 登录审计,并在网关层真实拦截每一个请求(HTTP 与 WebSocket),未认证流量无法触及后端。

dsh web 的官方认证只面向本机回环:dsh 0.1.2 起内部 webserver 启用内置浏览器认证(BrowserAuth),但它只是一枚绑定 authority 的 bearer cookie——设计说明写明「没有登出操作」("There is no logout operation"),且「认证不配置 TLS、转发头解释或代理」("Authentication does not configure TLS, forwarding-header interpretation, or proxies")。dsh 0.2.1 虽为直连场景补上了原生 HTTPS(--tls-cert / --tls-key)与具体 IP 绑定(CLI 仅拒绝 0.0.0.0 等通配地址,接受具体地址字面量),但认证层本身依旧没有登出、没有密码 / 多因素、没有防爆破、没有登录审计,也没有为「前面再套一层认证网关」预留任何集成通道。本插件以进程内网关的形态补上这层缺失的远程访问认证:对外端口由网关独占,内部 webserver 由 bundle patch 绑定在本地回环,网关是唯一入口。

本项目支持 0.1.5-rc.2 <= dsh <= 0.2.1-rc.2,均已实测。

已知缺陷:远程访问(域名 / 反向代理 / 局域网 IP 等非回环 http 入口)下无法登录 DeepSeek 账号——点左下角「登录 DeepSeek 账号」会立即报「登录失败」,其余功能均正常(会话、模型、工具、设置面板等)。根因在 dsh 上游:账号登录要求浏览器 origin 是带显式非零端口的回环 http 地址,而回环地址只在浏览器所在机器上解析——本插件无法在服务端绕过它(远程访问正是本插件提供的形态,所以该限制在这个部署形态下必然遇到)。判别与证据见 docs/zh/TROUBLESHOOTING.md 第 10 节。

手动解决方式(详细操作见 docs/zh/DEPLOYMENT.md 的「远程访问下登录 DeepSeek 账号」一节):

  1. 本机回环访问:浏览器与 dsh 同机时,直接打开 http://127.0.0.1:<网关端口>;
  2. SSH 本地端口转发:浏览器在异地时执行 ssh -L 8080:127.0.0.1:<网关 listenPort> <部署机>(冒号前是本机端口,任选空闲端口),再浏览 http://127.0.0.1:8080;
  3. 其它回环转发方式:任何能把 http://127.0.0.1:<显式端口> 映射到远端网关的隧道 / 端口转发工具(VS Code、kubectl port-forward、gost 等);
  4. 改用 API Key 通道:在账号弹窗里点「添加 API Key」,纯域名 / 反代部署下的既定替代方案。

安装和卸载

# 安装(从 npm registry)
dsh plugin --profile web add dsh-auth-gateway

# 启动(对外端口 8080,内部 webserver 自动挪到 8081;--no-open 让 dsh 不自己开浏览器)
dsh web --port 8080 --no-open

# 卸载(先清凭据,再移除插件)
~/.dsh/profiles/web/node_modules/.bin/dsh-auth-gateway-uninstall
dsh plugin --profile web remove dsh-auth-gateway
  • 支持从 GitHub / 本地目录安装,见 docs/zh/INSTALL.md;
  • 忘记密码用 dsh-auth-gateway-reset 重置(重启后控制台打印新初始密码);
  • 部署指南:docs/zh/DEPLOYMENT.md

功能特性

  • 密码认证:首次部署自动生成初始密码(控制台打印,一次性),登录后引导设置个人密码(scrypt 哈希存储),之后每次访问需登录;
  • 双因素认证(TOTP):可选启用,兼容 Google Authenticator、Authy、1Password 等主流认证器;含一次性备份代码(scrypt 哈希存储、单次使用),设备丢失时可恢复访问;OTP 密钥以 AES-256-GCM 加密存储(主密钥来自环境变量 DSH_AUTH_GATEWAY_MASTER_KEY 或自动生成的 auth-gateway/otp-master.key),磁盘泄露不再直接暴露第二因素根密钥;
  • 真实请求拦截:未认证 /api/* 返回 401、页面类路径 302 到登录页、WebSocket 升级直接拒绝;上游自有的一次性握手路径(publicPaths,默认 /oauth/callback)是唯一的匿名上游握手例外(公开静态资产本就匿名放行)——dsh 账号登录的回调由平台以跨站顶层导航回跳,浏览器必然不带会话 Cookie,见 SECURITY.md;认证通过后请求透明转发(Host/Origin 规范化,兼容内部 trust fence);
  • 登录审计:登录成功 / 失败 / 登出 / 改密与暴力破解告警(锁定/限流)均输出审计日志(ctx.logger.info/warn,含来源 IP 与失败原因,不记录任何凭据),并持久化落盘 $DSH_HOME/auth-gateway/log/audit.log(JSONL,按天轮转、保留 90 天),形成完整可审计闭环;
  • 多层防爆破:密码失败按来源锁定(默认 5 次/5 分钟)+ 全局速率限制(默认 60 次/分钟)+ OTP/备份码独立限流(默认 10 次/分钟),scrypt 在 libuv 线程池异步执行,登录洪峰不阻塞事件循环;
  • 会话管理:内存 256-bit token(30 天),HttpOnly + SameSite=Strict Cookie,修改密码/禁用 OTP 吊销全部会话;
  • 合规形态:host-only 插件(零构建、零运行时依赖)+ 可选 client 半(设置面板,源码构建),主体全部经 dsh 官方扩展点(ctx.effect、webServer.tapIndex、ctx.slots);唯有一项记录在案的安全例外——LAN trust(为域名/反代访问下模型设置页可用而对 connection 注册做最小介入,见 TROUBLESHOOTING §1)。

本插件不做的事情

以下需求在"单实例"前提下无法真正实现——它们的前提是进程/OS 强制的执行与存储隔离(独立 OS 账号、容器或沙盒),而本插件只是运行在 dsh 进程内的认证网关,提供不了这层隔离。列出它们是为了明确预期、避免误导:

  • 多账号登录 / 多租户:dsh 是单用户工具——一个 Home、一份模型凭据,全部会话与数据(sessions/、workspace/、.credentials.yaml)都以运行 dsh 的 OS 账号权限存放在本地。网关叠加"账号体系"只能区分谁在登录(访问控制 + 审计),无法隔离谁能看到什么:任何通过认证的用户都能经 dsh 的工具执行读取同一 Home 下的全部会话与凭据。没有 OS/容器/沙盒隔离就没有真正的多租户——本插件不做,也无法做到。
  • 角色权限限制(用户/管理员):同理,角色只能在网关自身的 HTTP 路由层生效(例如限制网关管理功能),挡不住 dsh 内部的能力面——普通用户一旦通过认证门,即拥有该实例的完整能力(工具执行、会话读写、配置与凭据访问)。需要"普通用户受限"的场景请用 OS 级隔离的多实例部署并自行管理账号。本插件的职责是:认证门禁(谁能进入)+ 拦截与审计(谁做了什么),不承担、也无法承担授权与隔离模型。

工作原理

flowchart LR
    B[浏览器] --> G["dsh-auth-gateway 网关<br/>对外端口 · 运行在 dsh 进程内"]
    G --> C{"认证检查<br/>会话表 O(1)"}
    C -->|未认证| U["/api/* → 401<br/>页面 → 302 /login<br/>WS 升级 → 拒绝"]
    C -->|未通过 2FA| O["/otp/verify"]
    C -->|已认证| F["转发<br/>Host/Origin 改写为回环"]
    F --> W["dsh webserver<br/>127.0.0.1:内部端口"]
  • 网关生命周期与 dsh 绑定:随 dsh 启动/退出,无独立进程;
  • bundle patch 将 webserver 移到回环端口(对外 = --port,内部 = 对外 + 1),远程无法绕过网关直连后端;
  • 网关在 DSH 的 __ModuleLoader__ 加载 connection 模块时、Settings 等消费者启动前建立客户端 loopback trust——这是唯一记录在案的安全例外(仅拦截 connection 注册,其他插件原样通过);该兼容层不替代登录、HTTP/WebSocket 门禁或服务端 fence,详见 TROUBLESHOOTING §1;
  • 认证状态机:首次部署 → 初始密码登录 → 引导(设置个人密码)→ 登录 →(可选)OTP 验证 → 会话;未完成引导或 2FA 的会话仅能访问对应验证端点。

界面预览

引导页(设置个人密码)
引导页(初始密码登录后)
登录(含 2FA 验证码)
登录(含 2FA 验证码)
2FA 登录成功
2FA 登录成功
OTP 设置(QR 码)
OTP 设置(QR 码)
设置菜单(含认证设置入口)
设置菜单(含"认证设置"入口)
认证设置面板
认证设置面板

快速开始

  1. 启动 dsh web:首次部署自动生成初始密码并打印在控制台(醒目提示块);请复制备用;
  2. 打开 Web UI,用初始密码登录——将进入引导页:设置你自己的访问密码(至少 8 位,包含大小写字母或特殊字符;强制,设置完成前所有功能不可用);初始密码为一次性凭据,设置后自动失效;
  3. 登录后可访问 /otp/setup 启用 TOTP(扫码或手动输入密钥,输入验证码确认;同时生成备份代码请妥善保存);
  4. 已启用 OTP 后,登录需密码 + 验证码(或备份代码);
  5. 修改密码:访问 /login(已登录时显示改密表单),或经"认证设置"面板。

配置

以下字段为 bundle patch / profile patch 中 dsh-auth-gateway 行的 config(Standard Schema 校验):

字段 默认 含义
listenHost / listenPort 0.0.0.0 / 3080 网关对外监听地址与端口
upstreamHost / upstreamPort 127.0.0.1 / 3081 内部 webserver 地址与端口
basePath / 反向代理子路径前缀(如 /dsh);默认 /(根路径)。字符集限 A-Za-z0-9._~/-,拒绝 ..、//、引号、空白、尖括号(该值会内嵌进页面脚本与链接,故按白名单校验;不合规配置会在加载时被拒绝)。子路径部署时在部署方 profile patch 中配置,不随插件分发
cookieSecure auto 会话 Cookie 是否携带 Secure 属性。auto:请求经 TLS(反向代理透传 X-Forwarded-Proto: https)时自动附加;true:强制附加;false:显式关闭。纯 HTTP 下强制开启会使浏览器拒绝 Cookie。面板可直接修改:覆盖值持久化于凭据记录,优先于部署配置,直至恢复
publicPaths ['/oauth/callback'] 匿名可达的上游握手路径(精确匹配、仅 GET)。默认值就是 dsh 账号登录的回调:平台以跨站顶层导航回跳过来,浏览器不会带上网关会话 Cookie,落在门后该功能就永久不可用。只接受精确绝对路径(不接受通配符、查询串),字符集同 basePath(A-Za-z0-9._~/-);数组或逗号分隔字符串均可,重复项自动去重;保留路径(/ 与其别名 /index.html、/api*、/connect、网关自有页面与已公开静态资产)会在加载期被拒绝。设为 [] 关闭放行。条目按 basePath 剥离后的路径匹配(与上游注册路径同坐标系),子路径部署不要带 basePath 前缀。取舍与边界见 SECURITY.md;该项只影响回调那一段——非回环 origin(域名 / 反代)下账号登录在发起阶段即被上游拒绝(account: protocol),与本项取值无关,见 TROUBLESHOOTING §10
minPasswordLength 8 密码最小长度(4–128)
requireMixedCase / requireSpecial true / true 密码复杂度:大小写混合或特殊字符二选一满足
maxLoginFailures / lockMinutes 5 / 5 密码失败锁定阈值与时长
maxGlobalAuthAttemptsPerMinute 60 全局登录尝试速率上限
maxOtpAttemptsPerMinute 10 单来源 OTP/备份码验证速率上限
otpIssuer / otpPeriod / otpDigits / otpWindow dsh-auth-gateway / 30 / 6 / 1 TOTP 参数(显示名、周期、位数、窗口)
backupCodeCount / backupCodeLength 10 / 8 备份代码数量与长度
updateCheck false 打开「认证设置 → 关于」时自动检查新版本。默认关闭:全新安装不发起任何对外请求。置 true 后在面板打开时自动查询一次公共 npm registry 的 latest(本插件唯一的对外请求,成功缓存 6h / 失败 15min,超时 3s,不含任何凭据)。无论此项如何,面板上的「检查更新」按钮都可手动发起一次检查

安全模型

认证状态变更(启用/禁用 OTP、修改密码)均要求完整验证:2FA 激活时禁用 OTP 需当前密码 + 验证码或备份代码;未完成 2FA 的会话不能访问敏感端点。OTP 验证防重放(记录已接受时间步)、防伪造(x-forwarded-for 不计入来源)。OTP 密钥在落盘前以 AES-256-GCM 密封,读取需主密钥——默认自动生成 auth-gateway/otp-master.key(0600),也可经环境变量 DSH_AUTH_GATEWAY_MASTER_KEY(hex/base64,32 字节)注入以隔离磁盘泄露。登录审计只记录事件种类、来源 IP 与失败原因,不落任何凭据。完整威胁模型、已知限制与恢复路径见 docs/zh/SECURITY.md。

文档

文档 内容
docs/zh/INSTALL.md(English) 安装、更新、卸载、凭据重置的完整操作步骤
docs/zh/NGINX-DEPLOYMENT.md(English) 配合 nginx 部署:裸金属直连 / 子域名 / 子路径 / Docker nginx 容器四种拓扑与配置示例
docs/zh/SECURITY.md(English) 威胁模型、OTP 安全设计、已知限制与恢复路径
docs/zh/DEPLOYMENT.md(English) 端口与监听、LAN 部署、HTTPS 建议、nginx 反向代理、故障排查
docs/zh/TROUBLESHOOTING.md(English) 实机故障案例:域名下模型页不可用、原生依赖构建被拦、bundle 加载失败、版本线凭据格式、跨境超时优化
docs/zh/TESTING.md(English) 单元测试、端到端(Playwright)、API/WebSocket 门禁验证
docs/DEVELOPMENT.md(English) 架构说明、构建、开发统计

致谢

  • @adra2n — 实现 OTP 双因素认证(PR #1),并添加 OTP 密钥 AES-256-GCM 静态加密存储与解密路径错误分类(PR #6);
  • @meowtech — 报告并初步实现了 dsh 新版本(rc8+ 配置平面收归 loopback)下 LAN 浏览器设置不可用问题的修复(PR #7);该实现(loader 包装 + provide 劫持)随后被证实会破坏共存插件,本仓库已改用最小介入方案重写。
  • @LuckVd — 修复 dsh ≥ 0.1.2 上游浏览器认证(BrowserAuth)导致的转发 401(PR #12):经官方 credentials 通道读取密钥并为回环一跳铸造同构 cookie,经评审补齐官方通道读取、轮换即时重铸、部署清单与双语文档后合入。
  • @pjj1234 — 实现 dsh 账号登录「OAuth 回调路径匿名放行」(PR #29):新增 publicPaths 配置(默认放行 GET /oauth/callback),平台跨站回跳的回调不再被认证门挡下,修掉「账号登录停在『等待登录』、之后平台一直返回『授权失败』(ALREADY_CONSUMED)」(见 docs/zh/TROUBLESHOOTING.md 第 9 节);合入时由本仓库补全 basePath 匹配语义与「不铸上游 bearer」的测试和文档。

验证概览

  • 单元与契约测试:npm test(覆盖 basePath 路由/重定向/转发、PWA 元数据放行、登录审计、审计日志轮转/清理、OTP 安全回归、client 契约、patch 端口推导)
  • 部署流水线:npm run deploy(语法检查 → 全量测试 → 同步到 DSH 安装目录 → 安装后验证)
  • 实机端到端:node scripts/e2e.mjs(Playwright,登录/2FA/改密全流程)
  • 门禁验证:./scripts/verify.sh(curl,401/302/WS 拒绝/锁定)

详见 docs/zh/TESTING.md。

Model Experience

None,本包是浏览器与内部 dsh webserver 之间的认证载体,不会进入任何模型请求。

KV Cache effect

None;本包既不组装也不发送 provider 请求。

License

MIT