mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 07:36:38 +08:00
docs: design nftables forwarding
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
# nftables 纯转发设计
|
||||
|
||||
**日期**: 2026-05-30
|
||||
**状态**: 待审核
|
||||
**作者**: Codex
|
||||
|
||||
## 概述
|
||||
|
||||
为 FLVX 增加一种不依赖 agent 的纯转发能力:节点可选择 `nftables` 转发模式,面板通过 SSH 在节点机器上下发和维护 nftables 规则。
|
||||
|
||||
第一阶段只支持端口级 DNAT/SNAT 纯转发。它不是 GOST 隧道能力的替代品,也不支持链路、限速、流量统计、连接数限制、Proxy Protocol、best exit 或 agent 诊断。目标是提供一个可靠、可回滚、可重建的轻量转发路径。
|
||||
|
||||
## 背景
|
||||
|
||||
当前 FLVX 的转发模型由三部分组成:
|
||||
|
||||
- `node` 表描述节点,现有本地节点通过 agent WebSocket 接收运行时命令。
|
||||
- `tunnel` 表描述入口、出口和链路类型,`type=1` 表示端口转发,`type=2` 表示隧道转发。
|
||||
- `forward` 表描述用户规则、入口端口和目标地址,运行时通过 GOST service 下发到入口节点。
|
||||
|
||||
nftables 模式的核心差异是没有 agent,因此不能复用现有 WebSocket command 通道,也不能依赖 agent 上报在线状态、流量和诊断结果。面板必须成为唯一控制面,通过 SSH 把数据库中的期望状态同步到远端 nftables。
|
||||
|
||||
## 用户决策
|
||||
|
||||
- 创建或编辑节点时选择转发模式。
|
||||
- 选择 nftables 转发后,不需要安装 agent。
|
||||
- nftables 转发不支持隧道、流量控制等能力,只支持纯转发。
|
||||
- 规则由面板端维护,并通过 SSH 下放到节点。
|
||||
|
||||
## 推荐方案
|
||||
|
||||
新增节点运行时模式:
|
||||
|
||||
| 模式 | 含义 |
|
||||
|------|------|
|
||||
| `agent` | 默认模式,保持现有 GOST agent 行为 |
|
||||
| `nftables` | 面板通过 SSH 管理 nftables 规则 |
|
||||
|
||||
业务层继续复用现有 `tunnel` 和 `forward` 概念,但对 nftables 模式加严格能力边界:
|
||||
|
||||
- nftables 节点只能创建端口转发隧道。
|
||||
- nftables 隧道不能配置出口节点或转发链。
|
||||
- 同一个隧道的入口节点必须全部是同一种运行时模式。
|
||||
- nftables 转发规则创建、更新、删除时,由后端同步 SSH 规则。
|
||||
- 面板提供节点级“测试 SSH”“重建规则”“清理 FLVX 规则”操作。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不支持 `tunnel.type=2` 隧道转发。
|
||||
- 不支持多跳链路、远程面板共享节点和 federation runtime。
|
||||
- 不支持 GOST service 能力:限速、每 IP 限速、最大连接数、Proxy Protocol、策略负载均衡。
|
||||
- 不支持 agent 流量统计、实时系统指标、节点升级、回退、agent 安装命令。
|
||||
- 不在第一阶段支持 HA 漂移、自动探活切换或复杂负载均衡。
|
||||
- 不改写用户机器上的非 FLVX nftables 规则。
|
||||
|
||||
## 数据模型
|
||||
|
||||
### node 表
|
||||
|
||||
新增字段:
|
||||
|
||||
| 字段 | 类型 | 默认 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `forward_mode` | string | `agent` | `agent` 或 `nftables` |
|
||||
|
||||
Go 模型使用 SQLite/PostgreSQL 兼容 tag:
|
||||
|
||||
```go
|
||||
ForwardMode string `gorm:"column:forward_mode;type:varchar(20);not null;default:'agent'"`
|
||||
```
|
||||
|
||||
### node_ssh_config 表
|
||||
|
||||
新增表保存 nftables 节点 SSH 配置。SSH 凭据不放进 `node` 主表,避免普通节点列表过度暴露敏感字段。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 主键 |
|
||||
| `node_id` | 关联节点,唯一 |
|
||||
| `host` | SSH 主机,默认可使用 node.server_ip |
|
||||
| `port` | SSH 端口,默认 22 |
|
||||
| `username` | SSH 用户 |
|
||||
| `auth_type` | `password` 或 `private_key` |
|
||||
| `password` | 加密后密码,可为空 |
|
||||
| `private_key` | 加密后私钥,可为空 |
|
||||
| `passphrase` | 加密后私钥口令,可为空 |
|
||||
| `sudo_mode` | `none` / `sudo` |
|
||||
| `created_time` | 创建时间 |
|
||||
| `updated_time` | 更新时间 |
|
||||
|
||||
第一阶段可使用现有配置密钥派生或面板本地密钥做对称加密;如果项目尚无统一密钥管理,应至少避免在列表 API 返回完整凭据。
|
||||
|
||||
### nft_rule_binding 表
|
||||
|
||||
记录面板认为已经应用到节点的规则状态,用于更新、删除、重建和错误展示。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 主键 |
|
||||
| `forward_id` | 转发规则 ID |
|
||||
| `node_id` | 下发节点 ID |
|
||||
| `in_port` | 入口端口 |
|
||||
| `protocols` | 第一阶段固定 `tcp,udp` |
|
||||
| `target_addr` | 目标地址 |
|
||||
| `bind_ip` | 可选监听 IP |
|
||||
| `rule_hash` | 当前期望规则 hash |
|
||||
| `status` | `pending` / `applied` / `error` |
|
||||
| `last_error` | 最近错误 |
|
||||
| `applied_time` | 最近成功应用时间 |
|
||||
| `created_time` | 创建时间 |
|
||||
| `updated_time` | 更新时间 |
|
||||
|
||||
绑定表不是最终事实来源。最终期望状态仍从 `forward`、`forward_port`、`tunnel` 和 `chain_tunnel` 推导,绑定表只记录应用结果。
|
||||
|
||||
## API 行为
|
||||
|
||||
### 节点创建和更新
|
||||
|
||||
`/node/create` 和 `/node/update` 新增入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"forwardMode": "nftables",
|
||||
"sshConfig": {
|
||||
"host": "203.0.113.10",
|
||||
"port": 22,
|
||||
"username": "root",
|
||||
"authType": "private_key",
|
||||
"privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----...",
|
||||
"passphrase": "",
|
||||
"sudoMode": "none"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `forwardMode` 缺省时按 `agent`。
|
||||
- `agent` 节点保留现有字段和行为。
|
||||
- `nftables` 节点要求 SSH 配置完整。
|
||||
- 从 `agent` 切到 `nftables` 前,若该节点已有 agent 隧道链路或转发规则,应拒绝并提示先迁移或删除。
|
||||
- 从 `nftables` 切回 `agent` 前,若存在 nftables 规则,应拒绝并提示先清理或迁移。
|
||||
|
||||
### 隧道创建和更新
|
||||
|
||||
创建 nftables 隧道仍使用 `/tunnel/create`,但后端根据入口节点模式校验能力。
|
||||
|
||||
规则:
|
||||
|
||||
- 入口节点为 nftables 时,`type` 必须为 `1`。
|
||||
- 不允许提交 `outNodeId` 或 `chainNodes`。
|
||||
- 入口节点必须在线的现有校验不能直接套用到 nftables 节点;应改为 SSH 可用性校验或允许保存后手动测试。
|
||||
- 同一隧道入口节点不能混用 `agent` 和 `nftables`。
|
||||
- 更新隧道时不允许改变运行时模式;需要通过迁移规则到新隧道实现。
|
||||
|
||||
### 转发创建和更新
|
||||
|
||||
选择 nftables 隧道时,`/forward/create` 和 `/forward/update` 强制收窄字段:
|
||||
|
||||
- `speedId` 必须为空。
|
||||
- `ipSpeedId` 必须为空。
|
||||
- `maxConn` 和 `ipMaxConn` 必须为 0。
|
||||
- `proxyProtocol` 必须为 0。
|
||||
- 第一阶段 `remoteAddr` 只允许单目标 `host:port`。
|
||||
- `strategy` 固定为 `fifo` 或忽略。
|
||||
|
||||
创建流程:
|
||||
|
||||
1. 校验权限、隧道状态、端口占用和 nftables 能力边界。
|
||||
2. 在数据库创建 `forward` 和 `forward_port`。
|
||||
3. 通过 nftables runtime 对关联入口节点执行同步。
|
||||
4. 若同步失败,回滚数据库创建,返回 SSH/nftables 错误。
|
||||
|
||||
更新流程:
|
||||
|
||||
1. 保存旧 forward 和端口绑定。
|
||||
2. 更新数据库。
|
||||
3. 同步 nftables 规则。
|
||||
4. 若同步失败,回滚数据库状态并尝试恢复旧规则。
|
||||
|
||||
删除流程:
|
||||
|
||||
1. 先删除远端 nftables 规则。
|
||||
2. 成功后删除数据库。
|
||||
3. 如果远端删除失败,普通删除返回错误;强制删除可删除数据库并保留 binding 错误记录,提示用户稍后清理。
|
||||
|
||||
## 后端组件
|
||||
|
||||
新增 package:
|
||||
|
||||
```text
|
||||
go-backend/internal/runtime/nftables/
|
||||
```
|
||||
|
||||
建议拆分:
|
||||
|
||||
| 组件 | 职责 |
|
||||
|------|------|
|
||||
| `Manager` | 对 handler 暴露 Apply/Delete/Reconcile/Test 方法 |
|
||||
| `Planner` | 从数据库记录生成节点级期望规则 |
|
||||
| `Renderer` | 把期望规则渲染为 nftables 脚本 |
|
||||
| `SSHRunner` | 负责 SSH 连接、sudo 包装、命令执行和超时 |
|
||||
| `Parser` | 解析目标地址、协议和错误信息 |
|
||||
|
||||
handler 不直接执行 SSH,也不拼 nft 脚本;handler 只做业务校验并调用 runtime manager。
|
||||
|
||||
## nftables 规则设计
|
||||
|
||||
FLVX 只维护自己的 table,避免触碰用户已有规则:
|
||||
|
||||
```nft
|
||||
table inet flvx {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
}
|
||||
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy accept;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
每条 forward 生成 TCP 和 UDP 规则:
|
||||
|
||||
```nft
|
||||
tcp dport 12345 dnat to 198.51.100.20:443 comment "flvx forward:42 tcp"
|
||||
udp dport 12345 dnat to 198.51.100.20:443 comment "flvx forward:42 udp"
|
||||
```
|
||||
|
||||
第一阶段默认生成 masquerade:
|
||||
|
||||
```nft
|
||||
masquerade comment "flvx masquerade"
|
||||
```
|
||||
|
||||
原因是大多数纯 DNAT 场景需要回程可达;如果不做 SNAT,目标服务回包可能绕过转发节点导致连接失败。后续可增加高级开关允许用户关闭 masquerade。
|
||||
|
||||
### 原子同步策略
|
||||
|
||||
推荐节点级 reconcile,而不是逐条追加:
|
||||
|
||||
1. 从数据库查询该节点所有 nftables forward。
|
||||
2. 生成完整 `table inet flvx` 脚本。
|
||||
3. 通过 SSH 执行 `nft -f <tempfile>`。
|
||||
4. 成功后更新所有相关 `nft_rule_binding` 状态和 hash。
|
||||
|
||||
这样可以避免局部更新导致规则漂移,也能让“重建规则”与创建/更新走同一条路径。
|
||||
|
||||
## SSH 执行策略
|
||||
|
||||
基础要求:
|
||||
|
||||
- 默认超时 10-15 秒。
|
||||
- 支持密码和私钥认证。
|
||||
- 支持 `sudo nft ...`。
|
||||
- 执行前检查 `command -v nft`。
|
||||
- 执行前检查 `nft --version`,错误时提示安装 nftables。
|
||||
- 所有临时脚本写入 `/tmp/flvx-nft-<nonce>.nft`,执行后删除。
|
||||
|
||||
建议命令流程:
|
||||
|
||||
```sh
|
||||
cat > /tmp/flvx-nft-xxxx.nft <<'EOF'
|
||||
table inet flvx {
|
||||
...
|
||||
}
|
||||
EOF
|
||||
nft list table inet flvx >/dev/null 2>&1 && nft delete table inet flvx || true
|
||||
nft -f /tmp/flvx-nft-xxxx.nft
|
||||
rm -f /tmp/flvx-nft-xxxx.nft
|
||||
```
|
||||
|
||||
如果目标 nft 版本支持 `destroy table`,也可以把删除动作放进脚本:
|
||||
|
||||
```nft
|
||||
destroy table inet flvx
|
||||
table inet flvx {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
实现时应按目标 nft 版本兼容性选择 `destroy` 或 shell 中先检测 `nft list table inet flvx`。
|
||||
|
||||
## 前端体验
|
||||
|
||||
### 节点页
|
||||
|
||||
节点表单新增“转发模式”:
|
||||
|
||||
- `Agent 节点`:默认,现有表单不变。
|
||||
- `nftables 节点`:显示 SSH 配置区块,隐藏 agent 安装相关提示。
|
||||
|
||||
nftables 节点列表操作:
|
||||
|
||||
- 测试 SSH
|
||||
- 重建规则
|
||||
- 清理 FLVX nftables 规则
|
||||
|
||||
隐藏或禁用:
|
||||
|
||||
- 安装命令
|
||||
- 升级
|
||||
- 回退
|
||||
- agent 协议开关
|
||||
- 实时 agent 指标入口
|
||||
|
||||
### 隧道页
|
||||
|
||||
隧道类型文案建议改为更明确的运行时说明:
|
||||
|
||||
- `Agent 端口转发`
|
||||
- `Agent 隧道转发`
|
||||
- `nftables 纯转发`
|
||||
|
||||
如果保持现有 `端口转发 / 隧道转发` 选择器,则在选择 nftables 入口节点后禁用隧道转发,并提示“不支持出口节点和转发链”。
|
||||
|
||||
### 转发页
|
||||
|
||||
选择 nftables 隧道后:
|
||||
|
||||
- 隐藏限速、每 IP 限速、最大连接数、Proxy Protocol。
|
||||
- 目标地址输入提示“第一阶段仅支持单目标 host:port”。
|
||||
- 创建/更新失败时显示远端 SSH 或 nftables 错误。
|
||||
|
||||
## 错误处理
|
||||
|
||||
- SSH 连接失败:返回“SSH 连接失败”,保留底层错误摘要。
|
||||
- 认证失败:返回“SSH 认证失败,请检查用户名和凭据”。
|
||||
- `nft` 不存在:返回“节点未安装 nftables”。
|
||||
- nft 脚本失败:返回 nft stderr 摘要,并记录到 `nft_rule_binding.last_error`。
|
||||
- 下发超时:标记 binding 为 `error`,允许用户重试“重建规则”。
|
||||
- 数据库成功但远端失败时,创建/更新路径应回滚数据库;批量重建路径不回滚业务规则,只记录错误。
|
||||
|
||||
## 安全边界
|
||||
|
||||
- SSH 凭据只在创建/更新时接收,列表 API 不返回明文。
|
||||
- 私钥和密码在数据库中加密保存。
|
||||
- 后端日志不得打印完整私钥、密码或 passphrase。
|
||||
- nft 脚本只由后端 renderer 生成,禁止直接拼接用户提交的自由文本。
|
||||
- `remoteAddr` 必须严格解析为 host/IP + port,端口必须为 1-65535。
|
||||
- `inPort` 仍复用现有端口占用校验。
|
||||
- comment 中只放 forward ID 和协议,不放用户输入。
|
||||
|
||||
## 与现有功能的关系
|
||||
|
||||
- `node/install` 对 nftables 节点返回错误或前端隐藏入口。
|
||||
- `node/check-status` 对 nftables 节点可返回 SSH 测试状态,而不是 agent 在线状态。
|
||||
- `forward/batch-redeploy` 对 nftables 规则执行节点级 reconcile。
|
||||
- `tunnel/batch-redeploy` 遇到 nftables 隧道时只重建相关 nftables 节点规则,不发送 GOST chain/service 命令。
|
||||
- federation 导入/共享第一阶段不支持 nftables 节点。
|
||||
- backup/import 应包含新增 node mode、SSH 配置和 binding 状态;导出时默认不导出 SSH 明文凭据。
|
||||
|
||||
## 测试计划
|
||||
|
||||
后端单元测试:
|
||||
|
||||
- nftables 节点不能创建隧道转发。
|
||||
- nftables 隧道不能包含出口节点或转发链。
|
||||
- agent 和 nftables 节点不能混在同一隧道。
|
||||
- nftables forward 拒绝限速、连接限制和 Proxy Protocol。
|
||||
- nftables forward 拒绝多目标 remoteAddr。
|
||||
- renderer 为 TCP/UDP 生成稳定脚本和 comment。
|
||||
- SSH runner 正确隐藏敏感信息并返回 stderr 摘要。
|
||||
|
||||
后端集成测试:
|
||||
|
||||
- 创建 nftables forward 时数据库和 binding 同步成功。
|
||||
- runtime 下发失败时创建回滚。
|
||||
- 更新失败时数据库和旧规则尽量恢复。
|
||||
- 删除失败时普通删除返回错误,强制删除保留清理提示。
|
||||
|
||||
前端验证:
|
||||
|
||||
- 节点表单按转发模式切换字段。
|
||||
- nftables 节点隐藏安装/升级/回退操作。
|
||||
- 隧道表单阻止 nftables 隧道转发配置。
|
||||
- 转发表单选择 nftables 隧道后隐藏不支持字段。
|
||||
|
||||
验证命令:
|
||||
|
||||
```bash
|
||||
(cd go-backend && go test ./...)
|
||||
(cd vite-frontend && pnpm run build)
|
||||
```
|
||||
|
||||
## 实施顺序
|
||||
|
||||
1. 数据模型和 repository:新增字段、SSH 配置表、binding 表和查询方法。
|
||||
2. nftables runtime:实现 planner、renderer、SSH runner、manager。
|
||||
3. handler 校验:节点、隧道、转发 create/update/delete 接入 runtime。
|
||||
4. 前端节点表单:增加转发模式和 SSH 配置。
|
||||
5. 前端隧道/转发表单:按 nftables 能力收窄 UI。
|
||||
6. 批量重建和清理操作:提供运维入口。
|
||||
7. 测试与文案打磨。
|
||||
|
||||
## 第一阶段固定决策
|
||||
|
||||
本设计先固定以下选择,除非审核时调整:
|
||||
|
||||
- 第一阶段同时下发 TCP 和 UDP。
|
||||
- 第一阶段只支持单目标。
|
||||
- 第一阶段默认启用 masquerade。
|
||||
- nftables 节点的“在线状态”以 SSH 测试为准,而不是常驻连接。
|
||||
Reference in New Issue
Block a user