dsh-web-guard:給 DSH Web UI 加一道密碼閘門,並修掉反代後的 /api 403

前言

DSH 的 Web UI 長期只有進程級 ?token=:每次重啓換一次,得從終端抄出來,HTTP 反代之後 /api 還容易直接 403。dsh-web-guard(倉庫 lvxianchao/dsh-web-guard,維護者 lvxianchao,MIT 許可證)把這幾個部署問題收進一個插件:一個訪問密碼、可吊銷會話、原生設置頁,以及 TLS。下面介紹它的定位、安裝、配置和運維注意事項。

生態背景:DSH 的理念是「一切皆插件」;本文的目錄頁來自第三方社區站點 SkillHub,與 DeepSeek 官方無從屬關係。星標 1、分類 admin-security 爲該目錄頁所載,截至撰寫時無其他獨立佐證。

它解決什麼

README 把原生行爲與插件行爲對比成一張表,四個問題如下:

問題 原生行爲 dsh-web-guard
沒有人類登錄 只有啓動時打印的 ?token=,重啓就換 一個密碼 + 可吊銷會話
反代一律 403 /api 柵欄要求 Host 原樣可達,NPM/Caddy 會改寫它 代理把 Host/Origin 改寫成迴環 authority
token 要挖日誌 得 journalctl \| grep token 才能進站 進程內兌換,瀏覽器見不到 token
明文 HTTP 報錯 crypto.randomUUID is not a function 注入 polyfill

零運行時依賴,只用 Node 標準庫;npm 包裏是預構建好的 JavaScript,dsh plugin add 不需要工具鏈。

它怎麼工作

browser ──HTTP(S)──> dsh-web-guard :3081 ──> DSH listener (loopback)
                          │
                          ├─ not signed in → /login
                          ├─ signed in → forward, Host/Origin rewritten to loopback
                          └─ injects the harness cookie (never exposed to the browser)

DSH 的 /api 柵欄接受迴環 authority,所以讓每個上游請求看起來都來自迴環,就消掉了 403。鑑權發生在代理裏,不是前端遮罩:沒有會話 cookie 的請求到不了 DSH。瀏覽器半(dsh.client)只負責把管理頁掛進 DSH 的設置面板;閘門本身完全在服務端,瀏覽器半加載失敗也不影響使用。

安裝與啓用

三種安裝方式,命令來自 README:

# npm
dsh plugin --profile web add @lvxianchao/dsh-web-guard

# from GitHub
dsh plugin --profile web add "github:lvxianchao/dsh-web-guard"

# from a local checkout
dsh plugin --profile web add /path/to/dsh-web-guard

main 分支只放已發佈代碼(開發在 dev 分支),所以這條命令裝到的是最新穩定版。包裏聲明瞭 dsh.bundle,guard 行會自動疊加,裝完重啓 dsh 即可。

首次啓動會自動生成一個密碼,打印在終端,並以哈希形式存到 <profile>/data/dsh-web-guard.json(0600)。想固定實例,改 ~/.dsh/profiles/web/cordis.patch.yml:

- id: guard
  config:
    listenHost: '0.0.0.0'
    listenPort: 3081

注意 profile patch 會整體替換 config,要保留的鍵都得重寫一遍。

從 v2 升級:右下角浮動盾牌和 /__guard/admin 已移除,管理入口改爲「設置 → DSH Web Guard」。重新安裝(或升級到 3.0.0)並重啓 DSH;密碼哈希、會話緩存和設置文件格式不變,升級不踢人。

前提:DSH 自己必須綁回環(默認即 127.0.0.1)。如果綁在局域網 IP 上,客戶端可以繞過閘門直連——插件啓動時會檢測並告警。

典型用法

讓插件終結 TLS(沒有反向代理)

config:
  listenHost: '0.0.0.0'
  listenPort: 8443
  tls:
    certFile: /path/fullchain.pem
    keyFile: /path/privkey.pem

瀏覽器訪問 https://<host>:8443,會話 cookie 自動帶 Secure。

放在反代之後

反代只做 TLS,上游填 http://<dsh-host>:3081,並開啓 WebSocket。不需要任何 Host 改寫配置——這正是插件要消掉的部分。

純局域網

明文 HTTP + 密碼,最簡單也最不安全,會話 cookie 可被嗅探。

常用配置鍵

鍵 默認 說明
listenHost 0.0.0.0 閘門監聽地址
listenPort 3081 閘門端口,不能與 DSH 端口相同
password — 明文密碼(啓動時哈希)
passwordHash — scrypt$N$r$p$salt$key,優先於 password
sessionDays 7 會話時長;0 = 關瀏覽器即失效
maxAttempts 5 連續失敗次數,超過臨時鎖定
lockMinutes 5 鎖定時長
polyfill true 注入 crypto.randomUUID 兜底
title DSH Web Guard 登錄頁標題
tls null {certFile, keyFile},啓用 HTTPS

生成 scrypt 哈希:

node -e "import('./lib/auth.js').then(m=>console.log(m.hashPassword(process.argv[1])))" 'your-password'

它是什麼,不是什麼

這是一道「入口」,不是可選插件。裝上之後,它監聽 3081,請求轉給只聽迴環的 DSH(3080)。插件一旦不加載,3081 就停止監聽,內網和外網會同時打不開——而 DSH 本體還在跑,會話和數據都在,只是夠不着。如果已經把這地址開放到公網,這一點需要格外當心。

安全模型上要說清楚邊界:一個密碼、一個權限級別,它不是多用戶系統。DSH 的 agent 本身能執行代碼,任何登錄者都擁有該機器的執行權限——這是產品設計,不是閘門的缺陷。

前面表格裏做的事——把 /api 的 403 歸因於 Host 被反代改寫,以及「代理把 Host 改寫成迴環 authority 就能放行」——都出自該項目 README 的表述。如果這個模型與你環境裏柵欄的實際行爲不一致,先查 DSH 官方文檔再改配置。

適用場景與注意

適合:想把 DSH Web UI 暴露到內網或公網、又不想繼續抄 token 的人;已經架好 NPM/Caddy/Traefik、被 /api 403 卡住的人;想用一個原生設置頁管理密碼與會話的人。

務必注意:

  1. 不要卸載、也不要禁用它。不要執行 dsh plugin --profile web remove @lvxianchao/dsh-web-guard;不要從 profile 的 package.json 裏刪掉 dsh.profile.bundles 中這一項;也不要在 cordis.patch.yml 裏給它加 disabled: true。DSH 的「設置 → 插件」頁是隻讀清單,只有狀態標籤,沒有開關,誤關基本發生在命令行。
  2. 要求 DeepSeek Harness 0.2.2-rc.1 或更新(engines.dsh: >=0.2.2-rc.1 <0.3.0),Node >=22.19,許可證 MIT。宿主版本不匹配時整個 bundle 會被跳過——這時是閘門不啓動,應該升級 DSH,而不是刪掉插件。
  3. 插件以當前 dsh 進程權限運行,裝前應檢查源碼與許可證。
  4. 會話表會落盤到 <profile>/data/dsh-web-guard-sessions-<port>.json(0600,臨時文件 + rename 原子寫入),重啓 DSH 不會把已登錄瀏覽器踢下線——但重啓不等於吊銷,要全部吊銷用設置頁的「退出所有設備」。

如果不小心關掉了,恢復命令不依賴插件本身:

# 1) 重新裝回(本地目錄裝的就用 file:)
dsh plugin --profile web add file:/path/to/dsh-web-guard
# 或:dsh plugin --profile web add github:lvxianchao/dsh-web-guard

# 2) 重啓,3081 會重新監聽
sudo systemctl restart dsh-web

如果當初是加了 disabled: true,把那行刪掉再重啓;密碼哈希在 <profile>/data/dsh-web-guard.json,不丟。

真正的坑不在這兩條命令,而在於此刻你已經連不上這臺機器。閘門只代理 HTTP 入口,不碰 SSH;但如果遠程訪問都收在這一個域名上,插件一沒,只剩一條路——一條不經過它的後路。至少準備一條:宿主控制台(PVE / ESXi / 雲廠商 VNC)進容器或虛機執行上面兩條命令,或一條獨立 SSH 通道(內網直連、跳板機、VPN)。並且在今天、現在就驗證它能用,然後把恢復命令抄一份到你能從外面打開的地方。

結尾

dsh-web-guard 的價值在於把「抄 token、改 Host、挖日誌、明文 HTTP 報錯」這四件事收斂成一個插件,代價是它成了唯一的入口——好處和風險都來自這一點。安裝前先確認 DSH 版本夠新,裝後留一條不經過閘門的後路。

目錄頁:https://www.skillhub.cn/plugins/lvxianchao/dsh-web-guard
GitHub:https://github.com/lvxianchao/dsh-web-guard
npm:https://www.npmjs.com/package/@lvxianchao/dsh-web-guard

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜