跨网段访问OpenVPN内网,这个坑你踩过吗?
本文介绍了通过Mac作为网关代理,实现办公内网跨网段访问OpenVPN远程内网的方案。核心矛盾在于办公内网用户无法直接路由到远程内网。方案经历了三个阶段:PF防火墙NAT方案(L3层)仅解决同网段访问,跨网段因路由器缺少回程路由而失败;路由器静态路由方案(L3层补充)需管理权限且不够灵活;最终采用端口映射方案(L4层),在Mac上运行nginx stream模块监听本地端口,通过四层代理绕开路由问题,实现稳定可靠的跨网段访问,支持HTTP和Samba等服务。
一、问题背景
网络环境
某办公网络中,Mac 设备通过 OpenVPN 客户端连接到远程内网(172.16.0.0/16 段)。Mac 本机有多个网络接口:
- 有线网卡
en0:办公内网 A 网段 - 无线网卡
en1:办公内网 B 网段 - USB 网卡
en6:连接 Windows 机器的独立子网 - OpenVPN
utun:远程内网 C 网段
办公内网有多个网段(A、B、D、E 等),各网段通过三层路由器互联互通。部分办公内网用户需要访问 OpenVPN 远程内网 C 中的资源(如 HTTP 服务、Samba 文件共享等)。
业务需求
- 同网段 A 的用户能通过 Mac 访问 OpenVPN 远程内网 C
- 跨网段(B、D、E 等)的用户也能通过 Mac 访问 OpenVPN 远程内网 C
- 访问的目标包括 HTTP 服务、Samba 文件共享等
- 方案需稳定可靠,支持长期运行
核心矛盾
办公内网用户无法直接路由到 OpenVPN 远程内网,必须借助 Mac 作为中转网关。如何让所有网段用户都能通过 Mac 访问远程内网,是本方案要解决的核心问题。
二、方案探索与演进
阶段一:PF 防火墙 NAT 方案(L3 网络层)
思路
利用 macOS 自带的 PF 防火墙,把 Mac 配置成 NAT 网关:
- 开启 IPv4 转发(
net.inet.ip.forwarding=1) - 配置 NAT 规则,把来源流量在 utun 接口做源地址转换
- 配置 pass 规则放行转发流量
- 通过 anchor 机制挂载到
/etc/pf.conf
实现
脚本 gateway.sh 完成:
- 自动检测 OpenVPN 的 utun 接口(通过
10.18.x.x前缀识别) - 给 USB 网卡配置固定 IP
- 生成 PF anchor 文件,包含 NAT 和 PASS 两组规则
- 通过 awk 把 anchor 安全、幂等地注入
/etc/pf.conf - 用
pfctl加载规则并启用 PF - 通知 Tailscale 通告远程内网路由
客户端配置(同网段 A 用户)
同网段 A 用户流量不经过路由器(二层直连),路由器静态路由对同网段无效,因此同网段客户端必须在本地加静态路由,把去往远程内网 C 的流量指向 Mac。
脚本 vpn_via.bat(Windows 客户端)完成:
- 自动提权到管理员
- 清理旧的静态路由
- 添加 2 条静态路由:远程内网 C 的子网 → Mac 的办公网段 IP
- 检查每条路由的添加结果(成功/失败)
配置示例:
route add <远程内网C子网1> mask 255.255.255.0 <Mac办公网段IP> metric 1
route add <远程内网C子网2> mask 255.255.255.0 <Mac办公网段IP> metric 1
特点:
metric 1确保优先级高于默认路由- 不加
-p,重启后路由自动清除(适合临时使用,干净) - 如需永久路由,在
route add后加-p
测试结果
| 访问方 | 结果 | 原因 |
|---|---|---|
| 同网段 A 用户(加客户端路由) | ✅ 正常 | 客户端静态路由指向 Mac,二层直达 |
| 跨网段 B/D/E 用户(加客户端路由) | ❌ 不通 | 客户端指向 Mac 但下一跳跨网段,路由器缺回程路由 |
问题定位
跨网段不通的根本原因:三层路由器不知道远程内网 C 的路由。
跨网段流量路径:
跨网段客户端 → 客户端网关 → 三层路由器 → ??? x→ Mac x→ OpenVPN
路由器收到去往远程内网 C 的包后,查路由表找不到匹配项,按默认路由走 WAN 出口,包被丢弃。
关于客户端静态路由的下一跳限制
标准路由规则要求下一跳必须是本机直连网段的地址,因为发包时需要通过 ARP 解析下一跳的 MAC 地址。
- 同网段客户端:下一跳 Mac 的办公网段 IP 在直连网段内 ✅
- 跨网段客户端:下一跳 Mac 的办公网段 IP 不在直连网段 ❌(Windows 虽能递归查找走默认网关,但路由器仍缺路由,包照样丢失)
因此客户端静态路由方案只适用于与 Mac 同网段的用户,跨网段必须依赖路由器配置。
验证过程
通过 route -n get 确认 Mac 本身路由正常(OpenVPN 没有改默认路由,远程内网走 utun,办公内网走 en0 默认网关),问题确实在路由器侧。
解决思路
需要在三层路由器上加静态路由:
- 远程内网 C 的所有目标网段 → 下一跳指向 Mac 的办公网段 IP
但这一步受限于路由器管理权限,且每增加一个目标网段都要在路由器上同步配置,不够灵活。
阶段二:路由器静态路由方案(L3 网络层,补充)
思路
在办公网的三层路由器上添加静态路由,把去往远程内网 C 的流量统一指向 Mac。
配置
在路由器上添加 2 条静态路由:
- 远程内网 C 子网 1 → Mac 的办公网段 IP
- 远程内网 C 子网 2 → Mac 的办公网段 IP
跨网段客户端配置变更
路由器配置静态路由后,跨网段客户端无需任何配置,流量自动经路由器转发到 Mac。之前在跨网段客户端上加的静态路由可以删除:
route delete <远程内网C子网1> mask 255.255.255.0
route delete <远程内网C子网2> mask 255.255.255.0
但同网段 A 用户仍需保留客户端静态路由,因为同网段流量不经过路由器。
限制
| 限制 | 说明 |
|---|---|
| 需要路由器管理权限 | 普通用户无法操作 |
| 每个目标网段都要加 | 远程内网增加网段时路由器要同步 |
| 影响面大 | 路由器配置错误可能影响全网 |
| 跨网段客户端无需配置 | 路由器配置后自动生效 |
| 同网段客户端仍需配置 | 同网段流量不经路由器,需保留客户端静态路由 |
适用场景
有路由器管理权限、目标网段固定、希望对跨网段客户端透明的场景。同网段用户仍需配套客户端静态路由脚本。
阶段三:端口映射方案(L4 传输层,最终采用)
思路
绕开三层路由问题,改用四层端口代理:
- Mac 上运行 nginx stream 模块,监听本地端口
- 内网用户(任意网段)直接 TCP 连接 Mac 的端口
- nginx 把连接转发到 OpenVPN 远程内网的目标服务
关键点:内网用户连 Mac 的办公网段 IP 是普通 TCP 连接,跨网段经路由器正常三层转发即可到达,不需要任何额外路由配置。Mac 主动向远程内网发起连接,走 utun 隧道,本机路由表已通。
架构图
内网用户(任意网段) OpenVPN 远程内网 C
│ ▲
│ TCP 连接到 Mac:端口 │
▼ │
┌─────────┐ 新建本地连接 ┌──────────┐ │
│ Mac │ ──────────────▶ │ utun │ ───┘
│ nginx │ (走 VPN 路由) │ (OpenVPN)│
│ stream │ └──────────┘
└─────────┘
实现
脚本 proxy_setup.sh 完成:
- 检查并安装 nginx(含 stream 模块)
- 配置 nginx.conf
- 通过
brew services启动,开机自启
配置示例:
stream {
# HTTP 服务端口映射
server {
listen <本地端口>;
proxy_pass <远程IP>:<远程端口>;
proxy_connect_timeout 10s;
proxy_timeout 1h;
}
}
测试结果
| 访问方 | 结果 | 说明 |
|---|---|---|
| 同网段 A 用户 | ✅ 正常 | TCP 直连 Mac 端口 |
| 跨网段 B/D/E 用户 | ✅ 正常 | 经路由器三层转发到 Mac,无需额外路由 |
| 路由器配置 | ✅ 无需改动 | 完全绕开 |
| 客户端配置 | ✅ 无需改动 | 直接访问 Mac 的 IP:端口 |
限制
| 限制 | 说明 |
|---|---|
| 只支持 TCP | UDP 服务(如 DNS)无法代理 |
| 不支持 ICMP | 无法 ping 远程内网 |
| 端口 1:1 映射 | 一个 Mac 端口对应一个远程服务 |
| HTTPS 证书警告 | 代理 HTTPS 时证书 CN 不匹配,客户端需忽略 |
三、三种方案对比
| 对比项 | 方案一 PF NAT + 客户端路由 | 方案二 路由器静态路由 | 方案三 端口映射(采用) |
|---|---|---|---|
| 工作层级 | L3 网络层 | L3 网络层 | L4 传输层 |
| 服务端配置 | PF NAT 规则(复杂) | 路由器静态路由 | nginx stream(简单) |
| 路由器配置 | 不需要 | 必须加静态路由 | 不需要 |
| 同网段客户端 | 需加静态路由 | 需加静态路由 | 无需配置 |
| 跨网段客户端 | 无法实现 | 无需配置 | 无需配置 |
| pf 规则 | 复杂 | 不需要 | 不需要 |
| 支持 ICMP/ping | ✅ | ✅ | ❌ |
| 支持 UDP | ✅ | ✅ | ❌(需额外配置) |
| 任意 IP:端口访问 | ✅ | ✅ | ❌ 需逐个映射 |
| 跨网段兼容 | ❌ 仅同网段 | ✅ | ✅ 完美 |
| 部署复杂度 | 中 | 低(但有前置条件) | 低 |
| 长期维护 | 中 | 低 | 低 |
四、最终方案:端口映射详细说明
4.1 方案选择理由
- 跨网段零配置:客户端和路由器都不用动,部署成本最低
- 稳定可靠:nginx stream 是成熟的四层代理,长期运行稳定
- 开机自启:通过
brew services管理,重启自动恢复 - 权限要求低:不需要路由器管理权限
4.2 客户端访问方式
- HTTP 服务:浏览器访问
http://<Mac办公网段IP>:<端口> - Samba 共享:资源管理器输入
\\<Mac办公网段IP>\share - SSH 等服务:用对应客户端连
Mac办公网段IP:端口