dsh-cloudflare-access
Re-validates Cloudflare Access JWTs at the DSH origin so Settings, Credentials, Agent Preset management, and model discovery work from a remote hostname.
Install
dsh plugin --profile web add dsh-cloudflare-access
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✗ |
| 2 list | — |
| 3 mount | — |
| 4 boot | — |
dsh: initialized profile web at /home/node/.dsh/profiles/webFour stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-cloudflare-access
[English](./README.md)
面向 DeepSeek Harness 的 Cloudflare Access JWT 再验证与远程 privileged 授权。
这是双面 Profile Bundle + Web Client 插件。它不替代 Cloudflare Access。身份仍在 Access。插件在 DSH Origin 再验证 Cf-Access-Jwt-Assertion,使 Settings、Credentials、Agent Preset 管理和模型发现可以在 https://dsh.example.com 这类远程主机名上工作。
不提供登录页、密码库、MFA、会话表或 Cloudflare API 客户端。
插件 2.0.x 对准 DeepSeek Harness 0.1.5-alpha.1。插件 1.0.x 是 DSH 0.1.2 之前那条线(live 验证于 0.1.1-rc.2)。在[兼容性矩阵](#兼容性)更新之前,不要默认其他 DSH 版本可用。
架构
插件运行在 DSH 进程内。它不替代 Cloudflare Access,也不替代 Origin 允许名单。
Internet → Cloudflare Access → Cloudflare Proxy
→ Origin allowlist → reverse proxy → DSH → this plugin
Web Client 只做 capability enablement(connection.isLoopback)。授权裁决留在 Origin 上的 Server。
交互图(切换主题、Guided Views、导出 SVG/PNG):[deployment](./docs/assets/archify/deployment.html)、[request path](./docs/assets/archify/privileged-request.html)、[client vs server](./docs/assets/archify/client-server.html)、[config](./docs/assets/archify/config-precedence.html)。说明见 [docs/diagrams.md](./docs/diagrams.md)。
快速开始
1. 使用 DeepSeek Harness Web profile。用 --trusted-host 钉住公网主机名。
2. 把该 Origin 放到 Cloudflare Access 应用后面。
3. 安装并重启:
dsh plugin --profile web add dsh-cloudflare-access
4. 锁定信任根(生产环境建议如此):
export DSH_CF_ACCESS_TEAM_DOMAIN=https://example.cloudflareaccess.com export DSH_CF_ACCESS_AUDIENCES=your-access-application-aud
5. 经 Access 打开站点并硬刷新一次,再打开 Settings。Origin 上有效的 Cf-Access-Jwt-Assertion 会代替远程 Host 上的 DSH launch-token Cookie。loopback 仍须使用 dsh web 打印的 ?token= URL。
用 dsh --profile web --dump-config 确认 bundle:应有一层名为 dsh-cloudflare-access,以及插件行 id: cloudflare-access。
安全模型
远程 privileged 请求必须同时通过:
1. DSH 的 Host / Origin / sec-fetch-site 检查(--trusted-host 仍是必须的)。
2. 有效的 Cloudflare Access JWT(签名、iss、aud、过期时间;允许约 30 秒时钟偏差)。
有效 JWT 永远不能授权任意 Host 或 Origin。Loopback(localhost / 127.0.0.1 / ::1)不要求 JWT,因此 SSH Tunnel → localhost → DSH 仍然可用。v2.0 中远程 privileged API 始终要求 JWT。
Origin 应只对 Cloudflare(或等价入口)可达。安装本插件不是把 DSH 挂到公网的理由。详见 [SECURITY.md](./SECURITY.md)。
先做 Host/Origin。有效 JWT 不得把 Host 改写为 loopback。privileged 成功交给原 /api handler(DSH Remote);远程信任主机上有效 Access JWT 同时跳过 DSH launch-token Cookie。缺失或无效 JWT 返回 401/403,且不进入 privileged 业务实现。Loopback 不读 JWT,仍走官方 token/Cookie。
转发 Host、Origin 和 Cf-Access-Jwt-Assertion。不要剥掉 assertion 头。不要信任 CF_Authorization Cookie。
安装
需要 Web profile。包在 npm。
dsh plugin --profile web add dsh-cloudflare-access
未发布的 Git commit:
dsh plugin --profile web add github:Luawig/dsh-cloudflare-access
Git 树已包含预构建的 lib/index.js 和 lib/client.js。github: 安装使用这些产物,不需要 TypeScript 或 esbuild。
重启 DSH。不要手工编辑 $DSH_HOME/profiles/web/cordis.patch.yml,也不要改 DSH 本体。
卸载:
dsh plugin --profile web remove dsh-cloudflare-access
unload 后,本插件的 JWT 包装消失。若运行中的进程仍是旧 fiber,再重启一次。
配置
1. 把 DSH Web origin 放到 Cloudflare Access 应用后面。
2. 复制应用的 Audience(aud)和 team domain,例如 https://example.cloudflareaccess.com(省略 https:// 的主机名也可以)。
3. Cloudflare 会在已认证请求上注入 Cf-Access-Jwt-Assertion。该头是本插件读取的唯一身份。
team domain 规范化为 http(s) origin(路径丢弃)后,推导 issuer 与 JWKS URL:
issuer = <origin> JWKS = <origin>/cdn-cgi/access/certs
不要配置 issuer 或 jwksUrl。支持多个 audience。
生产环境用环境变量钉住 team domain 和 audience。变量一旦存在(即使是空字符串),该字段即锁定,无法从远程 Settings 会话改指向。
变量 · 含义
DSH_CF_ACCESS_TEAM_DOMAIN · Team domain / issuer
DSH_CF_ACCESS_AUDIENCES · 逗号分隔的 audience
DSH_CF_ACCESS_ORDINARY_MODE · off \ · optional \ · required
dsh --profile web --trusted-host dsh.example.com
systemd:
[Service] Environment=DSH_CF_ACCESS_TEAM_DOMAIN=https://example.cloudflareaccess.com Environment=DSH_CF_ACCESS_AUDIENCES=your-access-application-aud Environment=DSH_CF_ACCESS_ORDINARY_MODE=off ExecStart=/usr/bin/dsh --profile web --trusted-host dsh.example.com
Cordis overlay(仅当对应 env 未设置时使用):
cloudflare:
teamDomain: https://example.cloudflareaccess.com
audiences:
- your-access-application-aud
auth:
ordinary: off
缺少 teamDomain 或 audiences:插件仍会启动,loopback 不变,远程 privileged API 被拒绝。
普通 API 模式
auth.ordinary 只作用于远程非特权 API,包括 /api/remote.mux。Loopback 忽略它。Host/Origin 始终先执行。
模式 · 无 JWT · 有效 JWT · 无效 JWT
off(默认) · DSH 原策略 · 忽略 · 忽略
optional · DSH 原策略 · 放行 · 拒绝
required · 拒绝 · 放行 · 拒绝
无论该设置如何,远程 privileged API 始终要求有效 JWT。
排障
现象 · 检查
远程 Settings 仍不可用 · Access 必须在站点前面;硬刷新,使 Client 模块在 Settings 之前加载;确认 Cf-Access-Jwt-Assertion 到达 Origin。
Settings UI 从不调用 settings/describe · 本包设置了 dsh.client.immediately: true。若旧 tarball 漏了该项,重新安装。
settings/* 返回 401 · Access header 缺失或未被转发。查反向代理是否转发 Cf-Access-Jwt-Assertion。loopback 仍须打开 dsh web 打印的 ?token= URL。
settings/* 返回 403 · iss/aud/签名/过期无效、插件未配置、Host/Origin 不匹配,或 Origin 时钟偏差超过约 30 秒。
ordinary=required 时事件 WebSocket 失败 · /api/remote.mux 走普通 API 策略。缺 JWT → 401;无效 JWT → 403。
Loopback Settings 坏了 · 卸载插件;loopback 不得要求 JWT。若仍要求,请报 bug。
JWKS / 密钥轮换失败 · Origin 必须能访问 https://<team>/cdn-cgi/access/certs。Cloudflare 轮换密钥后无需改配置。
日志 · 只记录类别(expired、invalid_signature、issuer_mismatch、audience_mismatch、missing_token、jwks_unavailable、unconfigured)。从不记录 token。
本插件不授权 host.pickDirectory 或 host.openPath。部分 native-host UI 仍可能出现。
兼容性
插件 · DSH · 状态
1.0.x · 0.1.1-rc.2(0.1.2 之前) · Live 验证(Web profile,远程 Settings / Credentials)。走 apiProxy + privileged pin。与 DSH 0.1.2+ 不兼容。
2.0.x · 0.
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::3,最后更新 2026-09-09