OpenVPN + ikuai + NAS 虚拟机网络不通的完整排查与原理分析

一、问题背景

我的使用场景如下:

  • 公司电脑通过 OpenVPN 连接回家里的 ikuai 路由器
  • ikuai 局域网网段:192.168.78.0/24
  • ikuai IP:192.168.78.1
  • 家里有一台 绿联 NAS
  • NAS 内运行 KVM 虚拟机
  • Ubuntu 虚拟机:
    • 网卡:virtIO
    • Bridge:vnet-bridge0
    • IP:192.168.78.19
    • 默认网关:192.168.78.1

二、异常现象

出现的问题非常“拧巴”:

  • 公司电脑 ✅ 可以 ping 通 192.168.78.1
  • 公司电脑 ✅ 可以 ping 通局域网内其他实体主机(如 192.168.78.239)
  • Ubuntu 虚拟机 ✅ 可以 ping 通路由器
  • 公司电脑 ❌ ping 不通 Ubuntu 虚拟机
  • 公司电脑 ❌ SSH 连接 Ubuntu

直觉上非常容易误判为:

  • Ubuntu 防火墙问题
  • SSH 服务问题
  • 虚拟机网络配置错误

但实际上,这些都不是根因。


三、真实网络拓扑(非常关键)

真实的数据路径如下:

1
2
3
4
5
6
7
8
9
10
11
12
13

公司电脑

│ OpenVPN

ikuai 路由器 (192.168.78.1)

├─ 实体主机(正常)

└─ NAS
└─ Linux Bridge (vnet-bridge0)
└─ Ubuntu VM (192.168.78.19)

这是一个 VPN + 路由器 + NAS 虚拟化桥接网络 的组合场景。


四、一个关键事实:去程是通的,回程是断的

通过在 Ubuntu 虚拟机上抓包:

1
sudo tcpdump -i any icmp or tcp port 22

可以确认:

  • 当公司电脑 ping / SSH Ubuntu 时
  • Ubuntu 是能收到数据包的
  • 但始终没有成功返回响应

这说明:

网络不是“完全不通”,而是“单向可达”


五、没有 SNAT 时,发生了什么?

1. Ubuntu 看到的源地址

没有做任何 NAT 时:

  • Ubuntu 收到的数据包源 IP 是:
1
10.1.1.x   (OpenVPN 客户端地址池)

2. Ubuntu 的路由认知

Ubuntu 只知道:

  • 本地网段:192.168.78.0/24
  • 默认网关:192.168.78.1

并不知道

  • 10.1.1.0/24 是通过 OpenVPN 隧道进来的
  • 回包需要“特殊路径”

于是它的行为是完全正确的:

1
把回包交给默认网关(ikuai)

六、关键误区:为什么“同一个 ikuai”却回不去?

一个非常容易产生的误解是:

ikuai 既是默认网关,又是 OpenVPN Server,
为什么它不知道这是 VPN 回包?

原因在于:

1️⃣ L3 路由 vs VPN 会话是两套系统

  • Ubuntu → ikuai 的回包:
    • 三层(L3)无状态 IP 报文
  • OpenVPN 隧道:
    • 依赖四层以上(UDP/TCP + 会话状态)的有状态通道

在没有 NAT 的情况下:

  • ikuai 收到的只是:
1
来自 LAN,目标是 10.1.1.x 的普通 IP 包
  • 无法自动映射到某一个 VPN 会话
  • 即使 VPN Server 就运行在 ikuai 上

七、为什么 SNAT 能一招解决问题

核心思想

不要让虚拟机看到 VPN 客户端的真实 IP

通过 SNAT:

1
10.1.1.x  →  192.168.78.1

Ubuntu 实际看到的是:

  • 源地址:192.168.78.1

于是回包变成:

1
Ubuntu → 192.168.78.1

ikuai 此时能做什么?

  • 回包命中 连接跟踪表(conntrack)
  • ikuai 清楚知道:
    • 这是自己 NAT 过的一条连接
    • 原始流量来自 OpenVPN 接口
  • 自动执行:
    • 反向 NAT
    • 回送至对应 VPN 隧道

👉 无状态问题,被转化成了有状态问题


八、最终解决方案(ikuai SNAT)

在 ikuai 防火墙 → NAT → 源地址转换 中新增规则:

字段
动作 源地址 NAT
进接口 OpenVPN(如 sovpn
出接口 LAN(如 lan1
源地址 10.1.1.0/24
目的地址 192.168.78.0/24
NAT 地址 192.168.78.1
协议 任意

规则生效后:

  • ping 正常
  • SSH 正常
  • 其他服务端口均可访问

九、为什么 ping 不能用于判断“哪里不通”

ping 的本质是:

1
Echo Request → Echo Reply

任何一侧失败,结果都是:

1
Request timeout

它无法告诉你:

  • 是没发出去
  • 还是发出去了回不来

在 VPN / 虚拟化场景中,正确的排障方式是:

  • tcpdump 抓包(是否到达)
  • NAT / conntrack 状态
  • 路由与回程路径分析

十、方法论总结(最重要)

这次问题的本质不是:

  • Ubuntu 配错
  • SSH 配错
  • 虚拟机配置错

而是一个非常典型的工程问题

VPN + 虚拟化桥接网络下,
回程路径缺失会话映射

记住一句话即可:

只要看到:VPN 能进、实体机能通、虚拟机不通,
先想回程路径,再想防火墙。


十一、延伸思考

类似问题同样会出现在:

  • Docker Bridge
  • KVM / libvirt
  • 多 VPN 并存
  • 多 WAN 场景

解决思路永远是三选一:

  1. SNAT(最简单、最稳定)
  2. 明确的反向路由
  3. 策略路由 + 会话绑定

记录一次真实问题,比十篇教程更有价值。