Back to home

wuwenfengmi1998

panel-auth

No description

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

Introduction

panel-auth — DSH 面板访问密码插件

dsh web 面板加一层原生 HTTP 认证(自绘登录页 + Basic + 签名 Cookie), 挂载在 /root/.dsh/profiles/web/cordis.patch.yml 的用户补丁层,升级 DSH 不会丢。

认证逻辑

  • 浏览器访问(Accept: text/html):匿名时返回内置登录页(无 JS、CSP 收紧), 表单 POST 到 /panel-auth/login,成功后签发 Cookie 并跳回原目标地址。
  • API / 脚本(fetch、curl 等):保持经典 401 + WWW-Authenticate: Basic 契约, 仍可用 Basic 凭据直接调用,成功后同样签发 Cookie。
  • WebSocket 升级:匿名一律 401,带 Cookie 放行(浏览器对 WS 不保证携带 Authorization 头,因此依赖 Cookie)。
  • /panel-auth/logout:清除 Cookie 并回到登录页。
  • Cookie 默认 30 天(cookieTtlSeconds),HttpOnly + SameSite=Lax。
  • Fail-openusers 为空或 secret 缺失/过短时不拦截任何请求, 配置写错不会把面板锁死。配置每次请求实时读取,改密码热生效。

登录日志(审计)

  • 所有登录活动写入结构化 JSONL 文件,默认 /root/.dsh/panel-auth-audit.jsonl(可用 auditLogPath 配置修改)。
  • 事件类型:
    event含义
    login-ok登录成功(含用户名、IP、User-Agent)
    login-fail登录失败(含用户名、IP、UA、reasonbad-credentials / missing-fields / cross-origin / body-too-large
    logout主动登出
    challenge匿名浏览器导航被重定向到登录页
    reject匿名 API / WebSocket 请求被拒绝(含方法、路径)
  • 每条含 ts(ISO 时间)、ip(优先 X-Forwarded-For,见下)、ua
  • 文件超过 5MB 自动轮转为 <path>.1
  • 查看示例:tail -f /root/.dsh/panel-auth-audit.jsonl | jq .

IP 说明:面板经 Caddy 反代时,直连 socket 是回环地址;插件在检测到回环 来源时会取 X-Forwarded-For 的首个值作为真实 IP(Caddy 默认会带上该头)。

防爆破(默认开启)

  • 按 IP 计数:同一 IP 在 failureWindowSeconds(默认 300 秒)窗口内连续 maxFailures(默认 5)次密码错误后进入锁定期。
  • 阶梯式锁定:首次锁 lockoutBaseSeconds(默认 30 秒),再次触发翻倍, 上限 lockoutMaxSeconds(默认 3600 秒)。
  • 锁定期内:登录页返回 429 + Retry-After("尝试次数过多"),API 返回 429 {"error":"too many attempts"},且不再执行 scrypt 校验(不消耗 CPU)。
  • 失败延迟:每次密码错误额外等待 failedLoginDelayMs(默认 300ms), 拖慢单连接暴力尝试。
  • 成功即清零;锁定状态为进程内存态,面板重启后清空(重启面板需要 root,攻击者无法自行重置)。
  • Basic 认证路径同样计入;IP 取自反代 X-Forwarded-For 的最后一跳 (Caddy 会覆写伪造值,见下)。
  • 配置(cordis.patch.ymlconfig 中可调):
    bruteProtection: true      # 关闭设为 false
    maxFailures: 5
    lockoutBaseSeconds: 30
    lockoutMaxSeconds: 3600
    failureWindowSeconds: 300
    failedLoginDelayMs: 300
    

局限:分布式攻击(每尝试换一个 IP)不受单 IP 锁定约束;这由 300ms 失败延迟 + scrypt 慢哈希兜底。公网反代场景建议再配合 Caddy 层的 IP 白名单/云防火墙(如 Cloudflare)使用。

反向代理部署(重要)

DSH 上游将 settings.*credentials.*agentPreset.*host.pickDirectory特权 /api 方法锁定为仅回环(loopback)Host 可访问(浏览器信任围栏的 设计:面板预期经 SSH 隧道访问)。用反向代理(如 Caddy)前置面板时,需把 Host 以回环形式转发给面板,否则这些方法返回 403 forbidden

dsh.example.com {
	reverse_proxy 127.0.0.1:3080 {
		header_up Host 127.0.0.1
		header_up -Origin
	}
}
  • 为什么删 Origin:围栏同时校验 Origin == Host;Host 呈回环时浏览器 携带的对外 Origin 必然不相等(全部 403)。删除后围栏改走 Sec-Fetch-Site: cross-site 检查(现代浏览器都发)拦截跨站,且所有请求 仍有 panel-auth 的密码/Cookie 层把关(跨站请求带不上 SameSite=Lax Cookie)。
  • 认证不受影响:所有请求仍先过 panel-auth(密码/Cookie + 防爆破), 且 panel-auth 的 Cookie 是浏览器端存储,与 Host 头无关。
  • panel-auth 的来源校验在 Host 为回环时自动跳过(代理场景);对外域名下的 真实跨站提交仍会被拒绝(非法请求来源)。

设置页「账号与安全」(登出 / 修改密码)

配套客户端插件 panel-auth-ui(本仓库 ui/ 目录)在面板设置里注册 "账号与安全"区块,包含:

  • 修改密码:输入当前密码 + 新密码(≥8 位、不得与旧密码相同), POST 到 /panel-auth/change-password
    1. 校验会话(Cookie/Basic)与旧密码(错误计入防爆破);
    2. 生成新 scrypt 哈希,写回 patchFilePath(cordis.patch.yml,永续);
    3. 通过 loader 热更新运行配置(live-updated,无需重启面板);
    4. 审计记录 password-change 事件(含结果明细)。
  • 退出登录:跳转 /panel-auth/logout,清除 Cookie 并回到登录页。
  • 改密不影响已登录会话(签名密钥不变,Cookie 继续有效)。

部署要求(客户端包按裸包名解析):

# 1. 把 ui/ 目录复制为 profile 内的独立包(host 存根 + client bundle)
cp -r ui /root/.dsh/profiles/web/panel-auth-ui
# 2. 建立裸包名解析符号链接(profiles 级 node_modules 为 pnpm 布局)
ln -sfn ../web/panel-auth-ui /root/.dsh/profiles/node_modules/panel-auth-ui
# 3. cordis.patch.yml 增加行(dsh.client 由 package.json 声明驱动)
#    - id: panel-auth-ui
#      name: 'panel-auth-ui'

相关配置项:

changePasswordPath: '/panel-auth/change-password'
patchFilePath: '/root/.dsh/profiles/web/cordis.patch.yml'

修改密码(命令行)

cd /root/.dsh/profiles/web/panel-auth
node hash.js admin 新密码        # 输出新 passwordHash

把输出的 passwordHash 替换进 ../cordis.patch.yml 后(loader HMR 热应用; 如未生效则 systemctl restart dsh-web)。

新增用户

cordis.patch.ymlusers 列表里再加一组 username/passwordHash

更换签名密钥

node hash.js --secret           # 生成新 secret

替换 cordis.patch.ymlsecret 后重启。注意:更换密钥会使所有已签发 Cookie 立即失效,所有人需重新登录。

应急解锁(忘记密码时)

编辑 /root/.dsh/profiles/web/cordis.patch.yml

  • 临时把 users 置为 [](fail-open,面板恢复无密码状态),或
  • 用上面的命令生成新哈希替换。

文件

  • index.js — 插件本体(登录页、认证守卫、审计日志、webServer 包装)
  • crypto.js — scrypt 哈希 / HMAC Cookie 签名(无依赖,纯 node:crypto)
  • hash.js — 生成密码哈希与随机密钥的 CLI
  • test.mjs — 单元测试(node test.mjs,覆盖 15 组流程)