fonlan
dsh-web-auth
DSH web plugin: password-gate authentication (login page + HttpOnly signed cookie) for exposing dsh web behind a reverse proxy
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-web-auth
为 DSH Web GUI 增加密码访问认证的插件:所有 HTTP 请求与 WebSocket 升级都必须携带有效会话,未登录一律跳转到登录页。装上它之后,把 dsh 反代到公网(或直接暴露端口)才不至于裸奔。
特性
- 登录页 + HttpOnly 签名 Cookie(HMAC-SHA256,7 天滑动续期),浏览器自动携带,HTTP / WebSocket / SSE / 静态资源全覆盖
- 首次设置:未配置密码时,登录页显示「设置访问密码」表单(引导流程,见下文安全说明)
- GUI 改密:设置 → 插件配置 →「访问认证」卡片,需旧密码;改密后轮换签名密钥,所有已登录会话立即下线
- 退出登录:设置卡片按钮,或访问
/logout - 防爆破:登录与改密接口按客户端 IP 限速(连续 5 次失败锁 1 分钟起,指数退避至 30 分钟;15 分钟内累计)
- 反代友好:Cookie
Secure自动跟随x-forwarded-proto;限速信任回环对端的X-Forwarded-For - 监听地址切换:设置卡片可一键把 dsh 的监听地址在
127.0.0.1(仅本机)与0.0.0.0(所有网卡 / 局域网可访问)之间切换,热生效、无需重启进程(写入 patch 层由 HMR 应用,webserver 自动重新绑定) - 密码以 scrypt 加盐哈希存储(
$DSH_HOME/web-auth/password.hash,0600),永不回显、不落明文配置
安装
dsh plugin --profile web add @fonlan/dsh-web-auth
从 npm 安装:dsh plugin add 会把包写入 profile 的依赖与 bundle 栈(cordis.patch.yml 自动 insert 插件行,host 半区 + 浏览器半区一次挂载)。升级到新版本:
dsh plugin --profile web update @fonlan/dsh-web-auth
安装后需要重启 dsh web 进程才生效(当前会话会短暂断连重连)。
激活(首次设置密码)
重启后打开 dsh web,会被重定向到登录页:
- 未设置过密码时,登录页显示「设置访问密码」——输入 ≥8 位的新密码并确认
- 设置成功后立即签发会话并跳回原页面;此后每次访问都要输密码
改密入口在 设置 → 插件配置 → 访问认证(需先登录)。
行为细节
| 场景 | 行为 |
|---|---|
| 未登录访问页面(GET/HEAD) | 302 → /login?next=原路径,登录后跳回 |
| 未登录访问 API(POST 等) | 401 JSON |
反代域名下已登录访问任意路径(含 /api、插件前缀) | 改写为回环来源放行,网关特权方法与插件围栏不再 403 |
| 跨站请求(即使带 Cookie) | 仍被围栏 403 拒绝 |
| 未登录 WebSocket 升级 | 握手直接 401 拒绝 |
| 登录失败 | 跳回登录页显示「密码错误」(统一文案) |
| 连续失败 | 按 IP 限速,提示「尝试过于频繁」 |
| 会话过期 | 跳回登录页;滑动续期:剩余不足 24h 自动刷新 |
| 修改密码 | 校验旧密码 → 轮换签名密钥 → 全员下线,跳转登录页 |
| 切换监听地址(设置卡片) | 校验取值(仅 127.0.0.1 / 0.0.0.0)→ 写 patch 层(profile 优先,home 兜底)→ HMR 热重载 webserver 重新绑定;WebSocket 短暂断连后自动重连 |
/logout | 清除 Cookie,跳转登录页 |
切换监听地址
dsh 默认只监听 127.0.0.1。在 设置 → 插件配置 → 访问认证 卡片底部可以把它切换为 0.0.0.0(所有网卡,局域网可访问),或切回仅本机——无需重启 dsh 进程:
- 切换写入当前部署的 patch 层:profile 的
cordis.patch.yml;若 home 层($DSH_HOME/cordis.patch.yml)已声明 webserver 行则写 home 层(home 优先级更高,改在低层会被覆盖)。文件里其余内容(注释、!!js端口表达式)原样保留。 - dsh 的 HMR 会监听到 patch 文件变化并热重载 webserver 行:关闭旧监听、按新地址重新绑定,全程无需重启进程。重启进程后该设置依然生效(它就在 patch 文件里)。
- 切换瞬间所有连接(包括当前页面与 WebSocket)会断开重连,属正常现象;卡片会轮询状态确认新地址生效。
- 注意:若你正通过局域网 IP(
0.0.0.0模式)访问并切回127.0.0.1,切回后只能在本机访问,请改用127.0.0.1重新打开页面。
该能力依赖 webserver 行位于 profile/home patch 层(默认安装即如此)。若你的 webserver 配置来自自定义
--patchoverlay,请直接编辑 overlay 文件。
安全模型与风险(请务必阅读)
- 认证是访问控制,不是安全边界。dsh 的 agent 拥有 bash 执行能力(远程代码执行级别),即使有密码保护,也不要把它当成不可攻破的堡垒:密码强度、反代层 WAF/限流、IP 白名单仍然值得做。
- 首密窗口期:未设置密码时,任何人都能访问登录页并抢注密码(设计如此,作为 bootstrap 通道)。启动日志会打印警告;请装好插件后第一时间设置密码。生产环境建议先用
127.0.0.1+ SSH 端口转发完成首密设置,再开放公网。 - 会话是无状态的(HMAC 签名),服务端无法单独吊销某一个会话;
/logout只清浏览器 Cookie。密码泄露时请修改密码——这会轮换密钥并让所有会话失效。 - 密码哈希与签名密钥存放于
$DSH_HOME/web-auth/(默认~/.dsh/web-auth/),权限 0600;删除password.hash即回到未配置状态(认证关闭)。 - 限速为进程内存级,多实例部署时各自独立,重启清零;如需更强防护请依赖反代层限流。
开发
pnpm install
npm run build # host: tsc → lib/;client: esbuild → lib/client.js(ModuleLoader 格式)
npm run typecheck
npm run test # 32 项单元 + 集成测试(node:test,真实 HTTP/WS 全流程)
仓库结构:
src/auth-core.ts— 纯逻辑:scrypt 哈希、Cookie 签名/校验、限速器、next校验src/gate.ts— 把认证闸门包到 node:http 服务器的 request/upgrade 监听器外层src/handlers.ts— 登录页/登录/登出/改密/状态/监听切换 路由处理src/profile-patch.ts— 监听地址切换:webserver 行的 patch 层文本改写(保留注释与!!js表达式)src/index.ts— cordis 插件装配(host 半区)src/client/index.tsx— 设置卡片(浏览器半区,settings.section 插槽)cordis.patch.yml— bundle 补丁层(insert 插件行)
License
MIT