iOS Surge + Tailscale + WireGuard 配置指南

通过 VPS 中继实现 iOS Surge 经 WireGuard 接入 Tailscale 全设备网络的完整方案。

iOS Surge + Tailscale + WireGuard 配置指南

架构总览

阶段一(当前):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 正常解析

排障顺序

  1. wg show — 两端 handshake 是否成功
  2. sysctl net.inet.ip.forwarding — Mac Mini 转发是否开启
  3. sudo pfctl -a "wg-tailscale" -s nat — NAT 规则是否存在且接口名正确
  4. 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 中继(两跳)。