From 9a2616dc0ee104ff27e9d499ce632c83e358c0a6 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 1 Jun 2026 11:48:55 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/design/architecture.md | 24 ++++--- docs/design/index.md | 74 +++++++++++++++++----- docs/guildline/development-constraints.md | 76 +++++++++++++++++++++-- 3 files changed, 142 insertions(+), 32 deletions(-) diff --git a/docs/design/architecture.md b/docs/design/architecture.md index e4e9b0e2..74dd4841 100644 --- a/docs/design/architecture.md +++ b/docs/design/architecture.md @@ -125,24 +125,30 @@ Agent 上报应用结果 ### Relay 同步流 +Relay(OpenFlareRelay 进程)运行在 TunnelRelay 节点上,与 Agent 共享同一 `agent_token`: + ```text -Relay HTTP heartbeat -> Server 返回 frps 配置 -Relay 生成 frps.toml 并启动/重启 frps -Relay 定期上报 frps 状态 +Relay HTTP heartbeat -> Server 返回 frps 基础配置 (bindPort, vhostHTTPPort, auth_token) +Relay 生成 frps.toml 并启动或更新 frps 进程 +Relay 定期上报 frps 健康状态与连接统计 +Relay 尝试升级 WebSocket 连接以支持实时配置推送 ``` -Relay 使用与 Agent 相同的 `agent_token` 认证(同一节点),通过 `/api/relay/*` 端点通信。frps 配置相对静态(端口、认证 Token),通过心跳下发,不纳入版本化发布流。 +frps 配置相对静态(端口、认证 Token),通过心跳下发,**不纳入版本化发布流**。Relay 需要监听 frps 进程异常并自动恢复。认证方式:`X-Agent-Token` + API 路径前缀 `/api/relay/*`,Server 通过 `node_type = tunnel_relay` 区分。 ### OpenFlared 同步流 +OpenFlared(客户端)运行在内网服务器,使用独立的 `tunnel_token` 认证: + ```text -Client HTTP heartbeat -> Server 返回 tunnel 配置版本摘要 -Client 发现新版本 -> 拉取 tunnel 路由配置(relay 列表 + proxy 定义) -Client 为每个 Relay 生成 frpc.toml 并启动/重载 frpc 进程 -Client 上报应用结果 +Client HTTP heartbeat -> Server 返回 tunnel 配置版本摘要 (version, checksum) +Client 发现新版本 -> 拉取完整 tunnel 路由配置 (relay 列表 + frpc proxy 定义) +Client 为每个 Relay 生成独立的 frpc.toml 配置文件 +Client 为新 Relay 启动 frpc 进程,或为已有 Relay 执行热重载 (frpc reload) +Client 上报应用结果 (成功/失败原因) ``` -OpenFlared 使用独立的 `tunnel_token` 认证,通过 `/api/flared/*` 端点通信。Tunnel 路由配置随发布流程版本化同步,配置变更时优先使用 `frpc reload` 热重载。 +OpenFlared 通过 `/api/flared/*` 端点与 Server 通信,认证使用 `X-Tunnel-Token`。Tunnel 路由配置随发布流程版本化同步,所有配置变更通过单一版本号关联并一致性发布到 Agent 和 Client。 **WebSocket 升级流程**(可选,通过 `AgentWebsocketUpgradeEnabled` 选项控制): diff --git a/docs/design/index.md b/docs/design/index.md index 681f0521..2f60ce4c 100644 --- a/docs/design/index.md +++ b/docs/design/index.md @@ -87,32 +87,72 @@ OpenFlare 当前不定位为通用日志平台、服务网格、Kubernetes Ingre ## 内网穿透约束 -OpenFlare 通过 TunnelRelay 节点与 OpenFlared 客户端实现内网穿透,底层基于 frp 构建。 +OpenFlare 通过 TunnelRelay 节点与 OpenFlared 客户端实现内网穿透,底层基于 frp(快速反向代理)构建。 -节点类型: +### 节点与组件模型 + +**节点类型**: * `nodes.node_type` 区分节点类型:`edge_node`(边缘节点,默认)和 `tunnel_relay`(隧道中继)。 -* TunnelRelay 节点同时运行 Agent(管理 OpenResty)和 Relay(管理 frps),共享同一个 `agent_token`。 -* Agent 负责 HTTPS 终结、WAF 防护等,Relay 负责隧道流量中继。 +* TunnelRelay 节点同时运行 Agent(OpenResty)和 Relay(frps 管理器),共享同一个 `agent_token`。 + - Agent 负责 HTTPS 终结、WAF 防护、缓存与流量限制等。 + - Relay 管理 frps 进程,为内网客户端提供隧道中继服务。 +* TunnelRelay 节点新增字段:`node_type`、`relay_bind_port`(frpc 连接端口,默认 7000)、`relay_vhost_http_port`(HTTP Vhost 端口,默认 8080)、`relay_auth_token`(自动生成)、`relay_status` 等。 -Tunnel 实体: +**Tunnel 客户端**: -* `tunnels` 表存储内网穿透客户端注册信息,与 `nodes` 体系独立。 -* 每个 Tunnel 拥有唯一的 `tunnel_id`(格式 `tun-<32hex>`)和 `tunnel_token`。 -* OpenFlared 客户端使用 `tunnel_token` 认证,通过 `/api/flared/*` 端点通信。 +* `tunnels` 表独立存储内网穿透客户端注册信息,与 `nodes` 体系无关。 +* 每个 Tunnel 拥有唯一的 `tunnel_id`(格式 `tun-<32hex>`)和 `tunnel_token`(客户端认证凭据)。 +* OpenFlared 客户端运行在内网,不对外暴露,使用 `tunnel_token` 认证,通过 `/api/flared/*` 端点与 Server 通信。 +* 一个 OpenFlared 客户端可同时连接多个 Relay(为高可用)。 -流量路径: +### 上游类型扩展 -* 数据面:浏览器 → Agent(OpenResty,TLS/WAF)→ Relay(frps,HTTP Vhost 路由)→ 隧道 → Client(frpc)→ 内网服务。 -* frps 使用 HTTP Vhost 单端口复用,通过 Host 头将请求路由到对应 frpc,无需为每个隧道分配端口。 -* Relay 配置(frps 端口、认证 Token)通过心跳下发,相对静态。 -* Tunnel 路由配置(frpc 代理定义)随发布流程版本化同步。 +`proxy_routes` 的上游配置分为两种类型,通过 `upstream_type` 字段区分: -当前阶段约束: +* **直连上游(`direct`,默认)**:直接将流量转发到源站地址,行为与现有完全一致。 +* **内网穿透上游(`tunnel`)**:通过 TunnelRelay 节点将流量转发到内网服务。 + - 必须指定 `tunnel_id`(关联 `tunnels` 表)。 + - 必须指定 `tunnel_target_addr`(内网目标地址,如 `192.168.1.100:8080`)和 `tunnel_target_protocol`(`http` 或 `https`)。 + - 发布时,Server 自动将上游地址替换为 `http://127.0.0.1:{relay_vhost_http_port}`。 -* 仅支持 HTTP 协议隧道流量,保留未来 TCP 隧道扩展性。 -* Tunnel 类型上游的域名 DNS 应仅解析到 TunnelRelay 节点,EdgeNode 上对应请求会因 frps 不可达返回 502。 -* 一个 OpenFlared 客户端可连接多个 Relay(每个 Relay 对应一个 frpc 进程)。 +### 流量路径与协议 + +**完整数据面流量路径**: + +``` +浏览器 → OpenResty (Agent, TLS/WAF) [TunnelRelay 节点] + ↓ + frps (Relay, HTTP Vhost 路由) [TunnelRelay 节点, 127.0.0.1:{vhost_port}] + ↓ + frp 隧道协议 (Host 头路由) + ↓ + frpc (Client, 多进程) [内网服务器] + ↓ + 内网服务 (192.168.x.x:port) +``` + +**关键特性**: + +* frps 使用 HTTP Vhost 单端口复用机制,所有 HTTP 隧道共享一个 `vhost_port`,通过 Host 头自动路由到对应 frpc。 +* Agent 保留原始 `Host` 请求头,frps 依据此头进行虚拟主机匹配。 +* 每个隧道对应一条 `proxy_routes`,可绑定多个域名。 +* OpenFlared 客户端为每个连接的 Relay 管理一个独立的 frpc 进程,通过单一 frp 隧道传输多个 HTTP 代理定义。 + +### 配置同步模型 + +发布流程同时生成两类配置版本数据,统一使用 `config_version` 版本号关联: + +* **Agent 侧配置**:OpenResty 主配置 + 路由配置 + WAF 规则。包含 tunnel 上游时,自动渲染为 `http://127.0.0.1:{vhost_port}` 上游。 +* **Tunnel 侧配置**:Relay 列表 + frpc 代理定义。随发布流程版本化,变更时优先使用 `frpc reload` 热重载。 +* **Relay 配置**:通过心跳响应下发,相对静态,不纳入版本化流程。 + +### 当前阶段约束 + +* 仅支持 HTTP 协议隧道流量,保留未来 TCP/UDP 隧道扩展性。 +* Tunnel 类型上游的域名 DNS 应仅解析到 TunnelRelay 节点;EdgeNode 上对应请求会因 frps 不可达返回 502。 +* frp 版本使用 v0.61+(或更新稳定版),frp 二进制由部署脚本或 Docker 镜像提供。 +* 暂不支持 TCP/UDP 端口分配;HTTP 单端口复用已满足 MVP 需求。 ## HTTPS 约束 diff --git a/docs/guildline/development-constraints.md b/docs/guildline/development-constraints.md index 4847cb25..9c27814d 100644 --- a/docs/guildline/development-constraints.md +++ b/docs/guildline/development-constraints.md @@ -83,22 +83,50 @@ Frontend: ### 1. 当前有效实体 * **核心配置与反代**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名). * **节点与状态**:`nodes` (节点), `node_system_profiles` (系统概况), `apply_logs` (应用日志). +* **内网穿透**:`tunnels` (隧道客户端), `tunnel_tokens` (隧道认证令牌,可选持久化). * **观测与分析**:`node_request_reports` (请求上报), `node_access_logs` (访问明细), `node_metric_snapshots` (指标快照), `traffic_analytics_rollups` (流量聚合), `node_health_events` (健康事件). * **系统配置与第三方登录**:`options` (全局参数), `auth_sources` (第三方认证源), `external_accounts` (外部绑定账号). * **安全与 WAF**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定). ### 2. 底层数据库技术约束 + 在编写或修改模型时,必须严格遵守以下持久化与数据库设计准则: + * **禁止随意引入平台化新实体**:除非 [产品边界](../design/index.md) 设计发生调整并经评审。 + * **业务唯一性保障**: * `proxy_routes.site_name` 作为业务唯一主标识。 * `proxy_routes.domains` 中的各域名必须全局唯一,不可跨站点冲突,列表第一项视为主域名。 + * `nodes.node_id` 唯一标识节点(自动生成或由用户指定)。 + * `tunnels.tunnel_id` 唯一标识内网穿透客户端(格式 `tun-<32hex>`,自动生成)。 + * **兼容字段处理**:遗留的 `proxy_routes.domain` 只能作为 `domains[0]` 的只读/兼容镜像,新代码不得以该字段为唯一业务输入。 + * **多上游及 Keepalive**:单上游时应支持 base path/query 并在 `proxy_pass` 中正确补齐 URI;多上游负载均衡时仅允许纯 `scheme://host[:port]`。 + * **证书映射**:证书绑定必须通过逐域名平行的 `domain_cert_ids` 字段精确保存,未绑定证书的域名不得参与 HTTPS 渲染。 + * **版本快照一致性**:`config_versions` 必须保存版本发布时的完整快照及 checksum 校验码,确保渲染结果不可变且全局单激活版本。 + * **外部账户唯一绑定**:第三方登录必须通过 `external_accounts` 映射至本地唯一用户,原 `users.github_id` 仅用于向后兼容迁移,任何新登录流程禁止以此为业务输入。 +* **Tunnel 与上游关联**: + * `proxy_routes.upstream_type = 'tunnel'` 时,必须指定 `tunnel_id`(关联到 `tunnels` 表)。 + * 必须指定 `tunnel_target_addr`(内网目标地址,如 `192.168.1.100:8080`)和 `tunnel_target_protocol`(`http` 或 `https`)。 + * 发布配置时,Server 自动将此上游渲染为 `http://127.0.0.1:{relay_vhost_port}`,Agent 依据 Host 头由 frps 路由。 + +* **TunnelRelay 节点配置**: + * `nodes.node_type = 'tunnel_relay'` 时,新增字段 `relay_bind_port`、`relay_vhost_http_port`、`relay_auth_token` 必须有合理默认值。 + * `relay_bind_port` 默认 7000,`relay_vhost_http_port` 默认 8080。 + * `relay_auth_token` 由 Server 自动生成(32 位随机字符串),不由用户输入。 + * 相对静态配置(如 `relay_agent_access_addr`、`relay_client_access_addr`)由 Relay 心跳下发,Server 可记录但不纳入版本化流。 + +* **Tunnel 客户端状态**: + * `tunnels.status` 记录客户端在线/离线/待激活状态。 + * `tunnels.current_version` / `tunnels.current_checksum` 记录当前已应用的配置版本。 + * `tunnels.connected_relays` 以 JSON 数组形式存储已连接 Relay 的信息(relay_node_id、连接状态等)。 + * `last_seen_at`、`last_error` 用于调试和可观测性。 + ## 数据库迁移 任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。 @@ -128,7 +156,7 @@ v1-v7 视为历史初始基线,不再维护逐版本升级文件。从 v8 起 ## API 与鉴权 -管理端与 Agent API 统一使用 JSON。成功与失败都必须返回清晰 `message`: +管理端与 Agent/Relay/Client API 统一使用 JSON。成功与失败都必须返回清晰 `message`: ```json { @@ -140,16 +168,29 @@ v1-v7 视为历史初始基线,不再维护逐版本升级文件。从 v8 起 约定: -* Agent API 固定放在 `/api/agent/*`。 +* Agent API 固定放在 `/api/agent/*`,使用 `X-Agent-Token` 认证(节点专属 token)。 +* **Relay API** 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 认证(同 TunnelRelay 节点)。 + - Server 通过 token + `/api/relay/*` 路径区分 Relay 请求。 + - Relay 心跳返回 frps 配置(bindPort、vhostHTTPPort、authToken)。 + - Relay 上报进程状态、连接数、proxy 列表等指标。 +* **Tunnel Client API** 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 认证(独立的 tunnel_token)。 + - OpenFlared 使用 `tunnel_token` 与 Server 通信,独立于 Agent 认证体系。 + - Client 心跳返回 tunnel 配置版本摘要。 + - Client 可拉取完整配置(relay 列表 + frpc 代理定义)。 + - Client 上报配置应用结果。 +* **Admin Tunnel 管理 API** - `/api/tunnels/*`,要求 Admin Session。 + - CRUD tunnel 实体(创建、查询、更新、删除)。 + - Token 管理(生成、轮换)。 + - 强制同步(触发 Client 立即拉取新配置)。 * 总览与节点详情优先使用专用聚合接口。 * 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。 * 管理端继续复用现有登录、角色与 Session。 * 第三方登录统一通过认证源 API 进入,认证源管理接口必须要求 Root Session。 * `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。 * 第三方账号未绑定且注册关闭时,应提供绑定已有账号流程,不得自动创建用户。 -* Agent 正式请求统一使用节点专属 `agent_token`。 -* 首次接入可使用全局 `discovery_token`。 -* Agent 请求头统一使用 `X-Agent-Token`。 +* Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。 +* 首次接入 Agent 可使用全局 `discovery_token`;首次接入 Client 由 Server 生成 tunnel_token,直接用于部署命令。 +* Agent/Relay 请求头统一使用 `X-Agent-Token`;Client 请求头统一使用 `X-Tunnel-Token`。 禁止暴露远程 shell 或任意命令执行入口,禁止在日志中打印完整 Token,禁止绕过占位符约束保存不可渲染的主配置模板。 @@ -161,14 +202,18 @@ v1-v7 视为历史初始基线,不再维护逐版本升级文件。从 v8 起 * 同时读取 OpenResty 主配置参数、反代性能参数与缓存参数。 * 读取 WAF 规则组、规则组引用的 IP 组与网站绑定关系,并在发布快照中保存可回放数据。 * 自动型 WAF IP 组只能由 Server 定时任务读取请求日志并执行 Expr 布尔规则,OpenResty Lua 与 Agent 不得直接访问请求日志库或执行自动挖掘逻辑。 +* **内网穿透配置扩展**:区分上游类型,为 `upstream_type = 'tunnel'` 的代理规则生成独立的 tunnel 配置数据。 + * OpenResty 侧:将 tunnel 上游自动渲染为 `http://127.0.0.1:{relay_vhost_port}`,必须保留原始 `Host` 请求头。 + * Tunnel 侧:为每个 Client 生成完整的 relay 列表与 frpc 代理定义(frpc proxy 配置)。 * 生成完整 OpenResty 配置。 * 计算 `checksum`。 -* 写入 `config_versions`。 +* 写入 `config_versions`(OpenResty 部分)+ 生成或更新 tunnel 配置版本数据。 * 通过切换 `is_active` 激活版本。 版本约束: * 版本号格式固定为 `YYYYMMDD-NNN`。 +* 同一版本号同时关联 OpenResty 配置与 Tunnel 配置,保证一致性。 * 不在线修改历史版本。 * 不做按节点分组的差异化版本。 * 预览与 diff 是只读能力,不产生发布记录。 @@ -190,6 +235,25 @@ Agent 必须满足: * 某个目标 `version + checksum` 一旦应用失败并回退,Agent 必须在本地状态中阻断该目标的重复应用。 * Agent 维护本地 MaxMind mmdb 时,下载或刷新失败只能记录警告,不得阻断心跳、同步、配置应用或 OpenResty 健康检查。 +OpenFlareRelay 必须满足: + +* 启动后从 config 读取 Server 地址和 `agent_token`。 +* 周期性向 Server 发送心跳,获取 frps 配置(bindPort、vhostHTTPPort、authToken)。 +* 根据心跳响应生成 frps.toml,启动或更新 frps 进程。 +* 上报 frps 进程健康状态、连接数、proxy 数等指标。 +* frps 进程异常时自动重启,并上报失败信息。 +* 可选支持 WebSocket 升级连接,接收实时配置推送。 + +OpenFlared 必须满足: + +* 启动后从 config 读取 Server 地址和 `tunnel_token`。 +* 周期性向 Server 发送心跳,获取 tunnel 配置版本摘要。 +* 发现新版本后拉取完整 tunnel 配置(relay 列表 + frpc 代理定义)。 +* 为每个 relay 生成独立 frpc.toml,启动新 frpc 进程或对已有进程执行热重载。 +* 上报每个 frpc 进程的健康状态与连接情况。 +* 配置应用失败时记录错误并上报,支持重试。 +* 可选支持 WebSocket 升级连接,接收实时配置变更通知。 + ## 前端请求、状态与类型 所有 API 请求必须统一经过 `lib/api/`: