dsh-cloudflare-access
已验证dsh-cloudflare-access · v2.0.0 · MIT · Web 界面
Cloudflare Access JWT verification and remote DSH privileged authorization
安装
dsh plugin add dsh-cloudflare-access 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
标签
作者
说明文档
dsh-cloudflare-access
面向 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、request path、client vs server、config。说明见 docs/diagrams.md。
快速开始
- 使用 DeepSeek Harness Web profile。用
--trusted-host钉住公网主机名。 - 把该 Origin 放到 Cloudflare Access 应用后面。
- 安装并重启:
dsh plugin --profile web add dsh-cloudflare-access
- 锁定信任根(生产环境建议如此):
export DSH_CF_ACCESS_TEAM_DOMAIN=https://example.cloudflareaccess.com
export DSH_CF_ACCESS_AUDIENCES=your-access-application-aud
- 经 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 请求必须同时通过:
- DSH 的 Host / Origin /
sec-fetch-site检查(--trusted-host仍是必须的)。 - 有效的 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。
先做 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,再重启一次。
配置
- 把 DSH Web origin 放到 Cloudflare Access 应用后面。
- 复制应用的 Audience(
aud)和 team domain,例如https://example.cloudflareaccess.com(省略https://的主机名也可以)。 - 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.1.5-alpha.1 | 已在 Cloudflare Access 后的 Web profile 上 live 验证(远程 Settings 不需要 DSH ?token=)。单元/集成测试。CI 不启动 DSH 进程。 |
在本矩阵更新之前,不要默认更新的 DSH 版本可用。
开发
pnpm install
pnpm test
pnpm typecheck
pnpm pack:check
改源码后执行 pnpm build,使提交的 lib/ 与 src/ 一致。CI 会重建 lib/,树不一致则失败。
dsh plugin --profile web add ./
插件打包遵循 dsh.pub/develop-plugin.md。本仓库不声称已有 dsh.pub listing 或安全审计。
维护者
Dependabot 每周更新 npm 和 GitHub Actions。从 GitHub Actions 发布时,使用 npm publish --access public --provenance,并授予 id-token: write。v0.1.0 和 v1.0.0 发布时没有 provenance。
License
MIT