如何在macOS上链式连接两个VPN以绕过封锁(以Outline+WireGuard为例)
我太懂你这种困境了——在受限地区用公司/学校的非混淆VPN(比如WireGuard、OpenVPN)直接连就被封,可又没法改组织的VPN协议,还好Outline这类混淆VPN能正常用。下面就手把手教你在Mac上把这俩串起来,不用额外硬件就能实现「Mac → Outline → 公司WireGuard → 互联网」的流量链路。
核心思路
本质上就是让公司WireGuard的所有流量先经过Outline的混淆隧道——具体来说,把公司WireGuard服务器的端点IP的所有请求,强制路由到Outline的VPN接口上,这样WireGuard的握手包和数据都会先经过混淆层,不会被检测和封锁。
具体实操步骤(Outline+WireGuard为例)
1. 先启动并连接Outline VPN
打开Outline客户端,确保它正常连接成功。你可以去「系统偏好设置→网络」面板里确认Outline的接口(一般叫「Outline VPN」,或者是类似utunX的虚拟接口,比如utun2)已经处于连接状态。
2. 获取公司WireGuard的服务器端点IP
打开你的WireGuard配置文件(.conf格式),找到Endpoint那一行,比如:
Endpoint = 123.45.67.89:51820
把冒号前的IP地址(比如123.45.67.89)记下来,这就是我们要定向路由的目标。
如果Endpoint是域名(比如wg.company.com:51820),可以打开终端用dig +short wg.company.com解析出对应的IP。
3. 添加静态路由,让WireGuard端点流量走Outline
打开终端,先确认Outline的接口名称:
- 可以用
networksetup -listallnetworkservices查看所有网络服务,找到Outline对应的名称; - 或者用
ifconfig查看,找带有Outline标识的utun接口(比如utun2)。
然后执行下面的命令(替换成你的实际IP和接口名):
sudo route add -host 123.45.67.89 -interface utun2
输入你的Mac密码确认执行,这条命令的意思是:所有发往123.45.67.89的流量,都通过utun2(Outline的接口)发送。
4. 连接公司的WireGuard VPN
现在打开WireGuard客户端,连接你的公司VPN。这时候WireGuard的所有请求都会先经过Outline的隧道,再也不用担心被封锁了!
验证链式是否成功
打开终端执行traceroute [公司内部资源IP],比如traceroute 10.0.0.10(假设这是公司内部服务器),看输出的第一跳是不是Outline的服务器IP,然后再到公司WireGuard的服务器,最后到达内部资源——如果是这样,就说明链式路由生效了。
持久化路由(避免重启后失效)
手动添加的静态路由在重启Mac或者断开Outline后会消失,你可以写个简单的脚本自动添加:
- 新建一个文本文件,命名为
route-wg-through-outline.sh,内容如下:
#!/bin/bash # 替换成你的WireGuard端点IP和Outline接口名 WG_ENDPOINT="123.45.67.89" OUTLINE_INTERFACE="utun2" # 先删除旧路由(如果存在) sudo route delete -host $WG_ENDPOINT > /dev/null 2>&1 # 添加新路由 sudo route add -host $WG_ENDPOINT -interface $OUTLINE_INTERFACE
- 给脚本加执行权限:
chmod +x route-wg-through-outline.sh
- 每次连接Outline后,打开终端运行
./route-wg-through-outline.sh即可自动添加路由。如果想更省心,还可以设置LaunchDaemon让系统在Outline接口上线时自动执行脚本(不过这个稍微复杂,日常手动跑脚本也足够用)。
注意事项
- 确保Outline是全局模式(或者至少让WireGuard端点IP走Outline):如果Outline是分流模式,可能端点IP还是走本地网络,照样会被封;
- 如果WireGuard的端点域名对应多个IP,记得把所有解析出来的IP都添加路由;
- 可以用
netstat -rn查看当前路由表,确认WireGuard端点IP的路由确实指向Outline的接口; - 要是遇到连接问题,先断开两个VPN,重新按顺序连接(先Outline,再加路由,最后WireGuard)。
备注:内容来源于stack exchange,提问作者Theo

