DeepSeek Harness Hub
← 返回列表

登录验证码门禁jiang539/dsh-auth-gate

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

为 Web UI 加登录门,配图形验证码与防爆破保护

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/15 · 已提供中文文档

DSH Web UI 的认证门禁插件,提供 SVG 图形验证码与防暴力破解保护

综合分
27.5
GitHub 分
27.5
用户评分
★ Stars
1
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add jiang539/dsh-auth-gate
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-host-webserver@deepseek-ai/schemastery
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-auth-gate

English: README.en.md · 简体中文

面向 DeepSeek Harness(DSH)的安全认证插件。
它在 DSH Web UI 前加一道登录门,并提供 Nginx auth_request 可强制校验的 /auth API,
让局域网或公网部署获得身份验证与防暴力破解能力,且无需改动 DSH 本身。

对接真实的 DSH 插件 API(ctx.webServer.register、Cordis 槽位系统、dsh.client 打包契约)实现。

工作原理

外网用户 → Nginx (HTTPS + 限流)
→ auth_request (Nginx 层认证校验, 子请求到 /auth/verify)
→ DSH Web UI (插件登录门 + 登录页)

DSH 内部: dsh-auth-gate 插件注册 /auth/* 路由
- GET  /auth/captcha → 图形验证码 { svg, uuid }   (一次性)
- POST /auth/login   → 校验 验证码+账号+密码 → { token }
- GET  /auth/verify  → 校验 Token (供 Nginx auth_request 调用)
- POST /auth/logout  → 销毁会话
- POST /auth/username → 修改自己的账号名称 (需登录)
- POST /auth/password → 修改自己的密码 (需登录, 校验旧密码)
- GET  /auth/security  → 安全设置状态与实时数据 (需登录)
- POST /auth/security  → 修改安全设置并持久化 (需登录)

两层相互独立的强制校验:

| 层 | 机制 | 说明 |
|---|---|---|
| Nginx 层 | auth_request /_auth → 子请求 GET /auth/verify | 没有合法 Token 的请求在到达 DSH 之前就被 401 拒绝 |
| 插件层 | 客户端登录门 + 服务端会话 | 浏览器打开页面时校验 Token;未登录时整个 UI 被登录页遮挡,服务端不签发会话 |

功能特性

- SVG 图形验证码 — 一次性使用、过期自动失效、剔除 0o1i 等易混淆字符;插件级按 IP 限流(每 60 秒 60 次,存储上限 5000 条),无 Nginx 前置时同样有界
- 防暴力破解(按 IP) — 同一 IP 在 blockDuration 窗口内的连续失败(验证码错误、验证码过期/无效或密码错误均计数)达到 maxLoginAttempts 后锁定 blockDuration 秒;另有 Nginx 限流兜底
- 防暴力破解(按账号) — 任一账号(不限 IP)在 blockDuration 窗口内的凭据失败达到 accountMaxLoginAttempts 后锁定该账号,IP 轮换无法绕过;验证码错误不计入此计数(避免被用于投毒锁定他人账号)
- 会话管理 — 服务端内存存储 Token + 滑动过期(/auth/verify 每次调用顺延 sessionTimeout)
- 会话绑定 IP — 可选能力:将 Token 绑定到登录时的客户端 IP(bindSessionToIp),在其他 IP 上使用立即失效并销毁会话,被 XSS/日志窃取的 Token 无法异地使用;默认关闭(客户端 IP 不固定的部署保持关闭,否则 IP 变化会强制下线)
- 单点登录 — 每个账号同时只允许一个登录会话(singleSessionPerUser,默认开启):在别处再次登录会立即使该账号之前的所有会话失效(旧 Token 下次校验即 401,被挤下线),登录响应中的 kickedPrevious 标记可让新登录方感知这一行为
- 密码安全 — bcrypt 哈希存储(username:bcrypt_hash,权限 0600,新哈希轮数 12),绝不存明文;创建/重置密码强制至少 8 个字符
- 请求约束 — 所有 /auth 请求体必须为 application/json(否则 415),且限 64 KB(否则 413)
- 审计日志 — 登录成功/失败(含锁定、验证码错误、凭据错误)均写入日志(含 IP 与用户名,绝不记录密码)
- 双端集成 — Host 端注册 /auth/ 路由;Client 端通过 DSH 官方 Slot 机制注册登录页(root slot 优先级 -1 覆盖布局,登录成功后自动释放)
- 信任代理 — 支持从 X-Forwarded-For 获取真实客户端 IP;仅当直接对端是回环地址(同机 Nginx)时才信任该头,且取代理追加的最后一项,客户端伪造的前缀无法绕过锁定

安装

开箱即用(默认凭据):如果启动时密码文件 ~/.dsh/auth.passwd 里没有任何账号,
插件会自动创建初始账号 admin,默认密码为 admin123,并在本次 DSH 启动日志中打印一次。
默认凭据是公开值:首次登录会被强制修改账号名与密码后才能进入系统,
且 admin 这个名称之后被保留禁用(任何账号都不能改名为它,改名后旧名立即失效)。
不需要此机制时,在配置中将 autoProvisionAdmin 设为 false(见「配置」)。

1. 将插件添加到 profile(会作为 profile 的依赖安装)
dsh plugin --profile web add dsh-auth-gate

2.(可选,推荐)创建带 bcrypt 哈希的密码文件(每行一个用户)——不执行此步则使用上面的默认账号
mkdir -p ~/.dsh
npx dsh-auth-passwd set admin            # 交互式输入密码,权限 0600
或手动生成(⚠️ 明文会出现在 shell 历史与进程列表中,仅限一次性使用):
node -e "console.log(require('bcryptjs').hashSync('你的密码', 10))" > ~/.dsh/auth.passwd

3. 重启 DSH
dsh web
DSH 启动时,插件的 cordis.patch.yml 会把 dsh-auth-gate 条目写入 profile,
客户端部分由 Web 客户端注册表(dsh.client 声明)自动加载。打开 Web UI 即可看到登录门。

本地开发安装:dsh plugin --profile web add ./path/to/dsh-auth-gate。

验证是否生效

获取验证码
curl http://127.0.0.1:3080/auth/captcha

登录(将验证码答案和 uuid 替换为上一步返回的值)
curl -X POST http://127.0.0.1:3080/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"your-password","captcha":"abcd","uuid":""}'

校验 Token(Nginx auth_request 即调用此接口)
curl -H "Authorization: Bearer " http://127.0.0.1:3080/auth/verify   # → 200 + X-Auth-User

防暴力破解:连续 5 次错误 → 429,并返回 blockedUntil

配置

所有选项都在插件条目的 config 中设置(可在 profile 的 cordis.patch.yml 或
--patch 覆盖层中修改):

| Key | 默认值 | 说明 |
|---|---|---|
| passwordFile | ~/.dsh/auth.passwd | 密码文件路径(~ 展开为操作系统用户主目录) |
| sessionTimeout | 3600 | 会话有效期(秒),滑动窗口续期 |
| captchaExpires | 300 | 验证码有效期(秒) |
| maxLoginAttempts | 5 | 同一 IP 连续失败多少次后锁定 |
| accountMaxLoginAttempts | 10 | 任一账号(不限 IP)凭据连续失败多少次后锁定该账号(防 IP 轮换;验证码错误不计入) |
| blockDuration | 300 | 锁定持续时长(秒,按 IP 与按账号共用) |
| bindSessionToIp | false | 会话是否绑定登录时的客户端 IP(Token 离开该 IP 立即失效);默认关闭(本部署客户端 IP 不固定);仅当客户端 IP 固定时可设为 true |
| singleSessionPerUser | true | 单点登录:同一账号再次登录会使之前所有会话立即失效(旧 Token 下次校验即 401,即"只能在一个地方登录,其它地方登录后前面登录的掉线");需要多处同时登录可设为 false |
| trustProxy | false | 是否信任 X-Forwarded-For(仅当 Nginx 与 DSH 同机、直接对端为回环地址时生效;取代理追加的最后一项) |
| devCaptchaText | false | 仅限开发 — 在 /auth/captcha 中回显验证码答案,便于 curl 调试;生产环境切勿开启 |
| defaultAdminUser | admin | 初始账号的用户名(仅当密码文件为空且 autoProvisionAdmin 开启时自动创建)。该名称是保留名:登录时会被强制改名,且任何账号都不能改名为它 |
| defaultPassword | admin123 | 初始账号的默认密码(公开值;首次登录强制修改;至少 8 个字符) |
| autoProvisionAdmin | true | 启动时若密码文件里没有任何账号,是否自动创建初始账号并把默认密码打印到日志 |

profile 覆盖示例:

~/.dsh/profiles/web/cordis.patch.yml
- id: dsh-auth-gate
config:
sessionTimeout: 7200
maxLoginAttempts: 10
blockDuration: 600

除 passwordFile 与 devCaptchaText 外,其余选项(sessionTimeout、captchaExpires、
maxLoginAttempts、blockDuration、trustProxy、singleSessionPerUser)都可以在登录后通过
设置 → 个人配置 → 安全设置 在线修改:修改立即生效,并持久保存到
密码文件同目录的 auth-gate.security.json(权限 0600),重启后依然有效;
profile 中的值作为基准层,运行时覆盖层在其之上。为保证安全,数值有上下限
(会话超时 60–86400 秒、验证码有效期 30–3600 秒、失败阈值 1–100 次、锁定
时长 30–86400 秒),越界请求会被拒绝;devCaptchaText 刻意不提供在线开关
(仅限开发,任何环境都不应在生产开启)。

密码文件

格式:每行一个 username:bcrypt_hash(以 # 开头为注释),权限 0600。

npx dsh-auth-passwd hash            # 打印一个哈希,供手动使用
npx dsh-auth-passwd set       # 添加/更新用户(交互式输入密码)
npx dsh-auth-passwd list            # 列出用户
npx dsh-auth-passwd delete    # 删除用户

密码策略:set 与 hash 均要求密码至少 8 个字符(与 Web 端修改密码一致)。

初始账号自动创建:当 autoProvisionAdmin 开启(默认)且密码文件中没有任何账号时,
插件启动时会创建 defaultAdminUser(默认 admin)并写入 defaultPassword(默认 admin123)
的 bcrypt 哈希(权限 0600),同时在本次启动日志中打印一次默认密码;
首次登录会被强制修改账号名与密码(改名后旧名称 admin 立即失效,且任何账号都不能再改名为它)。
已有账号的密码文件永远不会被改动。

个人配置(修改用户名 / 修改密码 / 退出登录)
登录后打开左下角 设置 面板,导航栏第一项即为 个人配置(通过官方
settings.section 槽位注册,位于「通用设置」之上),提供账号自助与安全设置:
修改用户名、修改密码、安全设置、退出登录;样式使用 shell 的
--dsw- 设计变量,自动跟随明暗主题。

安全设置 卡片展示每项防护措施的启用状态与当前参数(图形验证码、防暴力破解、
会话管理、单点登录、信任代理、请求体限制),并附在线会话数与当前锁定 IP 数;
下方可在线调整会话超时、验证码有效期、连续失败锁定阈值、锁定时长、信任代理开关与单点登录开关,
保存后立即生效并持久化(见「配置」一节)。

修改用户名需要输入新名称(1-32 个字符,不含空格和冒号;不能使用保留名 defaultAdminUser,默认 admin);
修改密码需要输入旧密码和新密码;
退出登录需二次确认,会调用 /auth/logout 销毁服务端会话并清除本地 Token;不影响正在执行的任务(子代理、后台作业等继续在服务端运行,重新登录后可见)。

服务端强制规则:

- 必须已登录(Authorization: Bearer );会话失效返回 401。
- 旧密码必须与存储的 bcrypt 哈希匹配(否则 403;失败会计入与登录相同的按 IP 锁定)。
- 新密码至少 8 个字符、最多 72 字节(bcrypt 上限)。
- 修改成功后,该用户的其他登录会话全部失效,当前会话保持有效。

密码文件以原子方式重写(临时文件 + rename,权限 0600),读-验-写在同一串行队列中完成;
注释和其他用户的条目都会保留。管理员仍可直接在服务器上重置任意用户密码:

npx dsh-auth-passwd set    # 覆盖任意用户的密码

Nginx 反向代理(公网 / 局域网部署)

DSH 刻意只监听 127.0.0.1。用同一台机器上的 Nginx 做前置(auth_request + 限流)
即可安全地对外暴露 — 公网 HTTPS 见 docs/nginx.conf.example,
局域网 HTTP 见 docs/nginx.conf.lan.example。

nginx -V 2>&1 | grep -- 'http_auth_request_module'   # 检查模块是否可用
sudo nginx -t && sudo systemctl restart nginx

要点:

- location / → auth_request /_auth;子请求携带浏览器的 Authorization 头转发到 /auth/verify。
2xx 放行,401/403 拒绝。
- /auth/login 和 /auth/captcha 放行通过,但按 IP 限流。
- limit_req 区域提供粗粒度的传输层限流;插件的失败计数存储提供细粒度的账号锁定。
- HTTPS 使用 Let's Encrypt:apt install certbot python3-certbot-nginx && certbot --nginx -d your-domain.com。

局域网 HTTP(IP 直连)

局域网内不需要 HTTPS 时,直接以 IP + 80 端口访问即可。把公网配置中的
listen 443 ssl 换成 listen 80、去掉 ssl_ 指令即可,auth_request
认证流程与传输层加密无关,行为完全一致 — 完整示例见
docs/nginx.conf.lan.example:

只需把 server 块改成:
listen 80;
server_name 留空或用本机 IP,如 server_name 192.168.1.10;

⚠️ HTTP 是明文传输:账号密码和会话 Token 在局域网内可被抓包看到。
仅建议在可信内网使用;任何对公网开放的部署都应使用上方的 HTTPS 配置。

安全说明

- 默认凭据是公开的:开箱即用的 admin / admin123 会被打印到启动日志并写入文档,
任何读到这些信息的人都能在改密前登录。首次登录强制改名 + 改密只是缩短暴露窗口,
请务必在部署完成后立即完成这两步(改名后 admin 名称即被保留禁用),
或在不需要开箱即用时将 autoProvisionAdmin 设为 false,改用 dsh-auth-passwd set
创建自己的账号。注意:命令行工具 dsh-auth-passwd 属于服务器管理员工具,仍然可以直接
创建名为 admin 的账号——Web 端无法做到的事,管理员在服务器上始终可以做。
- 插件信任:第三方 DSH 插件在启动时可以改写完整配置树(本门禁正是借此自启的)。
只安装可信来源的插件、锁定版本,并审查其 cordis.patch.yml。
- 内存态存储:会话、验证码和失败计数都存在内存中。进程重启会登出所有用户。
多实例部署时,可将这些 Map 换成共享存储(如 Redis)— 处理器被隔离在小型函数后面,替换很容易。
- Nginx 才是强制校验点:插件的登录门只是隐藏了 UI,公网部署下对 /api 流量的权威校验
在 Nginx 的 auth_request 层。没有 Nginx 前置时,DSH 只会在回环地址上提供服务。
- 密码文件:保持在 ~/.dsh/auth.passwd,权限 0600;用 dsh-auth-passwd set
轮换哈希(bcrypt 轮数 = 12,新哈希;旧哈希仍按各自轮数校验;创建/重置密码强制
至少 8 个字符)。
- trustProxy 务必与部署形态匹配:仅在「同机 Nginx 反代」时开启(插件启动时会打警告)。
直接部署(无反代)时开启 trustProxy,任何能直连回环地址的进程都可伪造
X-Forwarded-For 绕过按 IP 锁定,甚至把任意 IP 投毒进锁定状态;反之,有反代却关闭
trustProxy 会让所有远端用户共享同一个(回环)锁定桶。
- Token 存储于 localStorage:浏览器内的 XSS 可读取会话 Token(滑动窗口使其在被
使用期间一直有效)。高安全场景建议:为 Web UI 配置严格的 CSP。
- 分布式暴力破解:插件内置的按账号锁定(accountMaxLoginAttempts)已覆盖
IP 池/轮换攻击;高安全场景可再叠加 Nginx 全局限流与 fail2ban 作纵深防御。

开发

npm install
npm run build          # tsc 构建 host → lib/host,esbuild 构建 client → lib/client.js
npm run typecheck
npm test              # 构建 host 后运行集成测试(覆盖验证码/锁定/改密/信任代理/并发写)

目录结构:

dsh-auth-gate/
├── package.json            # dsh.bundle.patch + dsh.client(platform: web)
├── cordis.patch.yml        # 向 profile 插入 host 条目
├── src/
│   ├── host/index.ts       # /auth/ 服务(captcha、login、verify、logout)
│   ├── client/index.tsx    # 登录门(root slot,优先级 -1)
│   ├── client/login.css    # 登录门样式
│   └── shared/types.ts     # 双端共享的线上类型
├── bin/dsh-auth-passwd.mjs # 密码文件 CLI
├── scripts/build-client.mjs# 将 esbuild 产物包装进 __ModuleLoader__.load()
└── lib/                    # 构建产物
├── host/               # host ESM(tsc)
└── client.js           # client bundle(esbuild,CJS-in-loader 包装)

客户端 bundle 由 DSH 自身的模块系统在 /plugins/dsh-auth-gate/client.js 提供,
并自动注入 window.__DSH_BOOT__ — 无需额外接线。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群