iOS Surge + Tailscale + WireGuard 配置指南
通过 VPS 中继实现 iOS Surge 经 WireGuard 接入 Tailscale 全设备网络的完整方案。
架构总览
阶段一(当前):VPS 中继
iOS Surge ──WG──→ VPS (公网IPv4) ──WG──→ Mac Mini ──Tailscale──→ 所有设备
阶段二(换路由器后):IPv6 直连 + VPS 回退
优先:iOS Surge ──WG/IPv6──→ Mac Mini ──Tailscale──→ 所有设备
回退:iOS Surge ──WG/IPv4──→ VPS ──WG──→ Mac Mini ──Tailscale──→ 所有设备
角色分工
| 设备 | 角色 | 改动 |
|---|---|---|
| VPS | WireGuard 中继 | 安装 WireGuard |
| Mac Mini | WireGuard 客户端 + Tailscale 网关 | WireGuard + pf NAT |
| iOS | Surge 客户端 | 添加 WireGuard 配置 |
| 其他 Tailscale 设备 | 无改动 | 保持现状 |
第一步:密钥(已生成)
| 设备 | Private Key | Public Key |
|---|---|---|
| VPS | aEdTUW73VsbAjbcPbi4p8GZxhUUnJMBvc311DGb0e18= |
dd7PH9OAxYfb4yoaF+8LzZmr2LhK2N11uAIA65AOqnc= |
| Mac Mini | AAYVDaPl6KrB2pd1xMucvduwGLJoprjwQsrvL2zl7Vc= |
ntcMbfKFXTXaqLdLHjW9GMp7ETQEvMBxlT1i/oWy6wA= |
| iOS | YEC3Rim6YJYxdq9rrgN70y2EB+yz6hy7ZvyQJnkOlXo= |
edcBiCkrRMcy9iu8hNJ2cOOY4PeWOWmNJIPnkTuT4jk= |
⚠️ 私钥务必妥善保管,不要泄露。
第二步:VPS 配置
2.1 安装 WireGuard
apt update && apt install wireguard -y
2.2 创建配置文件
cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = aEdTUW73VsbAjbcPbi4p8GZxhUUnJMBvc311DGb0e18=
PostUp = sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
# ---- Mac Mini ----
[Peer]
PublicKey = ntcMbfKFXTXaqLdLHjW9GMp7ETQEvMBxlT1i/oWy6wA=
AllowedIPs = 10.10.0.3/32, 100.64.0.0/10, fd7a:115c:a1e0::/48
# VPS 把目标为 Tailscale 网段的流量全部转发给 Mac Mini
# ---- iOS (Surge) ----
[Peer]
PublicKey = edcBiCkrRMcy9iu8hNJ2cOOY4PeWOWmNJIPnkTuT4jk=
AllowedIPs = 10.10.0.2/32
EOF
2.3 启动
# 防火墙放行
ufw allow 51820/udp
# 启动 + 开机自启
wg-quick up wg0
systemctl enable wg-quick@wg0
# 验证
wg show
第三步:Mac Mini 配置
3.1 安装 WireGuard
brew install wireguard-tools
3.2 确认 Tailscale 接口名
ifconfig | grep -B 1 "inet 100\."
记下接口名(当前为 utun9),后续 NAT 规则要用。
3.3 创建 WireGuard 客户端配置
sudo mkdir -p /etc/wireguard
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.10.0.3/24
PrivateKey = AAYVDaPl6KrB2pd1xMucvduwGLJoprjwQsrvL2zl7Vc=
[Peer]
PublicKey = dd7PH9OAxYfb4yoaF+8LzZmr2LhK2N11uAIA65AOqnc=
Endpoint = cn.conversun.com:51820
AllowedIPs = 10.10.0.0/24
PersistentKeepalive = 25
EOF
AllowedIPs = 10.10.0.0/24 只路由 WireGuard 内网流量,不影响 Mac Mini 正常上网和 Tailscale。3.4 启动 WireGuard
sudo wg-quick up wg0
# 验证 handshake
sudo wg show
3.5 开启 IP 转发
sudo sysctl -w net.inet.ip.forwarding=1
# 永久生效
echo "net.inet.ip.forwarding=1" | sudo tee -a /etc/sysctl.conf
3.6 配置 pf NAT
从 WireGuard 进来的流量,以 Mac Mini 的 Tailscale IP 身份发往 Tailscale 网络:
sudo mkdir -p /etc/pf.anchors
sudo tee /etc/pf.anchors/wg-tailscale << 'EOF'
nat on utun9 from 10.10.0.0/24 to 100.64.0.0/10 -> (utun9)
# 访问家庭局域网设备(按需取消注释,改为你的网段)
# nat on en0 from 10.10.0.0/24 to 192.168.0.0/16 -> (en0)
EOF
注册 anchor 到 pf.conf:
⚠️ macOS pf.conf 要求规则按顺序排列(normalization → translation → filtering), 不能追加到文件末尾,必须插到已有的 nat-anchor 行附近。sudo cp /etc/pf.conf /etc/pf.conf.backup
# 在 nat-anchor "com.apple/*" 后面插入(不是追加到末尾!)
sudo sed -i '' '/^nat-anchor "com.apple\/\*"/a\
nat-anchor "wg-tailscale"
' /etc/pf.conf
如果 sed 报错,手动编辑也行:
sudo nano /etc/pf.conf
# 找到 nat-anchor "com.apple/*" 这一行,在它下面加一行:
# nat-anchor "wg-tailscale"
加载规则:
# 加载 pf.conf(注册 anchor)
sudo pfctl -f /etc/pf.conf
# 启用 pf
sudo pfctl -e
# 显式加载 anchor 内的 NAT 规则(关键步骤!)
sudo pfctl -a "wg-tailscale" -f /etc/pf.anchors/wg-tailscale
# 验证 anchor 内规则已生效
sudo pfctl -a "wg-tailscale" -s nat
# 应该看到:nat on utun9 from 10.10.0.0/24 to 100.64.0.0/10 -> (utun9) round-robin
ALTQ 相关警告是 macOS 正常现象,可忽略。3.7 开机自启
创建启动脚本,自动检测 Tailscale 接口名(重启后 utun 编号可能变):
sudo tee /usr/local/bin/wg-gateway-start.sh << 'SCRIPT'
#!/bin/bash
LOG="/var/log/wg-gateway.log"
echo "$(date): Starting WireGuard Gateway..." >> $LOG
sysctl -w net.inet.ip.forwarding=1
# 等待 Tailscale 启动(最多 60 秒)
for i in $(seq 1 12); do
TS_IF=$(ifconfig | grep -B 1 "inet 100\." | head -1 | cut -d: -f1)
[ -n "$TS_IF" ] && break
echo "$(date): Waiting for Tailscale... ($i)" >> $LOG
sleep 5
done
if [ -z "$TS_IF" ]; then
echo "$(date): ERROR - Tailscale interface not found!" >> $LOG
exit 1
fi
echo "$(date): Tailscale interface: $TS_IF" >> $LOG
# 动态写入正确的接口名
cat > /etc/pf.anchors/wg-tailscale << NATEOF
nat on $TS_IF from 10.10.0.0/24 to 100.64.0.0/10 -> ($TS_IF)
NATEOF
pfctl -f /etc/pf.conf
pfctl -e
pfctl -a "wg-tailscale" -f /etc/pf.anchors/wg-tailscale
wg-quick up wg0 2>> $LOG || echo "$(date): WireGuard may already be running" >> $LOG
echo "$(date): Gateway ready. Tailscale IF=$TS_IF" >> $LOG
SCRIPT
sudo chmod +x /usr/local/bin/wg-gateway-start.sh
sudo tee /Library/LaunchDaemons/com.wg-gateway.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.wg-gateway</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/wg-gateway-start.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/wg-gateway.log</string>
<key>StandardErrorPath</key>
<string>/var/log/wg-gateway.log</string>
</dict>
</plist>
EOF
sudo launchctl load /Library/LaunchDaemons/com.wg-gateway.plist
第四步:Surge iOS 配置
4.1 代理声明
[Proxy]
WG-Home = wireguard, section-name = HomeVPS
4.2 WireGuard 段
[WireGuard HomeVPS]
private-key = YEC3Rim6YJYxdq9rrgN70y2EB+yz6hy7ZvyQJnkOlXo=
self-ip = 10.10.0.2
dns-server = 100.100.100.100, 8.8.8.8
mtu = 1280
peer = (public-key = dd7PH9OAxYfb4yoaF+8LzZmr2LhK2N11uAIA65AOqnc=, allowed-ips = "10.10.0.0/24, 100.64.0.0/10, fd7a:115c:a1e0::/48", endpoint = cn.conversun.com:51820, keepalive = 25)
dns-server中的100.100.100.100是 Tailscale MagicDNS,iOS 可通过设备名.ts.net访问。
4.3 规则
放在其他规则前面:
[Rule]
# Tailscale 网段
IP-CIDR,100.64.0.0/10,WG-Home,no-resolve
IP-CIDR6,fd7a:115c:a1e0::/48,WG-Home,no-resolve
# WireGuard 内网
IP-CIDR,10.10.0.0/24,WG-Home,no-resolve
# MagicDNS 域名
DOMAIN-SUFFIX,ts.net,WG-Home
# 家庭局域网(按需取消注释)
# IP-CIDR,192.168.0.0/16,WG-Home,no-resolve
# ... 其他规则 ...
# FINAL,你的常规代理策略
第五步:验证
| 步骤 | 操作 | 预期 |
|---|---|---|
| VPS | wg show |
看到两个 peer,Mac Mini 有 handshake |
| Mac Mini | sudo wg show |
看到与 VPS 的 handshake |
| Mac Mini | ping 10.10.0.1 |
通(到 VPS) |
| Mac Mini | sudo pfctl -a "wg-tailscale" -s nat |
看到 NAT 规则 |
| iOS | 开启 Surge,访问 100.x.x.x |
能访问 Tailscale 设备 |
| iOS | ssh user@设备名.ts.net |
MagicDNS 正常解析 |
排障顺序
wg show— 两端 handshake 是否成功sysctl net.inet.ip.forwarding— Mac Mini 转发是否开启sudo pfctl -a "wg-tailscale" -s nat— NAT 规则是否存在且接口名正确ifconfig | grep -B1 "100\."— Tailscale 接口名是否与 NAT 规则匹配
阶段二升级:换路由器后启用 IPv6 直连
换了支持 IPv6 防火墙配置的路由器后,只需改 Surge iOS 配置,VPS 和 Mac Mini 完全不动。
Mac Mini 额外开一个 WireGuard 服务端
在 Mac Mini 上新增一个 WireGuard 接口,直接监听 51820(或用另一个端口如 51821):
# 生成 Mac Mini 服务端密钥(和之前的客户端密钥是两套)
wg genkey | tee macmini_server_private.key | wg pubkey > macmini_server_public.key
sudo tee /etc/wireguard/wg1.conf << 'EOF'
[Interface]
Address = 10.20.0.1/24
ListenPort = 51821
PrivateKey = <macmini_server_private.key>
[Peer]
PublicKey = edcBiCkrRMcy9iu8hNJ2cOOY4PeWOWmNJIPnkTuT4jk=
AllowedIPs = 10.20.0.2/32
EOF
sudo wg-quick up wg1
使用不同的子网(10.20.0.0/24)避免和 VPS 那条 WireGuard 冲突。更新 pf NAT 规则,加入新子网:
nat on utun9 from 10.10.0.0/24 to 100.64.0.0/10 -> (utun9)
nat on utun9 from 10.20.0.0/24 to 100.64.0.0/10 -> (utun9)
路由器 IPv6 防火墙放行 Mac Mini 的 [IPv6]:51821/udp。
Surge iOS 改为 Fallback
[Proxy]
# IPv6 直连 Mac Mini(优先)
WG-Direct = wireguard, section-name = HomeDirect
# VPS 中继(回退)
WG-VPS = wireguard, section-name = HomeVPS
[Proxy Group]
WG-Home = fallback, WG-Direct, WG-VPS, url = http://10.20.0.1/generate_204, interval = 300, timeout = 3
[WireGuard HomeDirect]
private-key = YEC3Rim6YJYxdq9rrgN70y2EB+yz6hy7ZvyQJnkOlXo=
self-ip = 10.20.0.2
dns-server = 100.100.100.100, 8.8.8.8
mtu = 1280
peer = (public-key = <macmini_server_public.key>, allowed-ips = "10.20.0.0/24, 100.64.0.0/10, fd7a:115c:a1e0::/48", endpoint = [Mac Mini IPv6]:51821, keepalive = 25)
[WireGuard HomeVPS]
# 保持不变
private-key = YEC3Rim6YJYxdq9rrgN70y2EB+yz6hy7ZvyQJnkOlXo=
self-ip = 10.10.0.2
dns-server = 100.100.100.100, 8.8.8.8
mtu = 1280
peer = (public-key = dd7PH9OAxYfb4yoaF+8LzZmr2LhK2N11uAIA65AOqnc=, allowed-ips = "10.10.0.0/24, 100.64.0.0/10, fd7a:115c:a1e0::/48", endpoint = cn.conversun.com:51820, keepalive = 25)
[Rule]
# 规则不变,指向策略组 WG-Home
IP-CIDR,100.64.0.0/10,WG-Home,no-resolve
IP-CIDR6,fd7a:115c:a1e0::/48,WG-Home,no-resolve
IP-CIDR,10.10.0.0/24,WG-Home,no-resolve
IP-CIDR,10.20.0.0/24,WG-Home,no-resolve
DOMAIN-SUFFIX,ts.net,WG-Home
IPv6 可用时直连(一跳),否则自动回退 VPS 中继(两跳)。