mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
131 lines
8.7 KiB
Markdown
131 lines
8.7 KiB
Markdown
# 内网穿透隧道设计文档
|
||
|
||
你会学到:OpenFlare 内网穿透隧道的架构设计、双端管控组件(Relay 与 Client)的内部原理、交互逻辑以及数据面与控制面的通信流程。
|
||
|
||
---
|
||
|
||
## 需求分析
|
||
|
||
在典型的 Web 应用托管场景中,许多源站(Origin Server)部署在内网环境(如本地开发机、局域网服务器或受防火墙限制的内网集群)。这些服务器通常:
|
||
1. **无公网 IP**:无法直接被公网流量访问。
|
||
2. **安全合规限制**:不允许随意在边界路由器上配置端口映射(NAT)。
|
||
3. **动态 IP 变动**:传统的 DDNS 方案延迟高且极不稳定。
|
||
|
||
为了让内网源站能够无缝接入 OpenFlare 全局数据网关并享受 WAF 地域防护、TLS 证书托管等增值服务,OpenFlare 设计了基于 **反向中继穿透隧道** 的整体解决方案。在该架构中,公网边缘节点作为反代入口和流量中继,内网侧仅需发起安全出向连接,即可实现公网流量安全、稳定地反向穿透到内网源站。
|
||
|
||
---
|
||
|
||
## 核心功能
|
||
|
||
内网穿透隧道子系统包含以下核心能力:
|
||
|
||
* **Relay 节点动态管理**:由控制面动态派发中继服务(frps),动态分发服务端口与认证令牌(Token)。
|
||
* **多隧道反向代理映射**:支持在单个内网客户端上映射多个内网 Web 端口,并将多域名路由绑定至对应的中继节点。
|
||
* **独立进程生命周期管控**:中继与客户端均为 Go 编写的独立二进制守护进程,内部负责拉起、监控、自愈及热升级底层的 frp 引擎。
|
||
* **基于 Token 的独立认证隔离**:中继端使用 `agent_token`,内网客户端使用专属 `tunnel_token`,权限与路由边界隔离。
|
||
* **配置校验与增量热重载**:仅在隧道绑定关系、证书或 Relay 拓扑发生实际变化时,才重写配置文件并平滑重载进程,降低运行开销。
|
||
|
||
---
|
||
|
||
## 内网穿透与隧道架构
|
||
|
||
内网穿透子系统基于成熟的 `frp` 高性能隧道协议进行整合,分为 **控制面 (Control Plane)** 与 **数据面 (Data Plane)**。
|
||
|
||
```mermaid
|
||
graph TD
|
||
%% 数据流
|
||
Browser[1. 浏览器 / 访客] -->|HTTPS 请求| Agent[2. OpenResty / Agent]
|
||
Agent -->|本机转发 proxy_pass| RelayFrps[3. OpenFlare Relay / frps]
|
||
RelayFrps -->|加密隧道协议| FlaredFrpc[4. OpenFlared / frpc]
|
||
FlaredFrpc -->|转发本地请求| LocalOrigin[5. 内网源站 192.168.x.x]
|
||
|
||
%% 控制流与心跳
|
||
Server[OpenFlare Server 控制面] <-->|Relay API / Heartbeat| RelayManager[openflare_relay 进程]
|
||
Server <-->|Client API / Heartbeat| ClientManager[openflared 进程]
|
||
|
||
RelayManager -.->|管控进程及配置| RelayFrps
|
||
ClientManager -.->|管控多 Relay 进程| FlaredFrpc
|
||
|
||
style Browser fill:#f9f,stroke:#333,stroke-width:2px
|
||
style LocalOrigin fill:#9f9,stroke:#333,stroke-width:2px
|
||
style Server fill:#f96,stroke:#333,stroke-width:2px
|
||
```
|
||
|
||
* **控制面(Control Plane)**:Server 维护数据库状态;中继节点上的 `openflare_relay` 进程与内网服务器上的 `openflared` 进程通过 HTTP 心跳与 WebSocket 长通道同步隧道配置。
|
||
* **数据面(Data Plane)**:公网流量首先进入公网边缘的 Agent (OpenResty),在此完成 HTTPS 握手、TLS 终止和 WAF 过滤,接着通过 `proxy_pass` 转发到同机部署的 `openflare_relay (frps)`。`frps` 再将请求封包通过与内网 `openflared (frpc)` 建立的持久隧道传输过去,最后由 `frpc` 拆包并分发给内网实际的源站服务。
|
||
|
||
---
|
||
|
||
## Relay (中继端) 设计
|
||
|
||
`openflare_relay` 是部署在公网边缘的中继管理器,运行在 `tunnel_relay` 类型的节点上。
|
||
|
||
### 1. 核心架构与逻辑
|
||
* **进程守护**:Relay 进程内部持有 `frps` 二进制,通过 `exec.Command` 拉起 `frps -c frps.toml` 子进程,并启动 goroutine 异步监听其退出状态。如果发现 `frps` 异常退出,会结合退避机制自动拉起。
|
||
* **动态配置渲染**:通过 HTTP 心跳向控制面同步状态,获取当前的 `RelayConfig`,主要参数包括:
|
||
* `bindPort`:frps 用于监听内网 frpc 客户端连接的公网控制端口。
|
||
* `vhostHTTPPort`:虚拟主机(Virtual Host)HTTP 流量监听端口,Agent 的 proxy_pass 会指向此端口。
|
||
* `authToken`:客户端连接时进行握手校验的安全凭证。
|
||
* `webServer`:开启 frps 的仪表盘 API,Relay 基于此接口或管理控制端口收集实时的活跃隧道数和流量指标。
|
||
* **状态上报**:Relay 每周期心跳会向控制面上报底层 `frps` 的活跃连接数、注册客户端数、各个代理通道的实时状态以及 Relay 版本。
|
||
|
||
---
|
||
|
||
## Openflared (客户端) 设计
|
||
|
||
`openflared` 是运行在用户内网服务器侧的客户端管理器,使用独立的 `tunnel_token` 进行鉴权。
|
||
|
||
### 1. 核心设计机制
|
||
* **多 Relay 支持(多路复用)**:
|
||
为保障高可用或就近接入,控制面可能会将客户端连接调度到多个公网 Relay。`openflared` 会读取 `TunnelConfig` 中下发的 Relays 列表,在本地为每一个 Relay 节点独立生成一个专用的配置文件(命名为 `frpc_<relay_node_id>.toml`),并分别为每个 Relay 进程分配独立的 cancelable context。
|
||
* **子进程独立监控**:
|
||
`openflared` 内部维护一个 `processes` 映射表,对每个 `frpc` 子进程进行独立的生命周期管控。当控制面增加或移除 Relay 时,客户端会增量拉起新进程或优雅注销老进程,避免影响其他正常工作的隧道。
|
||
* **动态 TOML 生成**:
|
||
为每个 Relay 渲染 TOML 时,客户端会遍历 Proxies 列表,将每个内网服务的 `LocalAddr`、`LocalPort`、绑定的 `CustomDomains` 写入到 `[[proxies]]` 块中。
|
||
|
||
---
|
||
|
||
## 交互逻辑与流量模型
|
||
|
||
内网穿透子系统实现了一致性版本控制和状态反馈。
|
||
|
||
### 1. 控制面发布与同步流程
|
||
|
||
```text
|
||
管理员修改隧道/内网端口映射 -> 提交发布 -> 生成新 Tunnel 版本与 Checksum
|
||
|
|
||
v (推送或心跳拉取)
|
||
+-------------------------------------------+-------------------------------------------+
|
||
| |
|
||
v (中继端) v (内网客户端)
|
||
openflare_relay 心跳检测到 frps 端口/Token 变化 openflared 心跳检测到 tunnel_version 发生变更
|
||
重新渲染本地 frps.toml 请求拉取最新代理映射包
|
||
Kill 并重新拉起 frps 进程 重新渲染 frpc_<relay_id>.toml
|
||
上报健康状态为 healthy 对有变更的 Relay 进程执行重启与配置热重载
|
||
上报应用结果 (Apply Success/Error)
|
||
```
|
||
|
||
1. **版本化控制**:所有内网隧道的路由和映射关系与主路由系统类似,也经过版本化控制,下发 `version` 与 `checksum`,确保客户端不重复写入和频繁重载进程。
|
||
2. **应用结果闭环**:客户端应用新配置后,会在心跳中携带应用结果上报控制面。若因内网端口不可达或证书配置有误导致 frpc 无法建连,客户端会截获进程输出将 `LastError` 上报,管理员在 Server 即可直观查看穿透失败原因。
|
||
|
||
### 2. 数据面流量模型
|
||
1. **公网入口 (Agent)**:
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name intranet.example.com;
|
||
# ... TLS 证书与 WAF 过滤逻辑 ...
|
||
location / {
|
||
proxy_pass http://127.0.0.1:18080; # 指向本地 frps 的虚拟主机端口
|
||
proxy_set_header Host $host; # 必须保留原 Host,因为 frps 依靠 Host 进行内部路由分发
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
2. **中继节点 (frps)**:
|
||
`frps` 监听到 `18080` 端口有 HTTP 请求进来,读取 HTTP 请求头中的 `Host: intranet.example.com`,在其已注册的活跃隧道表中检索该域名对应的加密 TCP 连接(由内网 frpc 建立)。
|
||
3. **加密隧道传输 (TCP)**:
|
||
`frps` 将 HTTP 请求封装进内部 TCP 隧道协议,发送给内网的 `frpc` 客户端。
|
||
4. **内网客户端分发 (frpc)**:
|
||
`openflared` 管理的 `frpc` 收到封包,根据本地配置(`localIP = "127.0.0.1"`, `localPort = 8080`)将请求建立本地 TCP 连接转发给内网 Web 服务,并将 Web 服务的响应原路打包返回,最终呈现给公网用户。
|