diff --git a/docs/cloudflared-integration-plan.md b/docs/cloudflared-integration-plan.md index f8d295e..40204cb 100644 --- a/docs/cloudflared-integration-plan.md +++ b/docs/cloudflared-integration-plan.md @@ -1,8 +1,8 @@ # Cloudflare Tunnel(cloudflared)与 Fonu 的融合方案 -> 日期:2026-09-25 对象:Fonu 1.0.2(VERSION 1.2.12) -> 方法:先查 Cloudflare 官方文档与同类开源项目,再逐个核对本仓库现有可复用面(下文带 `文件:行` 的都是读过代码得出的)。 -> 结论速览:**值得做,但建议以「第二条出口」的形态并存,先做 locally-managed + 进程托管,不接 Cloudflare API**。详见 §五、§六。 +> 日期:2026-09-25,v2 修订 2026-09-28 对象:Fonu 1.5.4(VERSION 1.5.4) +> 方法:先查 Cloudflare 官方文档与同类开源项目,再逐个核对本仓库现有可复用面(下文带 `文件:行` 的都是读过代码得出的);v2 逐文件核对了 `F:\WWW\cf\Tunnel-Cloudflared-linux`(Tunnel-Cloudflared-Manager 1.0.1)源码。 +> 结论速览:**值得做,但建议以「第二条出口」的形态并存,先做 locally-managed + 进程托管,不接 Cloudflare API**。v2 修订了部署位置、模式生效侧、metrics / 健康与二进制校验四处关键细节,详见 §五、§六、§十二;实施以 §十二 为准。 --- @@ -57,7 +57,7 @@ Cloudflare Tunnel 把「服务端」换成 Cloudflare 的边缘网络:本机 ` **结论:第一轮只支持 locally-managed。** 若用户只想粘贴 token,提供「仅托管进程」的降级模式并**在界面上明确写清「规则由 Cloudflare 面板管理」**,不要让用户以为 Fonu 生成的规则生效了。 -> 补充(参考 §九 后修正):这个二分法不够细。参考实现证明**token 可以解码**(base64 的 JSON,含 `AccountTag` / `TunnelID` / `TunnelSecret`),用它自己生成 `creds.json` 就能按 locally-managed 跑本地 `config.yml`;再配合「云端 ↔ 本地 ingress 互相同步」,两种模式实际上是可切换的。所以 Fonu 的正确做法不是「二选一」,而是**显式区分并同步**:当前生效的是本地 `config.yml` 还是云端 ingress,以及「推送/拉取」两个动作。 +> 补充(参考 §九 后修正):这个二分法不够细。参考实现证明**token 可以解码**(base64 的 JSON,含 `AccountTag` / `TunnelID` / `TunnelSecret`),用它自己生成 `creds.json` 就能按 locally-managed 跑本地 `config.yml`;再配合「云端 ↔ 本地 ingress 互相同步」,两种模式实际上是可切换的。所以 Fonu 的正确做法不是「二选一」,而是**显式区分并同步**:当前生效的是本地 `config.yml` 还是云端 ingress,以及「推送/拉取」两个动作。**v2 已把模式拆成三种并明确生效侧,见 §12.3。** ### 健康探测(这点比 frp 更省事) @@ -112,6 +112,8 @@ Cloudflare Tunnel 把「服务端」换成 Cloudflare 的边缘网络:本机 ` ## 六、推荐落地路径(分阶段) +> 本节阶段划分是 v1 口径;v2 修订见 §12.10,实施以 §12.10 为准。 + ### 阶段 1:只读与引导(最低风险,先验证需求) 1. 新增「Cloudflare 隧道」入口:粘贴 **token**(remotely-managed)或上传 **credentials JSON + tunnel UUID**(locally-managed),凭据加密存储。 @@ -158,6 +160,8 @@ Cloudflare Tunnel 把「服务端」换成 Cloudflare 的边缘网络:本机 ` ## 九、参考实现分析:`cf-tunnel-manager`(用户自有项目,Gitee `li-hua-88/cf-tunnel-manager`) +> 本节是 2026-09-25 的 v1 分析。2026-09-28 已对照 1.0.1 Linux 源码逐文件复核,补充与修正见 §十二。 + > 项目:**Tunnel-Cloudflared-Manager** v1.0.1,Rust + Actix-web + Tokio + Serde,前端原生 JS + Tailwind(`static/`),JSON 文件存储,MIT;Linux systemd(`tcm`/`cftm` 运维脚本)+ Docker 两套交付。 > 本节结论来自逐个读到源码的模块:`src/services/{tunnel,binary,cloudflare_api}.rs`、`src/models/tunnel.rs`、`src/utils/{config_helper,paths}.rs`、`Cargo.toml`。 @@ -254,6 +258,8 @@ Cloudflare Tunnel 把「服务端」换成 Cloudflare 的边缘网络:本机 ` | 阶段 3(可选) | 「边缘优选」工具:并发探测官方 IPv4 段的 **TCP 7844 握手延迟** → 生成「两个 region 各 1~2 个优选 IP」的 hosts 建议;**页面展示而不是代写 hosts**(Windows 改 hosts 需管理员,Fonu 不应越权),并提供「复制这段内容」+ 生效检测 | 沿用本仓「不越权、降级要说明原因」的口径 | | 阶段 3(可选) | 优选前后对比:用**已有的** `metrics_samples` 延迟/可用率序列展示效果,而不是另造一套测速图 | 复用 §四 的健康与指标链路 | +> metrics 端口发现、健康判定与网络调优默认值见 §12.5 / §12.7;本节只讨论「本机 → CF 边缘」的优选。 + ### 必须写进功能说明的三句实话 1. 优选只影响「本机 → CF 边缘」这一段;访客到自己那侧 CF 边缘的那段,Fonu 与用户都控制不了。 @@ -270,4 +276,279 @@ Cloudflare Tunnel 把「服务端」换成 Cloudflare 的边缘网络:本机 ` --- +## 十二、方案优化 v2(结合 Tunnel-Cloudflared-Manager 1.0.1 与 Fonu 1.5.4) + +> 本节是 2026-09-28 的优化版落地口径。§一~§十一 保留为背景与论证;出现冲突时以本节为准。 + +### 12.1 源码核对新增的关键事实 + +逐文件核对 `F:\WWW\cf\Tunnel-Cloudflared-linux` 后,以下事实会直接改变实施细节: + +| 事实 | 源码位置 | 对方案的影响 | +| --- | --- | --- | +| token 解码后的 JSON 字段是 `a` / `t` / `s`,兼容 `AccountTag` / `TunnelID` / `TunnelSecret`;使用标准 base64 并自动补 `=` | `src/services/cloudflare_api.rs` 的 `decode_tunnel_token` / `base64_decode` | token 模式也能本地生成 `creds.json`,从而按 locally-managed 跑本地规则 | +| `cert.pem` 内含 `-----BEGIN ARGO TUNNEL TOKEN-----` 块,解出 `accountID` + `apiToken` / `serviceKey` | 同文件 `parse_cert_pem` | 只有账号模式持有云端 API 凭据,才能 push / fetch 云端 ingress | +| `start_tunnel` 在 token 非空时优先用 `--token` 启动,即使已经生成了本地 `config.yml` | `src/services/tunnel.rs` 的 `start_tunnel` | **本地规则会被云端覆盖**;Fonu 必须显式选择「本地生效」还是「云端生效」 | +| 命名隧道**不传** `--metrics`,从 stderr 解析 `starting metrics server on 127.0.0.1:/metrics`;快速隧道传 `--metrics 127.0.0.1:0` | `start_tunnel` / `start_quick_tunnel` | metrics 端口必须从日志发现,不能固定,否则多实例会撞端口 | +| 健康用 `cloudflared_tunnel_ha_connections > 0` 判定已连接;QUIC 有字节指标,HTTP/2 没有 | `start_metrics_scraper` | `/ready` 只适合做就绪判断,连接数与带宽要走 metrics;HTTP/2 带宽要明确降级 | +| 二进制下载只校验「文件 >= 10MB」,没有 SHA-256;`get_available_mirrors` 返回的镜像 ID 与实际支持的镜像不一致 | `src/services/binary.rs` | Fonu 必须做哈希 / 版本校验,并统一镜像表 | +| ingress 三条不变式:末条固定 `http_status:404`、`localhost` 归一化为 `127.0.0.1`、新规则插在 catch-all 之前 | `add_tunnel_endpoint` / `add_tunnel_endpoint_local_only` | 生成器必须锁定这三条并加纯函数测试 | +| Windows 子进程设置 `CREATE_NO_WINDOW` | `start_tunnel` | Fonu 在 Windows 托管 `cloudflared.exe` 时必须带,否则闪黑窗 | +| 快速隧道从 stderr 解析 `*.trycloudflare.com` URL 并缓存 | `start_quick_tunnel` | 只能做「临时演示」;URL 每次重启都会变 | +| 存储是 JSON 文件 + 内存 `HashMap` | `src/storage/config.rs`、`TunnelProcessManager` | Fonu 用 SQLite + 迁移,多实例与并发状态更稳 | + +### 12.2 部署位置:原方案最大的空白 + +原方案默认「本机跑 cloudflared」,但没有回答一个关键问题:**Fonu 是容器化部署,容器里的 `127.0.0.1` 不是宿主机。** + +Fonu 的运行时(`Dockerfile`)在同一个容器里装了主程序与 nginx,`docker-compose.yml` 只映射 `6893` / `80` / `443`。因此 v2 的默认拓扑是: + +> **cloudflared 跑在 Fonu 容器内,与 nginx 同命名空间;CF ingress 统一指向 `http://127.0.0.1:`(默认 80),由 nginx 再转发到上游。** + +这样做有四个直接收益: + +1. 绕开容器网络命名空间的坑:`127.0.0.1` 在容器内确定指向 Fonu nginx,而不是宿主机或上游。 +2. 复用现有 nginx 的安全、限流、geo、TLS、错误页与 Basic Auth,不在 CF 侧重复一套策略。 +3. 规则同源:CF 只负责「域名 → Fonu nginx」,nginx 负责「域名 → 上游」;本机反代规则仍是唯一事实来源。 +4. 只有 HTTP / HTTPS 规则能进 CF;TCP / UDP 规则明确排除,避免用户误解。 + +三种拓扑的取舍: + +| 拓扑 | cloudflared 位置 | ingress 目标 | 适用 | +| --- | --- | --- | --- | +| **容器内(默认)** | Fonu 容器 | `http://127.0.0.1:80`(容器内 nginx) | 绝大多数部署;规则先接入本机 nginx | +| host 网络 | Fonu 容器 `network_mode: host` | `http://127.0.0.1:80`(宿主机 nginx) | 需要直接访问宿主机端口、或已有宿主机 nginx | +| VPS Agent(可选) | VPS 上的 `fonu-agent` 托管 | `http://127.0.0.1:` | 已有 VPS、只想让部分域名走 CF 的高级用户 | + +部署注意事项: + +- cloudflared 是**出站 only**,不要为它额外映射任何入站端口。 +- 二进制、`creds.json`、`cert.pem`、`config.yml`、日志都必须落在 `/data` 持久卷下,否则容器重建即丢失。 +- 如果要暴露宿主机或内网服务,推荐先把该服务接入 Fonu 的本机 nginx 反代,再让 CF 指向 nginx;不要在 CF ingress 里直接写宿主机 IP。 +- host 网络模式只作为显式选项,不默认开启;开启后 `127.0.0.1` 的语义会变化,UI 必须提示。 + +### 12.3 凭据与模式:显式区分「谁在生效」 + +参考实现证明了 token 可以解码,但也暴露了一个陷阱:**token 启动与本地 `config.yml` 是互斥的**。v2 把模式拆成三种,UI 必须让用户明确选择并显示当前生效侧: + +| 模式 | 凭据 | 启动方式 | 规则来源 | 云端 fetch / push | +| --- | --- | --- | --- | --- | +| **A. token + 本地规则** | token | 解码 token → 写 `creds.json` → `tunnel --config config.yml run` | Fonu 生成的 `config.yml` | ❌(token 不含 API 凭据) | +| **B. token + 云端规则** | token | `tunnel run --token ` | Cloudflare Dashboard / 云端 | ❌ | +| **C. 账号 + 本地规则 + 云端同步** | `cert.pem` + credentials JSON | `tunnel --config config.yml run` | 本地,可与云端互相同步 | ✅ | + +实施口径: + +- 模式 A 绝不传 `--token`,只用 `--config`,否则本地规则不生效。 +- 模式 B 必须在 UI 明确写「规则由 Cloudflare 面板管理,Fonu 的本地预览不生效」,不要让用户以为 Fonu 生成的规则已经下发。 +- 模式 C 才提供「从云端拉取」「推送到云端」两个动作;token 模式不提供,只给 Dashboard 指引。 +- 所有凭据走 `secret.Box` 加密字段;预览一律掩码,下发时用原文。`creds.json` 权限 `0600`,`config.yml` 权限 `0640`,目录 `0750`。 +- 状态接口返回 `effective_side: local | cloud`,前端常驻显示,切换模式时要求二次确认。 + +### 12.4 数据模型与目录布局(Fonu 版) + +参考实现用 JSON 文件 + 内存 HashMap,适合单进程小工具;Fonu 已有 SQLite 与迁移体系,v2 建议落成表: + +`cf_tunnels`: + +| 字段 | 说明 | +| --- | --- | +| `id` / `name` | 主键与用户可见名称 | +| `mode` | `token-local` / `token-remote` / `account-local` | +| `tunnel_id` / `account_tag` | 从 token 或 credentials 解析 | +| `credentials_enc` | token 或 credentials JSON 密文 | +| `cert_pem_enc` | 账号模式 `cert.pem` 密文 | +| `api_token_enc` | 可选的云端同步 API Token 密文 | +| `config_path` / `creds_path` / `log_path` | 运行文件路径 | +| `binary_version` | 当前使用的 cloudflared 版本 | +| `network_json` | protocol / edge-ip-version / ha-connections / proxy_mode / originRequest | +| `status` / `last_error` | `stopped` / `starting` / `connected` / `reconnecting` / `failed` | +| `metrics_port` | 从日志发现的端口,仅运行时缓存 | +| `auto_start` | Fonu 启动时是否自动拉起 | + +目录布局: + +```text +/cloudflared/ + bin//cloudflared + tunnels// + config.yml + creds.json + cert.pem + cloudflared.log +``` + +多实例 = 多目录 + 多子进程 + 多日志文件;快速隧道单独用 `mode=quick` 记录,`public_url` 只在运行期有效。 + +### 12.5 进程与二进制管理 + +建议新建 `internal/cloudflared` 包,沿用 `internal/frp` 的 runtime / manager 模式,但不直接耦合 frp 模型: + +**二进制** + +- 统一镜像表:`official` / `gh-proxy-org` / `gh-proxy-hk` / `gh-proxy-cdn` / `gh-proxy-edgeone` + 自定义 URL 模板;修正参考项目「镜像列表与实现不一致」的问题。 +- 下载校验:优先官方 checksums(若提供);否则至少校验文件大小、可执行性、`cloudflared --version`;镜像源与官方源使用同一份哈希。 +- 版本目录 + 原子替换 + 激活指针,复用 `internal/frp/runtime.go` 的 `readActiveBinary` / `writeActiveBinary` 思路。 +- 支持 linux amd64 / arm64、windows amd64、darwin amd64 / arm64;Fonu 侧用 `runtime.GOOS/GOARCH` 选择。 + +**进程** + +- `exec.Command` + stdout / stderr 逐行读取,同时追加写入 `/cloudflared.log`。 +- Windows 设置 `CREATE_NO_WINDOW`(`0x08000000`)。 +- 启动参数: + - 模式 A / C:`tunnel --config run` + - 模式 B:`tunnel run --token ` + - 快速隧道:`tunnel --metrics 127.0.0.1:0 --url ` +- 网络参数:`--ha-connections`、`--origin-ca-pool`、`--no-tls-verify`、`--http-host-header`;环境变量 `TUNNEL_TRANSPORT_PROTOCOL`、`TUNNEL_EDGE_IP_VERSION`。 + +**metrics 端口发现** + +- 命名隧道不传 `--metrics`;快速隧道传 `--metrics 127.0.0.1:0`。 +- 从 stderr 用正则解析:`starting metrics server on 127\.0\.0\.1:(\d+)/metrics`。 +- 解析到端口后轮询 `http://127.0.0.1:/metrics`,最多等待 60 秒;超时给出明确原因,不留下空白状态。 + +**健康判定** + +- `/ready` 返回 200:cloudflared 已就绪。 +- `cloudflared_tunnel_ha_connections > 0`:已连上边缘。 +- 延迟:QUIC 用 `quic_client_smoothed_rtt`;HTTP/2 用 `cloudflared_rpc_client_latency_secs` 或主动 HEAD 探测。 +- 带宽:QUIC 直接用 `quic_client_receive_bytes` / `quic_client_sent_bytes` 差值;HTTP/2 官方没有字节指标,Fonu 第一轮明确降级为「不展示带宽」并给出原因,不做参考项目那套进程 socket 统计。 + +--- + +### 12.6 ingress 生成器(与 nginx 规则同源) + +输入: + +- 本机反代规则中启用且属于 HTTP / HTTPS 的域名; +- Fonu nginx 的容器内入口端口(默认 `80`,可用配置覆盖); +- 用户勾选要暴露的域名集合。 + +输出:`config.yml` 的 `ingress` 数组。三条不变式必须写进生成器并用纯函数测试锁定: + +1. 末条永远是 `- service: http_status:404`; +2. `localhost` 一律归一化为 `127.0.0.1`; +3. 新规则插在 catch-all 之前,且同域名去重、排序稳定。 + +与 nginx 的关系: + +```text +访客 → Cloudflare 边缘 → cloudflared(Fonu 容器)→ 127.0.0.1:80(Fonu nginx)→ 上游 +``` + +- CF 侧不重复配置限流、geo、TLS、Basic Auth;这些仍由本机 nginx 负责。 +- 只允许 HTTP / HTTPS 规则进入 CF ingress;TCP / UDP 规则在界面上明确标为「Cloudflare 隧道不支持」。 +- 生成后提供「配置预览(脱敏)」与「生成后自检」,自检至少覆盖三条不变式与空 ingress 场景。 + +### 12.7 网络调优(默认值与下发方式) + +| 设置 | Fonu 默认 | 下发方式 | 说明 | +| --- | --- | --- | --- | +| `transport_protocol` | `http2` | 环境变量 `TUNNEL_TRANSPORT_PROTOCOL` + `config.yml` 的 `protocol` | 国内 QUIC(UDP 7844)常被 QoS,TCP 更稳;提供 `auto` / `quic` | +| `edge_ip_version` | `4` | 环境变量 `TUNNEL_EDGE_IP_VERSION` + `config.yml` 的 `edge-ip-version` | 2026.4.0 起官方默认改为 `auto`,国内 IPv6 路由差时反而变慢;提供 `auto` / `6` | +| `ha_connections` | `2` | `--ha-connections` + `config.yml` 的 `ha-connections` | 每个 region 的连接数,1–4 可调 | +| `proxy_mode` | `system` | 启动时按需移除 / 保留 `HTTP_PROXY` 等环境变量 | `disabled` 时清空代理变量;检测到代理或 Windows 时协议回落 `http2` | +| `originRequest` | 空 | `--origin-ca-pool` / `--no-tls-verify` / `--http-host-header` | 仅高级用户;默认不回源 HTTPS,避免自签证书问题 | + +诊断接口返回:平台、代理环境探测结果、实际协议、实际代理模式、建议。降级必须给出原因,例如 `windows_estats_requires_admin`、`http2_official_metrics_unavailable`。 + +边缘优选(阶段 3 可选): + +- 只做「本机 → CF 边缘」的 TCP 7844 延迟探测与展示,不代写 hosts; +- 两个 region 都要覆盖,IP 列表可配置、有默认值、可一键恢复默认; +- 访客到自己那侧 CF 边缘的一段无法优选,功能说明必须写清楚。 + +### 12.8 云端同步(仅账号模式) + +账号模式(`cert.pem`)才提供云端同步,API 细节: + +- 拉取:`GET https://api.cloudflare.com/client/v4/accounts/{account_id}/cfd_tunnel/{tunnel_id}/configurations` +- 推送:`PUT` 同一路径,body 为 `{"config":{"ingress":[...]}}` +- 推送的 ingress 必须包含末条 catch-all,否则 Cloudflare 可能拒绝或行为不确定。 + +冲突处理: + +- UI 常驻显示 `effective_side: local | cloud`; +- 「拉取到本地」会覆盖本地 `config.yml`,操作前备份当前版本(复用本仓 `fsutil.BackupVersioned`); +- 「推送到云端」会覆盖云端 ingress,操作前展示 diff 并二次确认; +- 两个动作都写审计日志(操作者、隧道、方向、结果)。 + +token 模式不提供 push / fetch,只给 Dashboard 指引;这是凭据能力的硬边界,不要用「同步失败」这类模糊提示掩盖。 + +### 12.9 与 Fonu 现有模块的集成点 + +| Fonu 模块 | 复用还是新增 | 具体落点 | +| --- | --- | --- | +| 二进制下载 / 启用 | 新增 `internal/cloudflared`,参考 `internal/frp/runtime.go` | 镜像表、哈希校验、版本目录、激活指针 | +| 进程托管 | 新增 `internal/cloudflared`,参考 `internal/frp/manager.go` | 启停、日志追加、退出原因、`tailFile` | +| 健康与可用率 | 复用 `internal/monitor` 的 `Source` / `HealthRecorder` | 新增 CF Tunnel Source;`/ready` + `ha_connections` | +| 通知 | 复用 `internal/notify` | 隧道断开 / 恢复事件 | +| 状态页 | 复用现有公开状态页 | 新增隧道条目与可用率 | +| 规则来源 | 复用 `internal/proxy` 的本机反代规则 | 只取 HTTP / HTTPS 域名,生成 ingress | +| 日志界面 | 复用现有日志页模型 | 读 `/cloudflared.log`,不引入 WebSocket | +| 凭据加密 | 复用 `internal/secret` | token / cert.pem / credentials JSON 全部加密 | +| frp 链路 | **不改** | CF Tunnel 是并存出口,不碰现有 frp 规则、健康与状态语义 | + +### 12.10 修订后的阶段计划 + +| 阶段 | 交付物 | 验收标准 | +| --- | --- | --- | +| 阶段 1:凭据与配置 | 凭据导入(token / cert.pem)、模式选择、`creds.json` + `config.yml` 生成、脱敏预览 | 三种模式都能生成文件;模式 B 明确提示本地规则不生效;凭据不明文回显 | +| 阶段 2:进程与健康 | 二进制下载 / 启用、进程启停、日志、metrics 端口发现、`/ready` + 连接数健康 | 启动后能连上边缘;停止后进程与端口释放;健康状态落库并进通知 | +| 阶段 3:规则同源与云端同步 | 从本机反代规则生成 ingress、漂移检测、账号模式 fetch / push、网络调优设置 | ingress 三条不变式测试通过;云端同步有 diff 与审计;网络参数可配置 | +| 阶段 4(可选) | 边缘优选、快速隧道、高级 originRequest | 优选只展示不越权;快速隧道明确标注「临时演示」 | + +### 12.11 测试与验收 + +单元测试: + +- token 解码:标准 base64、缺填充、非法输入、`a/t/s` 与 `AccountTag/TunnelID/TunnelSecret` 两种字段名; +- ingress 生成:catch-all、`localhost` 归一化、插入位置、去重、空规则; +- metrics 解析:端口行、QUIC / HTTP/2 指标、连接数、降级原因; +- 网络参数构建:protocol / edge-ip-version / ha-connections / proxy_mode; +- 镜像 URL 与自定义模板; +- 凭据脱敏:token、cert.pem、credentials JSON 不出现在预览与日志。 + +集成测试: + +- 启动 / 停止 / 重启 / 自动启动; +- metrics 端口发现与健康状态流转; +- 日志追加与 tail; +- 云端 fetch / push 用 mock API 验证请求体、catch-all 与错误处理; +- 容器内 `127.0.0.1:80` 回源链路。 + +前端: + +- 隧道列表 / 详情 / 状态 / 日志 / 配置预览; +- 模式选择与「当前生效侧」提示; +- 脱敏预览与二次确认。 + +真机矩阵:Linux amd64 / arm64、Windows、Docker 容器内、host 网络、代理环境、国内与海外链路。 + +### 12.12 风险与边界(补充) + +- **容器网络**:默认拓扑是容器内 cloudflared → 容器内 nginx;不要假设 `127.0.0.1` 是宿主机。 +- **token 覆盖**:模式 B 的本地 `config.yml` 不生效;UI 必须明确,模式 A 才能本地管规则。 +- **HTTP/2 带宽**:官方 metrics 没有字节指标;第一轮明确降级并说明原因,不做进程 socket 统计。 +- **Windows**:`CREATE_NO_WINDOW` 必带;若做进程级字节统计还需要管理员权限,Fonu 第一轮不做。 +- **CF 条款**:视频 / 大文件分发不在推荐用法内;TryCloudflare 不保证 SLA。 +- **国内链路**:CF 边缘延迟受运营商影响;优选只影响本机到边缘一段。 +- **API 凭据**:账号模式的 `cert.pem` 与可选 API Token 都是高权限凭据,必须最小化权限并加密存储。 +- **Access**:只做诊断提示,不代配策略。 +- **TCP / UDP**:CF Tunnel 不是通用 TCP 出口;需要访问端装 cloudflared 的场景不在首轮范围。 +- **不改 frp**:现有 frp 链路、健康与状态页语义保持不变。 + +### 12.13 结论 + +Cloudflare Tunnel 对 Fonu 是**第二条出口**,不是 frp 的替代:它补上「没有公网 IP、也不想长期租 VPS」的 HTTP 类场景。v2 的核心修订是四件事: + +1. cloudflared 跑在 Fonu 容器内,ingress 统一指向容器内 nginx,绕开容器网络坑; +2. 显式区分「本地规则生效」与「云端规则生效」,不让 token 模式悄悄覆盖本地配置; +3. metrics 端口从日志发现,健康用 `/ready` + `ha_connections`,HTTP/2 带宽明确降级; +4. 二进制下载补哈希 / 版本校验,ingress 生成器锁定三条不变式。 + +按 §12.10 的阶段 1–3 实施即可形成可用闭环;阶段 4 的边缘优选与快速隧道作为可选增强,不阻塞主线。 + +--- + > 说明:本文只做方案分析,未改动任何代码。实施时沿用本仓库既有约定:每步验证 `go build/vet/test` + `vue-tsc/vite build`、agent 行为变更递增 `AgentVersion`、提交同批更新 `CHANGELOG` 与 `VERSION`、密钥相关内容必须过一遍「预览脱敏 / 下发原文」两头检查。