# VPS 流量转发配置

基于 **Xray + Nginx SNI 分流 + Mihomo TUN 透明代理 + WireGuard**，实现：
内网服务反向代理、多 VPS 链路中转、BT 透明代理、Minecraft UDP 转发。

---

## 设备

| 设备 | IP / 域名 | 角色 |
|---|---|---|
| **北京 VPS** | `salmonstill.cn` / `49.232.242.90` | 公网入口，Nginx + Xray + socat + WireGuard |
| **搬瓦工 VPS** | `67.230.189.235` | 代理出口，Xray Reality 入站（客户端直连） |
| **旁路由** | `192.168.1.199` / WG `10.0.0.2` | 内网核心，Xray bridge + Mihomo 代理 |
| **NAS** | `192.168.1.188` | 绿联云 UGOS，思源笔记等服务 |
| **qBittorrent** | `192.168.1.200` (macvlan) | PT 下载，独立 IP |
| **Windows** | `192.168.1.177` | 内网办公，SSH |
| **Spark** | `192.168.1.166` | Ubuntu 主机，本地 TUN 代理 |
| **iPhone** | WG `10.0.0.3` | WireGuard 客户端，远程访问内网 |
| **Mac** | WG `10.0.0.4` | WireGuard 客户端，远程访问内网 |

---

## Nginx SNI 分流 (`北京vps-stream.conf`)

**端口 443 — 公网入口**，按 SNI 分发流量：

| SNI | 后端 | 端口 | 用途 |
|---|---|---|---|
| `www.apple.com` / `www.samsung.com` / `www.amd.com` / `www.asus.com` | Xray interconn | :9443 | 反向代理隧道（4 SNI leastPing 轮换） |
| `www.bilibili.com` | Xray proxy_from_lan | :9445 | 北京直连代理（qBittorrent 出站） |
| `drive.salmonstill.cn` | NAS | :38653 | 绿联云直接转发 |
| 默认 | NPM | :8443 | Nginx Proxy Manager |

**端口 9447 — 数据中心回落**：Reality 探针/探测流量回落到此，按 SNI 代理到真实网站。

搬瓦工 VPS（部署 `代理vps-stream.conf` + `xray-代理服务器-config.json`）：`www.cloudflare.com` → Xray :9443，默认 → NPM。

---

## Xray 核心

### 北京 VPS (`xray-北京vps-config.json`)

**Reality 入站** — 共享 privateKey `GGT9LfN_...`，由 Nginx SNI 转发：

| Tag | 端口 | serverNames | Reality target | 说明 |
|---|---|---|---|---|
| `interconn` | :9443 | apple/samsung/amd/asus 4 SNI leastPing 轮换 | `127.0.0.1:9447` | 反向隧道，多 SNI 防封 + 数据中心回落 |
| `proxy_from_lan` | :9445 | `www.bilibili.com` | `www.bilibili.com:443` | 北京直连代理（qBittorrent） |

> `mihomo_in`（旧 Mihomo 中转）已删除，客户端直接连接搬瓦工。

**dokodemo-door 入站** — 端口转发到旁路由反向隧道，共 11 个：

| Tag | 公网端口 | 路由目标 |
|---|---|---|
| `external` | 38653 | → portal → NAS :9443 |
| `external_siyuan` | 38654 | → portal → NAS :5005 |
| `external_minecraft` | 39132 | → portal → 旁路由 :39132 |
| `external_wsl` | 38655 | → portal → Windows :22 |
| `external_nas_ssh` | 38656 | → portal → NAS :22 |
| `external_router_ssh` | 38657 | → portal → 旁路由 :22 |
| `external_spark_ssh` | 38659 | → portal → Spark (166) :22 |
| `external_spark_rdp` | 38660 | → portal → Spark (166) :3389 |
| `external_win_rdp` | 38664 | → portal → Windows (177) :3389 |
| `external_spark_38662` | 38662 | → portal → Spark (166) :38662 |
| `external_router_web` | 39766 | → portal → 旁路由 :80 |
| `external_tmp` | 8501 | → portal → Windows :8501 |
| `external_gitea` | 38661 | → portal → NAS :3000 |
| `external_222` | 222 | → portal → NAS :222 |
| `external_qbit` | 51413 | → portal → qBittorrent :51413 |

**其他入站**：

| Tag | 端口 | 类型 | 用途 |
|---|---|---|---|
| `socks-dynamic` | 38658 | SOCKS5 (password) | 动态 LAN 端口访问 |

**出站**：`direct`（freedom 直连）

**路由**：所有 `external_*` + `interconn` + `socks-dynamic` → `portal`（反向隧道）；`interconn` 探测流量（baidu.com）→ `direct`；`proxy_from_lan` → `direct`

> `mihomo_in` + `proxy_in` + `to_tokyo` 旧中转链路（北京→搬瓦工），已删除。

### 旁路由 (`xray-旁路由-config.json`)

**Bridge 入站**：与北京 VPS 的 Portal 建立永久反向隧道。

**出站**（14 个）：

| Tag | 目标 | 用途 |
|---|---|---|
| `to_nas` | `192.168.1.188:9443` | NAS 管理 |
| `to_siyuan` | `192.168.1.188:5005` | 思源笔记 |
| `to_wsl` | `192.168.1.177:22` | WSL SSH |
| `to_nas_ssh` | `192.168.1.188:22` | NAS SSH |
| `to_router_ssh` | `192.168.1.199:22` | 旁路由 SSH |
| `to_router_web` | `192.168.1.199:80` | 旁路由 Web |
| `to_minecraft` | `127.0.0.1:39132` | Minecraft 服务 |
| `to_tmp` | `192.168.1.177:8501` | 临时服务 |
| `to_3000` | `192.168.1.188:3000` | Web 服务 |
| `to_222` | `192.168.1.188:222` | 备用服务 |
| `to_qbit` | `192.168.1.200:51413` | qBittorrent 入站 |
| `to_spark_ssh` | `192.168.1.166:22` | Spark SSH 远程 |
| `to_spark_rdp` | `192.168.1.166:3389` | Spark xRDP 桌面远程 |
| `to_spark_38662` | `192.168.1.166:38662` | Spark 自定义服务 |
| `interconn-apple/samsung/mozilla` | VLESS+Reality → `salmonstill.cn:443` (多 SNI 轮换) | 隧道链接（balancer + leastPing + fallback） |
| `to_beijing_direct` | VLESS+Reality → `salmonstill.cn:443` (SNI=www.bilibili.com) | 北京直连 |
| `direct` | freedom | 直连 |

**路由规则**（bridge 入站按端口匹配）：
- 38653 → `to_nas`（NAS 管理）
- 38654 → `to_siyuan`
- 38655 → `to_wsl`
- 38656 → `to_nas_ssh`
- 38657 → `to_router_ssh`
- 8501 → `to_tmp`
- 39766 → `to_router_web`
- 38661 → `to_3000`
- 222 → `to_222`
- 39132 → `to_minecraft`
- 51413 → `to_qbit`
- 38658 → `direct`（SOCKS5 动态回家，直接连接目标地址）
- 38659 → `to_spark_ssh`（Spark SSH 远程）
- 38660 → `to_spark_rdp`（Spark xRDP 桌面远程）
- 38662 → `to_spark_38662`
- 38664 → `to_win_rdp`（Windows 远程桌面）
- 38665 → `to_spark_3080`（Spark Web 3080）
- 默认（catch-all）→ **`direct`**（不匹配端口规则的动态请求直连目标）
- `socks-lan` 入站 → `to_beijing_direct`

> 所有显式端口规则优先于 catch-all，现有端口转发不受影响。

### 搬瓦工 VPS (`xray-代理服务器-config.json`)

极简：VLESS+Reality 入站 `:9443`（SNI=www.cloudflare.com, dest=www.cloudflare.com），freedom 出站。
所有客户端（Spark、旁路由、macOS）直连此服务器，不再经北京 VPS 中转。

---

## Mihomo

### 旁路由 (`旁路由的mihomo config.yaml`)

**纯代理模式** — 无内核级劫持，提供多端口代理服务供客户端手动指定：

| 端口 | 类型 | 路由 | 用途 |
|---|---|---|---|
| `7890` | mixed | 按规则分流 | 本地服务默认代理 |
| `7891` | mixed | 全部走 US-Direct | 全局国外代理 |
| `7892` | mixed | 全部直连 | 全局国内直连 |

配合 **fake-ip DNS**（`enhanced-mode: fake-ip`），DNS 请求返回 `198.18.x.x` 假 IP，强制流量进入代理路由。

**代理节点**：
- `US-Direct` — VLESS+Reality → `67.230.189.235:443`（默认国外出口，客户端直连搬瓦工，SNI=www.cloudflare.com）

> `Xray-Real` 代理节点（经北京 VPS 中转）已移除，Mihomo 全部直连搬瓦工。

**关键策略组**：
- 国外 → `[US-Direct, 直连]`

**防死循环 IP**：`49.232.242.90`（北京 VPS）、`67.230.189.235`（搬瓦工 VPS）强制直连。

**额外监听器**：`:7891`（全局→US-Direct）、`:7892`（强制直连）

### macOS (`非tun模式的主机mihomo config.yaml`)

结构与旁路由一致，无 TUN，监听器 `:7890` + `:7891` + `:7892`。

**重启**（launchctl 管理）：
```bash
launchctl unload ~/Library/LaunchAgents/com.mihomo.proxy.plist
launchctl load ~/Library/LaunchAgents/com.mihomo.proxy.plist
```

### Ubuntu / Spark (`tun模式的主机mihomo config.yaml`)

本地 TUN 模式（`stack: system`），不依赖旁路由网关。设备重启后 `auto-route` 自建路由表，稳定可靠。监听器 `:7890` + `:7891` + `:7892`，节点直连 US-Direct。



## qBittorrent 透明代理

### 架构总览

```
                    出站（SOCKS5 代理）
qBittorrent(192.168.1.200)                                 互联网
    ↓ SOCKS5 旁路由:1080 (socks-lan)                         ↑
    ↓ Xray routing → to_beijing_direct                       │
    ↓ Reality(VLESS, SNI=www.bilibili.com)                     │
    ↓ 北京VPS:443 → Nginx分流                                │
    ↓ proxy_from_lan:9445 → direct(freedom) ─────────────────┘

                    入站（端口转发）
Peer → 北京VPS:51413 → external_qbit → portal
    → bridge隧道 → 旁路由 → to_qbit → 192.168.1.200:51413
```

---

### 设备清单

| 设备 | IP | 角色 |
|---|---|---|
| 北京VPS | `salmonstill.cn` / `49.232.242.90` | 公网出口 + 入站入口 |
| 旁路由 | `192.168.1.199` | Xray 桥接 + SOCKS5 代理 |
| NAS | `192.168.1.188` | Docker 宿主机 |
| qBittorrent 容器 | `192.168.1.200` | macvlan 独立 IP，PT 专用 |

---

### 第一部分：北京 VPS 配置

#### 1.1 Nginx Stream SNI 分流 `beijing-vps-stream.conf`

新增 `www.bilibili.com` SNI 映射（用于旁路由 Mihomo 直连代理）：

```nginx
stream {
    map $ssl_preread_server_name $backend {
        www.apple.com xray;            # 旁路由反向代理隧道
        www.microsoft.com mihomo;      # 外部客户端代理 → 东京出口
        www.bilibili.com xray_lan;       # 旁路由 Mihomo 北京直连代理
        drive.salmonstill.cn nas;      # 绿联云服务
        default npm;                   # Nginx Proxy Manager
    }
    upstream xray      { server 127.0.0.1:9443; }
    upstream mihomo    { server 127.0.0.1:9444; }
    upstream xray_lan  { server 127.0.0.1:9445; }   # 新增
    upstream nas       { server 127.0.0.1:38653; }
    upstream npm       { server 127.0.0.1:8443; }

    server {
        listen 443 reuseport;
        listen [::]:443 reuseport;
        ssl_preread on;
        proxy_pass $backend;
    }
}
```

#### 1.2 Xray 配置 `xray-北京vps-config.json`

##### 新增入站 `proxy_from_lan`（北京直连出口）

```json
{
  "tag": "proxy_from_lan",
  "listen": "127.0.0.1",
  "port": 9445,
  "protocol": "vless",
  "settings": {
    "clients": [
      {
        "id": "113e167a-a2be-4b46-9010-60020108626c",
        "flow": "xtls-rprx-vision"
      }
    ],
    "decryption": "none"
  },
  "streamSettings": {
    "network": "raw",
    "security": "reality",
    "realitySettings": {
      "show": false,
      "target": "www.apple.com:443",
      "serverNames": ["www.bilibili.com"],
      "privateKey": "GGT9LfN_2JdQG68cwrULgUK-adfT6wIokLzWjaB0fXs",
      "shortIds": ["7c947a71b94f369e"]
    }
  }
}
```

> Reality 公私钥复用已有的 `interconn` 入站 keypair，`serverNames` 用新的 `www.bilibili.com` 与 Nginx 对应。
> `target` 设为 `www.apple.com:443`，Reality 从此地址偷取真实 TLS 证书用于伪装。

##### 新增入站 `external_qbit`（BT 入站端口）

```json
{
  "tag": "external_qbit",
  "listen": "0.0.0.0",
  "port": 51413,
  "protocol": "dokodemo-door",
  "settings": {
    "address": "127.0.0.1",
    "port": 51413,
    "network": "tcp"
  }
}
```

> 仅 TCP——Xray portal 反向代理对 UDP 支持不完善，BT 的 μTP(UDP) 走不了，需要在 qBittorrent 里关闭。

##### 新增路由规则

```json
{ "type": "field", "inboundTag": ["proxy_from_lan"], "outboundTag": "direct" },
{ "type": "field", "inboundTag": ["external_qbit"],  "outboundTag": "portal" }
```

#### 1.3 腾讯云防火墙

新开端口：

| 端口 | 协议 | 用途 |
|---|---|---|
| 51413 | TCP | BT 入站 |

---

### 第二部分：旁路由 Xray SOCKS5 出站代理

#### 2.1 配置 `xray-旁路由-config.json`

qBittorrent 出站不走 Mihomo 透明代理，而是通过旁路由上 Xray 的 `socks-lan` 入站（`:1080`），直接转发到北京 VPS 直连出口。

##### SOCKS5 入站

```json
{
  "tag": "socks-lan",
  "port": 1080,
  "listen": "0.0.0.0",
  "protocol": "socks",
  "settings": {
    "auth": "noauth",
    "udp": true
  }
}
```

##### 出站 `to_beijing_direct`

```json
{
  "tag": "to_beijing_direct",
  "protocol": "vless",
  "settings": {
    "vnext": [
      {
        "address": "salmonstill.cn",
        "port": 443,
        "users": [
          {
            "id": "113e167a-a2be-4b46-9010-60020108626c",
            "flow": "xtls-rprx-vision",
            "encryption": "none"
          }
        ]
      }
    ]
  },
  "streamSettings": {
    "network": "raw",
    "security": "reality",
    "realitySettings": {
      "fingerprint": "chrome",
      "serverName": "www.bilibili.com",
      "publicKey": "62y5gDjPrdeuePGl-D2IW4Cw9Kb8_bSBBTmArvL7Nhs",
      "shortId": "7c947a71b94f369e"
    }
  }
}
```

##### 路由规则

```json
{ "type": "field", "inboundTag": ["socks-lan"], "outboundTag": "to_beijing_direct" }
```

> 路径：`qBittorrent → SOCKS5 旁路由:1080 → to_beijing_direct → 北京VPS:443(SNI=www.bilibili.com) → proxy_from_lan:9445 → direct → 互联网`

---

### 第三部分：旁路由 Xray 51413 转发

#### 3.1 配置 `xray-旁路由-config.json`

##### 新增加出站 `to_qbit`

```json
{
  "tag": "to_qbit",
  "protocol": "freedom",
  "settings": {
    "redirect": "192.168.1.200:51413"
  }
}
```

##### 新增路由规则

```json
{
  "type": "field",
  "inboundTag": ["bridge"],
  "port": "51413",
  "outboundTag": "to_qbit"
}
```

> 放在 bridge 下——从北京 VPS 的 portal 通过反向隧道过来的 BT 入站流量，由 bridge 接收后按端口 51413 匹配到此规则，转发到 qBittorrent 容器。

---

### 第四部分：NAS qBittorrent Docker

#### 4.1 创建 macvlan 网络

```bash
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.199 \
  --ip-range=192.168.1.200/32 \
  -o parent=eth0 \
  qbit_macvlan
```

> `--gateway=192.168.1.199`：qBittorrent 的默认网关设为旁路由，确保出站流量经过旁路由。
> `--ip-range=192.168.1.200/32`：固定 IP。

#### 4.2 启动容器

```bash
docker run -d \
  --name qbittorrent \
  --network qbit_macvlan \
  --ip 192.168.1.200 \
  -e WEBUI_PORT=8090 \
  -p 8090:8090 \
  -v /path/to/downloads:/downloads \
  lscr.io/linuxserver/qbittorrent:latest
```

#### 4.3 qBittorrent 设置

| 设置项 | 值 |
|---|---|
| 监听端口 | `51413` |
| UPnP/NAT-PMP | **禁用** |
| SOCKS5 代理 | `192.168.1.199` / 端口 `1080` |
| 连接协议 | **仅 TCP**（关闭 μTP） |
| DHT | 可选（建议开） |
| PEX | 可选（建议开） |

> 关闭 μTP(UDP)：Xray portal 反向代理不支持 UDP，BT 的 μTP 走 UDP 会导致入站失败。

#### 4.4 定时做种调度（crontab）

让 qBittorrent 只在夜间（01:00-07:00）做种，白天暂停以节省带宽：

```bash
crontab -e
```

添加以下两行（在 NAS 或任意可访问 192.168.1.200 的设备上）：

```
0 1 * * * curl -s -X POST "http://192.168.1.200:8888/api/v2/torrents/start" --data "hashes=all"
0 7 * * * curl -s -X POST "http://192.168.1.200:8888/api/v2/torrents/stop"  --data "hashes=all"
```

| 时间 | 操作 | 含义 |
|---|---|---|
| 凌晨 01:00 | `/torrents/start` `hashes=all` | 启动全部种子开始做种 |
| 早上 07:00 | `/torrents/stop` `hashes=all` | 停止全部种子 |

---

### 第五部分：部署顺序

```
1. 腾讯云防火墙 → 开放 51413/tcp
2. scp beijing-vps-stream.conf     → 北京VPS /etc/nginx/stream.conf.d/
3. scp xray-北京vps-config.json     → 北京VPS /usr/local/etc/xray/config.json
4. 北京VPS: nginx -t && systemctl reload nginx
5. 北京VPS: systemctl restart xray
6. scp 旁路由的mihomo config.yaml   → 旁路由 /opt/mihomo/config.yaml
7. scp xray-旁路由-config.json      → 旁路由 /etc/xray/config.json
8. 旁路由: /etc/init.d/mihomo restart
9. 旁路由: /etc/init.d/xray restart
10. NAS: 创建 macvlan 网络 + 启动 qBittorrent 容器
11. NAS: 配置 qBittorrent 监听端口 51413，关闭 μTP
```

---

### 第六部分：验证

#### 6.1 SOCKS5 代理出站验证

```bash
# 在 NAS 上执行，应返回北京 VPS 的公网 IP
docker exec qbittorrent curl --socks5 192.168.1.199:1080 https://ip.sb
```

#### 6.2 Xray 日志确认

```bash
# 旁路由上查看 Xray 日志，确认 socks-lan 流量转发正常
tail -f /var/log/xray.log | grep socks-lan
```

#### 6.3 BT 入站验证

```bash
# 北京 VPS 上确认端口监听
ss -tlnp | grep 51413

# 从外部测试端口可达
nc -zv salmonstill.cn 51413
```

#### 6.4 端到端 BT 测试

下载一个热门 Ubuntu torrent 种子，观察：
- qBittorrent WebUI → 连接 → 应显示 DHT 节点数增长
- 跟踪器页面 → 应显示 "Working"
- 下载速度应有上传来确认入站工作

---

### 第七部分：故障排查

| 现象 | 排查 |
|---|---|
| qBittorrent curl ip.sb 返回真实 IP | SOCKS5 代理未生效，检查 qBittorrent 设置中代理配置 |
| 代理连不上 | 确认 旁路由 Xray 正在运行且 `socks-lan` 入站监听 :1080 |
| 入站无上传 | 检查北京 VPS ufw/腾讯云安全组已放行 51413/tcp |
| xray 报错 `reverse-proxy.xray.internal` | portal/bridge 域名不匹配，两边必须一致 |
| 下载有速度、无上传 | μTP 没关或 portal UDP 不支持，qBittorrent 设置仅 TCP |

---

### 第八部分：文件清单

| 文件 | 位置 | 作用 |
|---|---|---|
| `beijing-vps-stream.conf` | 北京VPS `/etc/nginx/stream.conf.d/` | Nginx SNI分流（含 www.bilibili.com → 9445） |
| `xray-北京vps-config.json` | 北京VPS `/usr/local/etc/xray/config.json` | Xray 入站+路由（含 proxy_from_lan + external_qbit） |
| `xray-旁路由-config.json` | 旁路由 `/etc/xray/config.json` | Xray bridge + to_qbit(51413) + socks-lan(1080) → to_beijing_direct |

## WireGuard 内网穿透（iPhone / Mac 远程访问内网）

复用已有的 WireGuard 隧道，让外部 iPhone / Mac 通过北京 VPS 中继，访问家里整个 `192.168.1.0/24` 内网的**任意端口**（无需逐个配置端口规则）。

### 架构

```
iPhone (WireGuard App, 10.0.0.3)
Mac   (WireGuard, 10.0.0.4)
    ↓ WG 隧道 (UDP 51820)
北京VPS 49.232.242.90 (wg0, 10.0.0.1/24)
    ↓ 转发 (iptables FORWARD ACCEPT)
旁路由 192.168.1.199 (10.0.0.2, penetration zone)
    ↓ 转发 + SNAT (→ 192.168.1.199)
整个 192.168.1.0/24 任意端口可达 (如 Spark 192.168.1.166:8081 Expo dev server)
```

### 虚拟网段 IP 分配

| 设备 | WG IP | 说明 |
|---|---|---|
| 北京 VPS | `10.0.0.1/24` | WG 服务端，监听 `:51820` |
| 旁路由 | `10.0.0.2/24` | 子网代理节点，进 LAN |
| iPhone | `10.0.0.3/32` | WireGuard App |
| Mac | `10.0.0.4/32` | WireGuard |

### 北京 VPS 配置 (`/etc/wireguard/wg0.conf`)

```ini
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <北京VPS私钥>
MTU = 1420

PostUp = iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT

[Peer]
# 旁路由（子网代理，带 192.168.1.0/24）
PublicKey = <旁路由公钥>
AllowedIPs = 10.0.0.2/32, 192.168.1.0/24
PersistentKeepalive = 25

[Peer]
# iPhone
PublicKey = <iPhone公钥>
AllowedIPs = 10.0.0.3/32
PersistentKeepalive = 25

[Peer]
# Mac
PublicKey = <Mac公钥>
AllowedIPs = 10.0.0.4/32
PersistentKeepalive = 25
```

> ⚠️ **关键坑：iptables FORWARD policy DROP**。VPS 装过 Docker 后，`iptables` 的 FORWARD 链默认策略会变成 `DROP`，导致客户端流量穿透 VPS 转发时被丢弃（症状：`ping 10.0.0.1` 通、`ping 10.0.0.2` 不通）。必须在 PostUp 加 `-i wg0` / `-o wg0` 两条 ACCEPT。
> 查看规则用 `iptables -L FORWARD -n -v`（**必须带 `-v`**，否则不显示 In/Out 接口列，`grep wg0` 会以为规则不存在）。

### 旁路由配置（ImmortalWrt LuCI）

#### 1. 新建 `penetration` zone

网络 → 防火墙 → General Settings → 添加：

| 字段 | 值 |
|---|---|
| Name | `penetration` |
| Input | `accept` |
| Output | `accept` |
| Forward | `accept` |
| Covered networks | `WireGuard` |
| Allow forward to destination zones | `lan` |

同时把 WG 接口从 `wan` zone 的 **Covered networks 取消勾选**（一个接口只能属于一个 zone）。

#### 2. 新增通用 SNAT 规则（关键）

网络 → 防火墙 → NAT 规则 → 添加：

| 字段 | 值 |
|---|---|
| Name | `penetration-snat` |
| 地址族限制 | 仅 IPv4 |
| 协议 | Any |
| 出站区域 | `lan` |
| 源地址 | `10.0.0.0/24` |
| 目标地址 | any |
| 操作 | `SNAT` → 重写 IP `192.168.1.199` |

生成规则：`ip saddr 10.0.0.0/24 ... snat ip to 192.168.1.199`

> ⚠️ **关键坑：penetration zone 勾 masq 无效**。ImmortalWrt (fw4) 的 masquerade 按"出接口"触发，隧道流量转发进 LAN 时出接口是 `br-lan`（属于 lan zone），不会进 `srcnat_penetration`。必须显式加 SNAT 规则，否则 166 等设备的回包发给主路由(192.168.1.1)而丢失（症状：`ping 10.0.0.2` 通、`ping 192.168.1.166` 不通）。

#### 3. Minecraft 端口转发联动

因为 WG 接口从 `wan` 移到了 `penetration`，Minecraft 那条 `Minecraft-udp` 端口转发的 **Source zone 要从 `wan` 改成 `penetration`**（玩家流量实际也走 WG 隧道进来），否则 Minecraft 会断。

### 客户端配置

#### iPhone（海外区 App Store 官方 WireGuard）

```
[Interface]
PrivateKey = <iPhone私钥>
Address = 10.0.0.3/32
DNS = 223.5.5.5

[Peer]
PublicKey = <北京VPS服务端公钥>
Endpoint = 49.232.242.90:51820
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25
```

#### Mac

同上，`Address = 10.0.0.4/32`，PrivateKey 换成 Mac 的。

> AllowedIPs 只含虚拟网段和内网网段 → split tunnel，仅内网流量走隧道，不影响正常上网。

### 验证命令

```bash
# 北京 VPS 上：确认 3 个 peer 都有握手
wg show

# 客户端设备上：三层诊断（定位故障层级）
ping -c 3 10.0.0.1        # ① 隧道到 VPS —— 通说明客户端配置对
ping -c 3 10.0.0.2        # ② 隧道到旁路由 —— 通说明 VPS 转发对
ping -c 3 192.168.1.166   # ③ 进内网 —— 通说明旁路由转发+SNAT 对

# 旁路由上确认 SNAT 规则
nft list ruleset | grep -iE "penetration-snat|10.0.0.0/24"
```

### 排障对照表

| 现象 | 原因 | 修复 |
|---|---|---|
| `ping 10.0.0.1` 通、`ping 10.0.0.2` 不通 | VPS iptables FORWARD policy DROP | wg0.conf PostUp 加 `-i wg0` / `-o wg0` ACCEPT |
| `ping 10.0.0.2` 通、`ping 192.168.1.166` 不通 | 旁路由缺通用 SNAT | 新增 `penetration-snat`（SNAT → 192.168.1.199） |
| 旁路由自己 ping 166 通，但客户端不通 | 同上 | 同上 |
| `iptables -L FORWARD -n \| grep wg0` 为空 | 没带 `-v`，接口列不显示 | 用 `iptables -L FORWARD -n -v` |

## Minecraft UDP 转发

### 架构

```
公网玩家 (UDP 19132)
    ↓
北京VPS (salmonstill.cn)
socat 监听 19132 → 转发到 10.0.0.2:19132
    ↓ WireGuard 隧道
旁路由 ImmortalWrt (192.168.1.199 / 10.0.0.2)
nftables 端口转发 + SNAT
    ↓
NAS (192.168.1.188:19132)
    ↓
Minecraft 基岩版 Docker 容器
```

---

### 设备信息

| 设备 | IP | 系统 |
|---|---|---|
| 北京VPS | `salmonstill.cn` / `49.232.242.90` | Ubuntu 22.04 |
| 旁路由 | `192.168.1.199` / WG隧道: `10.0.0.2` | ImmortalWrt 24.10 (GL-MT2500) |
| NAS | `192.168.1.188` | 绿联云 UGOS |

---

### 第一部分：北京VPS 配置

#### WireGuard 配置 `/etc/wireguard/wg0.conf`

```ini
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <北京VPS私钥>
MTU = 1420

# 回包源地址转换（必须，否则公网玩家收不到回包）
PostUp = iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

[Peer]
# 旁路由
PublicKey = 9jPlaUhx2Dc+C5ZqJx6Iu8GtNMig3cFIoqfHg8PZbCA=
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25
```

> ⚠️ 不使用 iptables DNAT 转发，改用 socat 处理 UDP 转发，避免 conntrack 连接跟踪问题导致回包丢失。

#### 开启内核转发

```bash
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p
```

#### 开放防火墙端口

```bash
ufw allow 51820/udp   # WireGuard
ufw allow 19132/udp   # Minecraft 基岩版
```

#### 启动 WireGuard

```bash
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
```

#### socat UDP 转发

socat 监听公网 19132 端口，收到包后转发给旁路由隧道 IP，并维护连接状态确保回包正确返回。

创建 systemd service：

```bash
nano /etc/systemd/system/mc-forward.service
```

```ini
[Unit]
Description=Minecraft UDP Forward
After=network.target

[Service]
ExecStart=/usr/bin/socat UDP4-LISTEN:19132,fork,reuseaddr UDP4:10.0.0.2:19132
Restart=always

[Install]
WantedBy=multi-user.target
```

启动并设置开机自启：

```bash
systemctl daemon-reload
systemctl enable mc-forward
systemctl start mc-forward
```

---

### 第二部分：旁路由 ImmortalWrt 配置

#### WireGuard 接口配置

路径：**网络 → 接口 → 添加新接口**

**常规设置：**

| 字段 | 值 |
|---|---|
| 接口名称 | `WireGuard` |
| 协议 | `WireGuard VPN` |
| 私钥 | `<旁路由私钥>` |
| IP 地址 | `10.0.0.2/24` |
| 监听端口 | 不填 |

**防火墙设置：** 加入 `wan` 区域

**Peers → 添加对端：**

| 字段 | 值 |
|---|---|
| 公钥 | `n159R7bNB+tW3Br0cok2zA27Pzg2WSPTI9uQ9odOFyU=` |
| 端点主机 | `salmonstill.cn` |
| 端点端口 | `51820` |
| 允许的 IP | `0.0.0.0/0` |
| 路由允许的 IP | ✅ 勾选 |
| 持续 Keep-Alive | `25` |

> ⚠️ 允许的 IP 必须设为 `0.0.0.0/0`，否则 WireGuard 会丢弃来自公网玩家 IP 的包。

#### 端口转发配置

路径：**网络 → 防火墙 → 端口转发 → 添加**

| 字段 | 值 |
|---|---|
| 名称 | `Minecraft-udp` |
| 协议 | `UDP` |
| 源区域 | `wan` |
| 外部端口 | `19132` |
| 目标区域 | `lan` |
| 内部 IP 地址 | `192.168.1.188` |
| 内部端口 | `19132` |

#### SNAT 配置

路径：**网络 → 防火墙 → NAT 规则 → 添加**

| 字段 | 值 |
|---|---|
| 名称 | `minecraft-snat` |
| 地址族限制 | `仅 IPv4` |
| 协议 | `UDP` |
| 出站区域 | `lan` |
| 目标地址 | `192.168.1.188` |
| 目标端口 | `19132` |
| 操作 | `SNAT - 重写为特定的源 IP 或端口` |
| 重写 IP 地址 | `192.168.1.199` |

> SNAT 的作用：将转发给 NAS 的包源 IP 改为旁路由 IP，确保 NAS 的回包发回给旁路由而不是直接走主路由，避免回包路径不对称。

---

### 第三部分：NAS Docker 配置

使用 `network_mode: host` 避免 Docker NAT 导致的 IP 映射问题。

```yaml
services:
  bedrock:
    image: itzg/minecraft-bedrock-server:2026.2.1
    container_name: mc-bedrock
    network_mode: host
    stdin_open: true
    tty: true
    environment:
      EULA: "TRUE"
      VERSION: "1.26.14.1"
      TZ: "Asia/Shanghai"
      OPS: "2535472561115036"
    volumes:
      - /volume2/ProgramsV2/minecraft:/data
    restart: unless-stopped
```

---

### 第四部分：验证

#### 检查 WireGuard 隧道

```bash
# 旁路由
wg show
# 正常应有 latest handshake 和双向 transfer
```

#### 检查 socat 运行状态

```bash
systemctl status mc-forward
```

#### 抓包验证完整链路

```bash
# VPS 上抓 wg0，确认双向流量
tcpdump -i wg0 udp port 19132 -n

# 旁路由抓 br-lan，确认转发到 NAS
tcpdump -i br-lan udp port 19132 -n

# NAS 上抓包，确认收到并回包
sudo tcpdump -i bridge0 udp port 19132 -n
```

---

### 故障排查

| 现象 | 排查方法 |
|---|---|
| WireGuard 无握手 | 检查 VPS 防火墙 51820/udp 是否开放 |
| socat 收不到包 | 检查 ufw 19132/udp 是否开放 |
| 旁路由收不到包 | 检查 WireGuard AllowedIPs 是否为 `0.0.0.0/0` |
| NAS 收不到包 | 检查端口转发内部端口是否填写正确 |
| NAS 有回包但玩家连不上 | 检查 SNAT 规则是否生效，确认 NAS 用 host 网络模式 |
| 游戏内延迟不显示 | 检查 socat 是否正常运行，DNAT 规则是否已删除 |

---

### 扩展：新增其他 UDP 服务

1. VPS 新建一个 socat service，修改端口号
2. 旁路由 LuCI 端口转发新增一条规则
3. `ufw allow <新端口>/udp`
---

## SOCKS5 动态 LAN 端口访问

通过新增的 `socks-dynamic` 入站（`:38658`，密码认证），可从外部访问任意内网 IP:PORT，无需事先配置端口规则。

```
浏览器 SOCKS5 → salmonstill.cn:38658
  → 北京VPS socks-dynamic → portal → 反向隧道
  → 旁路由 bridge → catch-all direct → 任意 LAN IP:PORT
```

---

## Reality 密钥

| 位置 | Private Key | Public Key (客户端用) |
|---|---|---|
| **北京 VPS**（3 个入站共用） | `GGT9LfN_2JdQG68cwrULgUK-adfT6wIokLzWjaB0fXs` | `62y5gDjPrdeuePGl-D2IW4Cw9Kb8_bSBBTmArvL7Nhs` |
| **搬瓦工 VPS**（客户端直连） | `iBlu3eH1VLf1S5Qw87m_1w0TGYUktDwHAzgpQ2aKuGI` | `jr_zQjC4mvlQITuG5Ap5Mxqe5EBbGyyvwbVLDEi8OCA` |

`Beijing-Direct` 节点必须 `skip-cert-verify: true`，因 Reality 返回 target（`www.bilibili.com`）的证书，而 SNI 是 `www.bilibili.com`，TLS SAN 校验可能失败。

### Reality 故障排查

| 现象 | 根因 | 解决 |
|---|---|---|
| Mihomo 日志 `connect error: EOF`，Xray 日志 `handshake did not complete successfully` | 运营商检测并干扰特定 SNI（如 `www.microsoft.com`）。某次 VPS 迁移后开始出现，同配置在旧 VPS 正常 | 更换 SNI/serverNames/dest 为其他高信誉域名，如 `www.cloudflare.com`。仅 SNI 变换即可，无需更换密钥 |
| Xray 日志 `target sent incorrect server hello or handshake incomplete` | Reality dest 目标不适合（如 `www.cloudflare.com` 不支持直连 TLS 1.3 或返回非标准 Server Hello） | 换用标准 Web 站点（如 `www.microsoft.com` 或 `www.youtube.com` 作为 dest，`www.cloudflare.com` 作为 SNI） |

**Reality 参数一致性清单**（出现握手失败时逐项核对）：
- Xray `privateKey` ←→ Mihomo `public-key`（`xray x25519 -i <私钥>` 验证）
- Xray `shortIds` ←→ Mihomo `short-id`
- Xray `id` ←→ Mihomo `uuid`
- Xray `serverNames[0]` ←→ Mihomo `servername` ←→ Nginx stream map
- VPS 能直连 Reality `dest` 目标（`curl -4s https://<dest> -o /dev/null -w '%{http_code}'`）

---

## 流量路径

### 路径 1：内网服务反向代理
```
用户 → salmonstill.cn:端口 → dokodemo-door → portal → 隧道 → bridge
  → 端口匹配出站 → 内网目标 → 原路返回
```

### 路径 2：代理翻墙（直连搬瓦工）
```
Mihomo 客户端 → 67.230.189.235:443 (SNI=www.cloudflare.com)
  → Nginx 分流 → Xray :9443 → freedom → 互联网
```

### 路径 3：qBittorrent 代理出站
```
qBittorrent → SOCKS5 旁路由:1080 → Xray to_beijing_direct
  → salmonstill.cn:443 (SNI=www.bilibili.com) → proxy_from_lan → direct → 互联网
```

### 路径 4：BT 入站
```
BT Peer → salmonstill.cn:51413 → portal → 隧道 → bridge → to_qbit → 1.200:51413
```

### 路径 5：Minecraft UDP
```
公网玩家 → salmonstill.cn:19132 → socat → WireGuard → 旁路由 → NAS :19132
```

### 路径 6：SOCKS5 动态回家
```
浏览器 SOCKS5 → salmonstill.cn:38658 → socks-dynamic → portal → 隧道
  → bridge → catch-all direct → 任意 LAN IP:PORT
```



| 文件 | 目标设备 | 部署路径 |
|---|---|---|---|
| `北京vps-stream.conf` | 北京 VPS | `/etc/nginx/nginx.conf` 的 `stream {}` 块 |
| `xray-北京vps-config.json` | 北京 VPS | `/usr/local/etc/xray/config.json` |
| `代理vps-stream.conf` | 搬瓦工 VPS | `/etc/nginx/nginx.conf` 的 `stream {}` 块 |
| `xray-代理服务器-config.json` | 搬瓦工 VPS | `/usr/local/etc/xray/config.json` |
| `xray-旁路由-config.json` | 旁路由 | `/etc/xray/config.json` |
| `旁路由的mihomo config.yaml` | 旁路由 | `/opt/mihomo/config.yaml` |
| `非tun模式的主机mihomo config.yaml` | macOS | `~/Library/Application Support/mihomo/config.yaml` (launchctl 管理) |
| `tun模式的主机mihomo config.yaml` | Spark (Ubuntu) | `/opt/mihomo/config.yaml`
| `subscribe-7891-only.yaml` | 通用 | 精简版模板（单独 7891 端口） |
| `qbittorrent流量转发.md` | — | 方案文档（已合并到 README，可删除） |
| `我的世界udp转发.md` | — | 方案文档（已合并到 README，可删除） |

---

## 部署流程

```
1. 腾讯云防火墙开放端口（443, 51413/tcp, 51820/udp, 19132/udp）
2. 北京 VPS: 部署 Nginx stream → nginx -t && systemctl reload nginx
3. 北京 VPS: 部署 Xray → systemctl restart xray
4. 北京 VPS: 部署 WireGuard → wg-quick@wg0
5. 北京 VPS: 部署 socat → systemctl start mc-forward
6. 搬瓦工 VPS: 部署 Nginx + Xray
7. 旁路由: 部署 Xray → /etc/init.d/xray restart
8. 旁路由: 部署 Mihomo → /etc/init.d/mihomo restart
9. 旁路由: 配置 WireGuard + 端口转发 + SNAT（LuCI）
10. NAS: 创建 macvlan 网络 → 启动 qBittorrent 容器
11. NAS: 启动 Minecraft 容器
```

---

## 验证命令

```bash
# 北京 VPS
ss -tlnp | grep -E '944[345]|3865[3-8]|51413|10809'
systemctl status nginx xray wg-quick@wg0 mc-forward

# 搬瓦工 VPS
ss -tlnp | grep -E '443|9443'
systemctl status nginx xray

# 旁路由
ss -tlnp | grep -E '789[0-2]|1080'            # Mihomo 端口 + Xray socks-lan
mihomo -d /opt/mihomo -t                      # 配置文件校验
tail /opt/mihomo/logs/mihomo.log | grep INFO   # 确认代理无报错
wg show

# qBittorrent 连通性
docker exec qbittorrent curl -s https://ip.sb   # 应返回北京 VPS IP

# Minecraft
systemctl status mc-forward
tcpdump -i wg0 udp port 19132 -n
```
