ZMUKE vault 手册

Cloudflare 边缘入口: 能力分工、Token 和保管

最后更新: 2026-08-17

本文面向操作人员, 说明各组织的外部安全入口由 Cloudflare 的哪三个能力构成、各自负责什么、需要在 Cloudflare 后台创建哪些凭据、如何限制权限, 以及如何存入本机 Vault. 不要把任何 Token、Client Secret 或包含它们的命令粘贴到 Issue、提交、日志或聊天中.

官方参考:

1. 三个能力的分工

这三者经常被混为一谈, 但管的是互不重叠的事. 想清楚这一点, 权限清单和上线顺序就都是推论:

能力 负责 不负责
Tunnel 连通. 把内网服务接到 Cloudflare, 主机不开任何入站端口 不问请求背后是谁
Access 准入. 在边缘判断这个请求背后是不是我们认可的人或机器 不看请求内容是否是攻击
WAF 攻击面. 注入、恶意 bot、异常速率 不知道请求背后是谁

推论一: 只开 Tunnel 不开 Access, 等于把 origin 摆到公网上, 安全性完全落在应用自己的登录页上. 对本来就要公开的服务这是正常形态; 对内部服务不是.

推论二: Access 通过后 Cloudflare 会签发 JWT (CF_Authorization cookie). 单独部署时 origin 必须校验这个 JWT, 否则绕过 Cloudflare 直连源站就跳过了整层准入. 本项目的服务一律经 Tunnel 出去、源站不开入站端口, 这条绕过路径天然被堵死 —— 这正是 Tunnel 与 Access 成对出现的原因, 不是两个可以随意取舍的独立选项.

推论三: WAF 挡不住"凭据正确但不该进来的人", Access 挡不住"有权限的人发来的注入". 两者不能互相替代.

2. 归属: ZMUKE 是上级组织, 各业务是其下级组织

组织模型 (2026-08-15 确认): ZMUKE 是上级组织, 大脑主机即本机 (fleet 的控制面主机,

Workspace 划分里管理 Hub A/B、控制面主机、op-hubbusiness-hub 均属 zmuke).

slaunchxmocalmax 是 ZMUKE 的下级组织:

不在大脑主机上); mocalmax -> gitea.mocalmax.cc (旧 gitea.8milex.com 过渡);

zmuke -> 本机 zmuke-gitea 容器 + gitea.zmuke.com.

zone 是 mocalmax.cc. 8milex.com 是企业邮箱域, 不发 token.

本文的凭据模板服务于每个组织; 落到 ZMUKE 自身时**这套 Cloudflare 凭据属于 zmuke

业务域**, 用 vault --business zmuke 保管, 与下级组织的凭据分开.

2.1 Cloudflare 侧: 同一账号, 每组织一张管理 token

Tunnel / Access 无法按 zone 切开. 再拆 Account token 与 Zone token 换不来隔离.

每组织一张够用的管理 token, Zone Resources 只含该组织 zone.

本账号服务 zmuke、slaunchx、mocalmax. mocalmax 在管 zone 是 mocalmax.cc.

8milex.com 不管理. 对照见 CLOUDFLARE_ZONES.md.

3. 凭据集合

凭据 何时需要 Vault 域 Vault profile Vault key
组织管理 Token 该组织 Tunnel / Access / DNS / WAF 该组织 cloudflare-<组织>-workspace-admin CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID
CI / Pages Token 只部署 Workers / Pages 该组织 cloudflare-<组织>-workers-deploy CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID
Named Tunnel runtime token cloudflared connector 该组织 cloudflare-<组织>-<服务>-tunnel TUNNEL_TOKEN
Access Service Token 机器调用受 Access 保护的接口 该组织 cloudflare-<组织>-access-<caller> CF_ACCESS_CLIENT_ID, CF_ACCESS_CLIENT_SECRET

管理 token 与 CI token 分开, 是为了不让 Gitea Actions 拥有改 DNS / WAF / Tunnel

的能力. mocalmax-workers-deploy 已在 Gitea secret 中, 不要撤销.

存放 (D13, 2026-08-17): 3 张组织管理 token 双机同放 —— 大脑机与 debian-vm

各自 vault 的对应组织业务域, 同名 profile. 任一机器均可直接执行本组织边缘

操作. 此为 D11 双层边界的唯一例外, 决策记录见 zmuke-fleet-prototype

docs/DECISIONS.md D13; 其余管理凭据仍只在大脑机.

Zone ID 不进 vault. 用时 GET /zones?name=<根域名>.

管理 token 不得注入 Fleet Agent 或 cloudflared. 不创建 Global API Key.

凭据不进环境变量, 不进 shell profile. 历史上曾有工具把 Cloudflare 凭据写入 ~/.config/dev-env.sh 并由 ~/.profile source, 使该机器上每个进程和 Agent 都继承了它. 这与 Vault 模型直接冲突, 新 Token 不得重蹈; 发现此类残留应在轮换时一并清除.

4. 创建组织管理 Token

Cloudflare Dashboard -> Manage Account -> API Tokens -> Create Token -> Create Custom Token

名称: <组织>-edge-infra (zmuke / slaunchx / mocalmax 各一张).

Scope Permission group Level
Account Cloudflare Tunnel Edit
Account Access: Apps and Policies Edit
Zone Zone Read
Zone DNS Edit
Zone Zone WAF Edit
Zone Analytics Read
Account Cloudflare Pages Edit
Account Workers R2 Storage Edit
Account Access: Service Tokens Edit
Account Account Filter Lists Edit
Account Resources -> Include -> 只选这一个账号
Zone Resources    -> Include -> Specific zone -> 只选该组织的 Zone

禁止 All zones. 不要加 API Tokens、Account Settings、

Zone Edit / Zone Settings、Billing. 设 TTL.

Pages / R2 与 Tunnel / Access 同为账号级资源, 无法按组织切 token; 组织归属靠

命名纪律 (项目/桶名带组织前缀). 管理 token 含 Pages/R2 Edit 供人和 Agent 做

项目创建、域名绑定、桶管理; CI 自动部署仍必须用下节的低权 deploy token,

不得把管理 token 放进 Gitea Actions secret.

DNS 代价: 能改该组织 Zone 的任意解析, 含邮件记录. MX/SPF/DKIM/DMARC 仍由人维护.

5. 创建 CI / Pages Token (按需, 已有的不要动)

名称 <组织>-workers-deploy. 只勾实际用到的 Account: Workers Scripts /

Pages / R2 / KV / D1. 不含 Tunnel、Access、WAF、DNS.

mocalmax-workers-deploy 已在 Gitea Actions secret 中, 不要重建或撤销.

6. 创建 Named Tunnel 和 runtime token

6.1 先创建 Tunnel

Cloudflare Dashboard -> Networking -> Tunnels -> Create a tunnel

选择 Cloudflared. 命名规则 <workspace>-<位置>-<服务>, 例如现网的 mocalmax-local-gitea.

创建 Tunnel 后, 先建 Access Application 和默认拒绝策略, 最后才发布 public hostname 和 DNS. 顺序颠倒会留下一个 hostname 已解析、准入尚未生效的裸露窗口.

6.2 提取 Token, 不执行展示命令

Networking -> Tunnels -> <tunnel> -> Add a replica

Cloudflare 会显示包含 Token 的 cloudflared 安装命令. 把命令复制到临时编辑器只取其中 eyJ... 的 Token, 不要执行这条展示命令 —— Token 会进入 shell history 和进程参数. 立即隐藏录入:

vault --business zmuke set cloudflare-zmuke-tunnel-<tunnel> TUNNEL_TOKEN

6.3 Connector 运行原则

生产 systemd unit 通过受限凭据注入提供 TUNNEL_TOKENTUNNEL_TOKEN_FILE, 不把 Token 写进 ExecStart.

7. Access Application 和策略

每个对外 hostname 一个 Application. 基本构成:

  1. 默认拒绝: 先建一条拒绝一切的兜底策略, 再往上加放行.
  2. 人的放行: 接 IdP (Google Workspace / GitHub / OIDC / SAML), 按邮箱域或 IdP 组放行, 要求 MFA. 无 IdP 时可用 One-time PIN; 配合 Require IP 白名单可作为所有者核定的长期方案 (ZMUKE 2026-08-15 决策), 前提是收件邮箱自身有 MFA.
  3. 机器的放行: 只有确实存在机器调用时, 才加 Service Auth 策略, 且只 Include 那一个具体 Service Token. 不要配置成允许所有 Service Token.

Access 不是 Fleet 的 RBAC. 通过 Access 只代表可以抵达这个 hostname; Fleet 自身的登录、Workspace 授权、step-up 和审计一个都不能少.

7.1 控制面的例外

Fleet 控制面的设计前提是零公网暴露 —— Agent 只经 OpenVPN overlay 抵达, 管理平面不对外. 把 Fleet 控制台发布到 Tunnel 是对这条前提的改变, 需要单独决策, 不属于常规操作. 若确实要发布:

8. Access Service Token

浏览器用户通过 IdP/MFA 登录, 不需要 Service Token. 只有 CI/CD 调用受保护接口、外部监控探针访问受保护健康检查等明确登记的自动化场景才创建.

Cloudflare Dashboard -> Zero Trust -> Access controls -> Service credentials -> Service Tokens

每个调用方单独创建, 设有限有效期. Client Secret 只显示一次:

vault --business zmuke set cloudflare-zmuke-access-<caller> CF_ACCESS_CLIENT_ID
vault --business zmuke set cloudflare-zmuke-access-<caller> CF_ACCESS_CLIENT_SECRET

profile 名带调用方, 是为了能单独吊销其中一个而不影响其他.

9. WAF 基线

10. 上线顺序

  1. 创建该组织的 *-edge-infra 并存入 Vault.
  2. 创建 Named Tunnel, 保存 runtime token.
  3. 配置 Tunnel 的 origin service, 使用可验证的 HTTPS.
  4. 创建 Access Application 和默认拒绝策略.
  5. 添加人的 Allow 策略, 要求 MFA.
  6. 仅在存在机器调用时添加具体的 Service Auth 策略.
  7. 配置 WAF 基线和高风险路径速率限制.
  8. 最后发布 public hostname 和 DNS 记录.
  9. 从未授权浏览器、授权浏览器和管理 VPN 分别验证访问行为.

第 8 步排在最后是本节的要点: 准入和防护先就位, 再让这个名字在公网上可解析.

11. 存入 Vault

zmuke 是独立业务域, 首次使用需要 init:

vault --business zmuke init
vault --business zmuke set cloudflare-zmuke-workspace-admin CLOUDFLARE_ACCOUNT_ID
vault --business zmuke set cloudflare-zmuke-workspace-admin CLOUDFLARE_API_TOKEN
vault --business slaunchx set cloudflare-slaunchx-workspace-admin CLOUDFLARE_ACCOUNT_ID
vault --business slaunchx set cloudflare-slaunchx-workspace-admin CLOUDFLARE_API_TOKEN
vault --business mocalmax set cloudflare-mocalmax-workspace-admin CLOUDFLARE_ACCOUNT_ID
vault --business mocalmax set cloudflare-mocalmax-workspace-admin CLOUDFLARE_API_TOKEN

Zone ID 不入库. 使用时 GET /zones?name=<根域名>.

每条命令都是隐藏输入. 检查键名, 不显示值:

vault --business zmuke list
vault --business slaunchx list
vault --business zmuke check
vault --business zmuke run cloudflare-zmuke-workspace-admin -- wrangler whoami

wrangler whoami 不代表每个写权限都已验证. 部署工具应先提供只读检查或 dry-run, 再执行写入.

12. 轮换和撤销

API Token

  1. 在 Cloudflare 创建权限完全相同的新 Token.
  2. vault --business zmuke rotate <profile> CLOUDFLARE_API_TOKEN 隐藏录入.
  3. 运行身份检查和部署 dry-run.
  4. 确认新 Token 工作后撤销旧 Token.
  5. 记录轮换时间和下次到期日, 不记录 Token 值.

Tunnel runtime token

  1. Tunnel 页面选择 Rotate token.
  2. vault --business zmuke rotate cloudflare-zmuke-tunnel-<tunnel> TUNNEL_TOKEN.
  3. 逐个重启副本, 每次确认 Tunnel 仍 healthy, 不要同时中断全部 connector.
  4. 若已泄露, 除轮换外还要在 Cloudflare 端强制断开旧连接.

Access Service Token

到期前刷新或创建替代 Token, 更新调用方后撤销旧的. Client Secret 丢失不可找回, 只能重新生成.

13. 检查清单

操作员手册,不要公开传播。不含 Token / Account ID / Zone ID。HANDOFF 不在此站。