基于 T2 设备上的 zfrpc、zrps、frpc、nginx 配置分析 分析日期:2026-05-22
一、核心结论:四层递进式远程访问架构
极空间的远程访问不是单一方案,而是 四层递进 的混合架构,从最优到兜底依次尝试:
用户外网访问 *.direct.zspace.link 或 *.direct.ud.link
│
┌───────┴────────┐
│ DNS 解析 │
│ (DDNS/zddns) │
└───────┬────────┘
│
┌─────────▼─────────┐
│ 1. P2P 直连 (最优) │ ← zrps + UPnP/STUN 打洞
│ 延迟最低,无中转 │
└─────────┬─────────┘
│ 失败
┌─────────▼─────────┐
│ 2. zrps Relay 中转 │ ← 自研中继协议,设备互联
│ 极空间中继服务器 │ zconn 协议
└─────────┬─────────┘
│ 失败/特定场景
┌─────────▼─────────┐
│ 3. frp 反向代理 │ ← 开源 frp 0.32.0
│ 极空间自建 frps │ TCP 端口映射
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ 4. DDNS 直连 (兜底)│ ← 公网 IP + 端口映射
│ zddns + UPnP │
└───────────────────┘
二、四层方案详解
第一层:P2P 直连(zrps + STUN + UPnP)
这是极空间的核心远程方案,自研程度最高。
关键组件:
| 组件 | 路径 | 作用 |
|---|---|---|
| zrps | /zspace/applications/services/zrps/zrps | P2P 连接管理与中继 |
| zupnp | /zspace/applications/services/zupnp/zupnp | UPnP IGD 端口映射 |
| STUN 探测 | proxy.zenithspace.net:3478 | NAT 类型检测、获取公网 IP |
工作流程:
1. zupnp 通过 UPnP 协议向路由器申请端口映射
将内网 5055/5056 映射到公网
2. zrps 向 proxy.zenithspace.net:3478 (STUN) 查询公网 IP 和 NAT 类型
3. 客户端连接时,zrps 尝试 UDP/TCP 打洞
成功 → P2P 直连,延迟最低
失败 → 进入 Relay 模式
zrps 配置关键项:
{
"env": {
"firewall": {
"allow_ports": "tcp:5055,tcp:8050" // 允许外部访问的端口
},
"port": {
"p2p": "20000-30000" // P2P 打洞使用的端口范围
}
},
"system": {
"id": "/zspace/system/private/id", // 设备唯一 ID
"privkey": "/zspace/system/private/privkey", // 设备私钥
"pubkey": "/zspace/system/private/pubkey" // 设备公钥
}
}
zrps 监听端口:
| 端口 | 用途 |
|---|---|
| 127.0.0.1:22345 | zrps API(速度测试、连接管理) |
| 127.0.0.1:8041 | Relay 代理(跨设备中转) |
第二层:Relay 中继(zconn 协议)
当 P2P 打洞失败时(对称 NAT 等场景),流量通过极空间的中继服务器转发。
zconn 协议(ZSpace Connection Protocol)是极空间自研的设备间通信协议,在 nginx 配置中有完整实现:
# 设备间连接路由
location /zconn/http/5056/ {
# 解析目标设备 ID
# 如果目标是自身 → 走本地 5056
# 如果目标是其他设备 → 走 zrps relay 8041
if target_nas_id ~= self_nas_id then
port = 5056 # 本地
else
port = 8041 # relay 中转
end
}
这意味着用户可以通过任意一台自己的极空间设备访问另一台,zrps 自动判断是否需要中继。
分享链接的跨设备路由:
# /redirect/v2/share/ — 分享链接
# 读取请求头 shareNasId,判断目标是本地还是远程
# 本地 → 8050 (go-baseset)
# 远程 → 8041 (zrps relay)
第三层:frp 反向代理(开源方案,兜底)
极空间部署了两套 frp 客户端:
方案 A:自研 zfrpc(优先)
路径:/usr/zfrpc/zfrpc 和 /zspace/applications/services/zfrp/zfrpc
- 自研 Go 二进制(4.4MB)
- 通过 systemd 管理(
zfrpc.service) - 启动时会比较
/usr/zfrpc/和/zspace/applications/services/zfrp/两个版本的版本号,自动选择新版 - 这是 OTA 可升级的版本
方案 B:开源 frpc 0.32.0(兜底)
路径:/zspace/shell/tool/frpc/frpc_openwrt
配置文件 /zspace/shell/tool/frpc/frpc.ini(敏感字段已脱敏):
[common]
server_addr = <极空间自建 frps 服务器 IP>
server_port = 10050
token = <认证 token>
[ssh_<设备序列号>] # SSH 隧道
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 32xxx # 外网通过该端口访问 SSH
[http_<设备序列号>] # HTTP 隧道
type = tcp
local_ip = 127.0.0.1
local_port = 5055
remote_port = 52xxx # 外网通过该端口访问 Web
frp 服务器是极空间自建的 frps(frp server),不是公网 frp 服务。
每条隧道以设备 ID 命名,远程端口按规则分配(SSH 和 HTTP 各一个)。
第四层:DDNS + UPnP(传统兜底)
- zddns:动态 DNS 服务,将设备公网 IP 绑定到
*.direct.zspace.link域名 - zupnp:向路由器申请 UPnP 端口映射
- 公网 IP 通过
proxy.zenithspace.net:3478(STUN 协议,端口 3478 是标准 STUN)获取
三、远程访问域名体系
| 域名 | 用途 | TLS |
|---|---|---|
*.direct.zspace.link | 用户远程访问主域名 | Let’s Encrypt 证书 |
*.direct.ud.link | 备用域名 | 同证书 |
proxy.zenithspace.net | STUN/信令服务器 | - |
证书由 zbasic 服务自动申请和管理:
ssl_certificate /zspace/applications/services/zbasic/data/new_fullchain.pem
ssl_certificate_key /zspace/applications/services/zbasic/data/new_priv.key
四、Nginx 远程访问入口(5056 端口)
5056 端口是远程访问的统一 HTTPS 入口,配置极其复杂,集成了所有远程功能:
:5056 (HTTPS)
│
├── /v2/* → go-baseset (:8050) 核心 API
├── /zconn/http/5056/* → 设备间连接路由 跨设备访问
├── /redirect/v2/share/* → 分享链接跨设备路由
├── /syncthing/* → Syncthing (:8385) 文件同步
├── /docker/api/* → zdocker (:9123) Docker 管理
├── /hdmiapi/* → HDMI 输出 (:20124)
├── /netdisk/* → 网盘 (:5300)
├── /wps/* → WPS 文档
├── /ws → WebSocket (:8001)
├── /settings → 系统设置 (动态路由)
├── @dynamic_proxy → Lua 动态代理(远程访问白名单)
│
└── / → 默认转发到 :8020 (PC Web)
动态代理机制
# remote_access.conf
if ($dynamic_proxy_target) {
return 418; # 触发 @dynamic_proxy 错误页处理
}
location @dynamic_proxy {
proxy_pass $dynamic_proxy_target; # Lua 动态设置目标
}
Lua 脚本 rewrite_token.lua 在此处做 token 认证 + 动态路由:
- 验证用户 token 有效性
- 根据访问权限将请求路由到正确的后端服务
- 支持远程访问白名单控制(
RemoteAccessWhiteList.json)
五、设备身份与安全
设备唯一标识
每台极空间在出厂时烧录唯一 ID,格式为 <型号代码><序列号>(如 T020 开头即 T2 型号),主机名也从 ID 派生(形如 T2-xxxx)。
密钥体系
/zspace/system/private/
├── id ← 设备唯一 ID
├── privkey ← 设备私钥(用于 zrps P2P 加密)
└── pubkey ← 设备公钥(用于服务端验证设备身份)
多层认证
| 层级 | 机制 | 位置 |
|---|---|---|
| 传输加密 | TLS (Let’s Encrypt) | nginx 5056 |
| Token 认证 | rewrite_token.lua | nginx Lua |
| 密码二次验证 | auth_pwdcheck.lua | 敏感操作(格式化等) |
| 设备认证 | 公私钥对 | zrps P2P 握手 |
| frp 认证 | token | frpc.ini |
| 远程访问白名单 | RemoteAccessWhiteList.json | zrps |
六、NAT 穿透技术栈
┌────────────────────────────────────────┐
│ 极空间云服务 │
│ │
│ proxy.zenithspace.net:3478 (STUN) │
│ frp server (自建 frps) │
│ zrps relay server (中继) │
│ │
└────────────┬───────────────────────────┘
│
┌────────┴────────┐
│ 用户家庭网络 │
│ │
│ ┌───────────┐ │
│ │ zupnp │──┼──► 路由器 UPnP 端口映射
│ └───────────┘ │
│ ┌───────────┐ │
│ │ zrps │──┼──► P2P 打洞 / Relay 选择
│ └───────────┘ │
│ ┌───────────┐ │
│ │ frpc │──┼──► frps 反向代理 (兜底)
│ └───────────┘ │
│ ┌───────────┐ │
│ │ zddns │──┼──► DDNS 域名更新
│ └───────────┘ │
└─────────────────┘
STUN 协议 (proxy.zenithspace.net:3478):
- 端口 3478 是标准 STUN 端口(RFC 5389)
- 极空间用 STUN 协议而非自研,这里选择了标准方案
- 用于 NAT 类型检测和公网 IP 获取
七、设备间互联(zconn 协议亮点)
5056 端口 nginx 配置中最精彩的部分是 zconn 协议——实现了设备间透明路由:
用户 --→ 设备A (5056) --zconn--> 设备B (8041 relay 或直连)
典型场景:
- 你在外地,手机上打开极空间 App
- App 连接你家 T2 设备,同时也想看看父母家 Z2 设备的照片
- 通过 T2 的 5056 端口,nginx 自动判断
/zconn/http/5056/<Z2的ID>/...不是本地 - 请求自动通过 zrps relay (8041) 中转到 Z2 设备
- 整个过程中你只连了一个域名
八、总结
极空间的远程访问不是简单套用某个开源方案,而是一个精心设计的四层递进系统:
| 层 | 方案 | 自研程度 | 延迟 | 适用场景 |
|---|---|---|---|---|
| 1 | P2P 直连 (zrps + STUN + UPnP) | 高(自研 zrps) | 最低 | NAT 类型兼容 |
| 2 | Relay 中继 (zconn 协议) | 高(自研协议) | 中 | 对称 NAT |
| 3 | frp 反向代理 | 低(开源 frp + 自研 zfrpc) | 中 | 兜底 |
| 4 | DDNS 直连 | 低(标准方案) | 取决于网络 | 有公网 IP |
其中 zrps + zconn 是技术含量最高的部分,实现了:
- 自动 P2P 打洞
- 智能 Relay 切换
- 设备间透明路由
- 公私钥认证体系
这套架构的设计思路是:优先 P2P 省带宽,不行走 Relay,再不行兜底 frp,绝不会连不上。