前言¶
把 DeepSeek Harness(DSH)放在 Traefik、Keycloak 等反向代理後面時,身份校驗往往已經在代理層完成;若仍只依賴 DSH 自帶的瀏覽器 launch-token / cookie 流程,遠程訪問體驗和本地 loopback、SSH 隧道訪問會不一致。直接在 DSH 裏重寫 OIDC/JWT,又會動到核心安裝與傳輸層。
下面介紹社區插件 dsh-trusted-proxy-auth(維護者 lukepoo101,分類 admin-security):在代理已認證的前提下,通過私有請求頭建立第二條獨立認證路徑;頭缺失或錯誤時,原生 DSH 認證行爲不變。
這是什麼¶
dsh-trusted-proxy-auth 是一個僅面向宿主機部署的小型 DSH 插件(MIT 許可證,當前發佈版本 v0.2.3)。它不替換 DSH 傳輸、不 patch DSH 安裝目錄內的文件、也不實現 OIDC/JWT 邏輯,只是在認證邊界上適配「代理已登錄 + 注入共享密鑰」與「DSH 原生 401/403 語義」。
典型鏈路示意:
External browser
│
▼
Traefik ───────────► Keycloak OIDC
│ (identity, login, roles, sessions)
│
│ injects: X-DSH-Proxy-Auth: <private secret>
▼
DSH ─────────────► trusted-proxy-auth
│ 401 native browser auth + valid secret ──► permit
│ 403 Host/Origin fence ──► still 403
▼
native DSH transport, unchanged
無 X-DSH-Proxy-Auth 時(例如 SSH 隧道或直接訪問),仍走原生 ?token=… / cookie 流程。
核心功能¶
雙路徑認證且不改寫 403¶
插件掛鉤 DSH Web 服務的 requestRejection()(用於 /api 與 WebSocket mux)和首頁 authorizeIndex()。要點如下:
| 場景 | 共享密鑰 | 結果 |
|---|---|---|
上游已是 403(Host/Origin/DNS-rebinding 圍欄) |
有效 | 仍爲 403 |
上游 401(需原生瀏覽器認證) |
有效 | 放行 |
| 密鑰缺失或錯誤 | — | 保持原生 401 或委託原流程 |
啓動時會用 invariant 重新驗證 DSH 的 403/401 語義,避免插件在 DSH 升級後靜默變成 bypass。資料記載:在 DSH 0.1.5-rc.2、Node v24.21.0 上測試通過(Node 20/22 由 CI 覆蓋);86 個單元測試與 37 項 live-DSH 集成檢查通過。運行時無 npm 依賴,僅使用 node:crypto,純 ESM、無構建步驟。
遠程域名下的設置持久化(ownsHost)¶
經反向代理以公網 hostname 訪問時,客戶端可能因 isLoopback 判定走內存持久化,Models、Plugins 配置出現「settings are unavailable in this browser」。對攜帶有效代理認證頭的請求,插件通過 webServer.tapIndex 等擴展點在 index HTML 的 <head> 注入經服務端校驗的 bootstrap,使 connection 包將 ownsHost 視爲 true,從而啓用 'host' 持久化,且不改動 Node ServerResponse 流水線。
第三方插件路由與 loopback 歸一化¶
部分插件(README 以 dshmarket@1.65.1 爲例)對變更類 HTTP 路由做嚴格 loopback 檢查。公網 Host 下若簡單在代理層改寫 Origin,會削弱 CSRF 防護。插件在有效 X-DSH-Proxy-Auth 時,先對真實入站頭調用未裝飾的 connection.requestRejection(req);未過 Host/Origin 圍欄則立即 403;通過後纔對 match/handle 路由做安全的 loopback 歸一化,便於 Marketplace 等插件在代理後可用。
安裝與啓用¶
1、生成並寫入共享密鑰¶
密鑰至少 32 字節可打印 ASCII(不含逗號),不要寫入 YAML,只放在 DSH 服務已讀取的環境文件中(示例路徑 /etc/dsh/env,屬主 root:dsh,模式 0640):
openssl rand -hex 48
SECRET="$(openssl rand -hex 48)"
printf 'DSH_PROXY_AUTH_SECRET=%s\n' "$SECRET" | sudo tee -a /etc/dsh/env >/dev/null
sudo chown root:dsh /etc/dsh/env
sudo chmod 0640 /etc/dsh/env
反向代理側需配置同名邏輯:僅在內網、且已完成 OIDC 等身份校驗的請求上注入請求頭 X-DSH-Proxy-Auth(值與環境變量中的 secret 一致)。該頭不應下發給瀏覽器。
2、安裝插件並重啓服務¶
在 Web profile 安裝 bundle 後重啓 DSH(認證邊界插件建議 pin 到已審查的 tag 或 commit,不要跟蹤 main):
# from a Git checkout
dsh plugin --profile web add /path/to/dsh-trusted-proxy-auth
# or directly from this repository, pinned to the released tag
dsh plugin --profile web add git+https://github.com/lukepoo101/dsh-trusted-proxy-auth.git#v0.2.3
# an exact commit is equally acceptable for an authentication-boundary plugin
dsh plugin --profile web add git+https://github.com/lukepoo101/dsh-trusted-proxy-auth.git#<40-char-commit>
sudo systemctl restart deepseek-harness
3、校驗 profile 與公網 hostname¶
確認合成 profile 中除 Connection 傳輸外還包含本插件:
dsh web --dump-config | grep -E -A1 "connection|trusted-proxy-auth"
期望片段類似:
- id: connection
name: '@deepseek-ai/dsh-client-connection'
...
- id: trusted-proxy-auth
name: dsh-trusted-proxy-auth
將最終對外 hostname 寫入 DSH 啓動參數:
dsh web --trusted-host dsh.example.com
Traefik + Keycloak OIDC 的具體路由、中間件與 header 注入需與你的基礎設施一致;README 只約定 DSH 側環境變量與頭名稱,不在此展開代理配置模板。
配置說明¶
| 項 | 位置 | 說明 |
|---|---|---|
DSH_PROXY_AUTH_SECRET |
環境變量 | 必填。缺失、爲空、過短或格式非法會導致插件激活失敗,profile 無法半開半關地啓動。 |
X-DSH-Proxy-Auth |
請求頭 | 僅由反向代理注入,勿暴露給客戶端。 |
插件本身 沒有 YAML 配置項。openssl rand -hex 48 或 openssl rand -base64 48 均可滿足字符集要求。
適用場景與注意¶
適合:已在 Traefik(或其他可信反向代理)後接入 Keycloak OIDC,希望遠程瀏覽器會話被 DSH 視爲已認證,同時保留 SSH 隧道、本機 loopback 的原生 token/cookie 回退;並需要在公網 hostname 下正常使用 Models、Plugins 持久化及 Marketplace 類插件。
務必注意:
- 插件與當前 dsh 進程同權限 運行;安裝前請自行審閱源碼與 MIT 許可證,secret 泄露等同於僞造已認證請求(仍受 Host/Origin 403 約束,但不能替代代理層 OIDC 加固)。
- 共享 secret 只應存在於服務端環境與代理配置,不要進入前端或公開倉庫。
- 認證邊界插件應固定版本(如 v0.2.3 或指定 commit),升級時 deliberately bump 並復跑驗證(見倉庫
docs/VALIDATION.md)。
DSH 生態強調「一切皆插件」;社區目錄 SkillHub 插件頁 與 GitHub 倉庫由社區維護,與 DeepSeek / 幻方無官方從屬關係。
結尾¶
dsh-trusted-proxy-auth 把「代理已完成 OIDC」與「DSH 原生瀏覽器認證」拆成兩條獨立路徑:有效 X-DSH-Proxy-Auth 時在遵守 403 圍欄的前提下放行 401;否則行爲與未裝插件一致,併兼顧遠程 settings 與 strict loopback 插件的兼容性。