iOS 上实现同时使用代理和内网穿透的需求其实很常见,尤其是当你需要在外访问家里设备(如 NAS)时。 但是很多的工具(例如:tailscale,WireGuard)基本上只能满足其中一个需求,要么是全局代理,要么是内网穿透,很难同时兼顾,它们基本上都是靠VPNExtension,iOS中只能开一个。 小火箭(Shadowrocket)是 iOS 平台上一款相当不错的代理工具,协议支持全面,价格也很合适。这里我记录一下使用它实现 代理 + 内网穿透 的完整部署步骤,最终实现 “出国”+“回家” 两个功能。 内网穿透,我这里选择的协议是WireGuard。
需求分析
代理部分没什么特别的,直接导入订阅即可。这里主要记录 WireGuard 的搭建过程。
我的需求很简单:随时随地能连上家里的 NAS。我的 NAS 是群晖 DS218+,系统内核比较老,不支持原生 WireGuard(Linux 5.6 之后的内核才内置支持),懒得折腾内核升级,所以我在内网部署了一台虚拟机作为跳板机来连接它。
WireGuard 需要一台有公网 IP 的云主机作为中转节点。如果不想自己搞,可以试试 Tailscale(底层也是基于 WireGuard 的)。
网络拓扑
我们的网络结构很简单,就是一条线:
phone → server(Ubuntu 24.04) → jump(Ubuntu 24.04) → NAS
当然,你也可以直接把 WireGuard 装在 NAS 里省去跳板机。不过用跳板机有个额外的好处:可以访问局域网内的所有设备,不用每台都单独配置。
一、配置 Server(云主机)
24.04 的内核版本已经原生支持 WireGuard,直接安装即可:
sudo apt install wireguard -y如果你的系统内核较旧不支持,可以尝试升级:
sudo apt update
sudo apt install --install-recommends linux-generic
sudo reboot生成密钥对
安装完成后,我们把三台机器(phone、server、jump)的密钥对一次性都生成好:
wg genkey | tee phone_private.key | wg pubkey > phone_public.key
wg genkey | tee server_private.key | wg pubkey > server_public.key
wg genkey | tee jump_private.key | wg pubkey > jump_public.key编写 Server 配置
sudo vim /etc/wireguard/wg0.conf
[Interface]
# 填入 server_private.key 的内容
PrivateKey = <server_private.key>
# 服务端在隧道内的虚拟 IP
Address = 10.7.0.1/24
# 建议用一个 50000 以上的随机 UDP 端口
ListenPort = 51280
# 核心:开启流量转发(网卡名通过 ip addr 查看,常见为 eth0 / ens3 等)
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# --- Peer: 手机 ---
[Peer]
PublicKey = <phone_public.key>
AllowedIPs = 10.7.0.2/32
# --- Peer: Jump 跳板机 ---
[Peer]
PublicKey = <jump_public.key>
AllowedIPs = 10.7.0.3/32,192.168.1.0/24,192.168.2.0/24整体 IP 分配如下:
phone(10.7.0.2) → server(10.7.0.1) → jump(10.7.0.3) → NAS(192.168.1.x)文章末尾附有一键部署脚本,可以自动完成安装、生成密钥和配置文件。
启动服务
sudo wg-quick up wg0Tips:
- 关闭:
sudo wg-quick down wg0- 推荐使用 systemctl 管理:
sudo systemctl start [email protected]- 开机自启:
sudo systemctl enable [email protected]- 如果遇到
wg-quick: 'wg0' already exists错误,先sudo wg-quick down wg0再重新启动
这样 Server 就配置完成了。
二、配置 Jump(跳板机)
Jump 的安装方式和 Server 一样:
sudo apt install wireguard -ysudo vim /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <jump_private.key>
Address = 10.7.0.3/24
MTU = 1420
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE
[Peer]
PublicKey = <server_public.key>
Endpoint = <你的云主机公网IP>:51280
AllowedIPs = 10.7.0.0/24
PersistentKeepalive = 25启动:
sudo wg-quick up wg0验证连通性
在 Server 上执行 sudo wg show,留意 latest handshake 字段:
allowed ips: 10.7.0.3/32, 192.168.1.0/24, 192.168.2.0/24
latest handshake: 32 seconds ago
transfer: 8.30 MiB received, 515.13 KiB sent也可以直接 ping 10.7.0.3,应该是可以 ping 通的。在 Jump 上执行 sudo wg show 也能看到类似的结果。
连不通?排查清单:
- 检查内核转发:
cat /proc/sys/net/ipv4/ip_forward应返回1。如果不是,临时开启:sudo sysctl -w net.ipv4.ip_forward=1;永久生效:编辑/etc/sysctl.conf添加net.ipv4.ip_forward = 1,然后执行sudo sysctl -p- 检查防火墙/安全组:确认云主机的安全组或防火墙已放行配置的 UDP 端口(如 51280)
三、配置小火箭(Phone 端)
小火箭提供图形界面,按照提示添加 WireGuard 配置即可:
配置分流规则(重要!)
这一步是关键。需要在小火箭的分流规则中添加以下 CIDR,确保访问这些网段时走 WireGuard 隧道:
10.7.0.0/24— WireGuard 隧道网段192.168.1.0/24— 家庭局域网网段(按实际情况添加)
这样配置完成后,小火箭会自动根据目标 IP 判断是走代理还是走 WireGuard 隧道,实现"出国"和"回家"两不误。
附录:一键部署脚本
这个脚本在 Server(云主机) 上运行,会自动完成 WireGuard 的安装检测、内核检查、密钥生成和配置文件创建。
#!/usr/bin/env bash
#
# WireGuard 一键部署脚本
# 在 Server(云主机)上运行,在当前目录生成 server / phone / jump 三份配置
# 用法: sudo bash wireguard_setup.sh
#
set -euo pipefail
# ======================== 颜色定义 ========================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() {
echo -e "${RED}[ERROR]${NC} $*"
exit 1
}
# ======================== 权限检查 ========================
if [[ $EUID -ne 0 ]]; then
error "请使用 root 权限运行此脚本: sudo bash $0"
fi
# ======================== 可配置参数 ========================
WG_PORT=${WG_PORT:-51280}
WG_SUBNET="10.7.0"
SERVER_IP="${WG_SUBNET}.1"
PHONE_IP="${WG_SUBNET}.2"
JUMP_IP="${WG_SUBNET}.3"
LAN_SUBNETS=${LAN_SUBNETS:-"192.168.1.0/24,192.168.2.0/24"}
WG_DIR="."
KEY_DIR="${WG_DIR}/keys"
echo ""
echo -e "${CYAN}======================================${NC}"
echo -e "${CYAN} WireGuard 配置生成${NC}"
echo -e "${CYAN}======================================${NC}"
echo ""
# ======================== 1. 检测内核是否支持 WireGuard ========================
info "检测内核版本..."
KERNEL_VERSION=$(uname -r)
KERNEL_MAJOR=$(echo "$KERNEL_VERSION" | cut -d. -f1)
KERNEL_MINOR=$(echo "$KERNEL_VERSION" | cut -d. -f2)
if [[ $KERNEL_MAJOR -gt 5 ]] || { [[ $KERNEL_MAJOR -eq 5 ]] && [[ $KERNEL_MINOR -ge 6 ]]; }; then
info "内核版本 ${KERNEL_VERSION},原生支持 WireGuard ✓"
else
warn "内核版本 ${KERNEL_VERSION},可能不原生支持 WireGuard(需要 5.6+)"
warn "将尝试通过 DKMS 模块安装,如果失败请先升级内核:"
warn " sudo apt update && sudo apt install --install-recommends linux-generic && sudo reboot"
fi
# 检查内核模块是否可用
if modprobe wireguard 2>/dev/null; then
info "WireGuard 内核模块加载成功 ✓"
else
warn "WireGuard 内核模块暂未加载,安装后会自动处理"
fi
# ======================== 2. 检测并安装 WireGuard ========================
if command -v wg &>/dev/null; then
info "WireGuard 已安装,版本: $(wg --version) ✓"
else
info "WireGuard 未安装,正在安装..."
apt update -qq
apt install -y wireguard wireguard-tools
if command -v wg &>/dev/null; then
info "WireGuard 安装成功 ✓"
else
error "WireGuard 安装失败,请检查系统环境"
fi
fi
# ======================== 3. 开启内核转发 ========================
info "检查 IP 转发..."
if [[ $(cat /proc/sys/net/ipv4/ip_forward) -eq 1 ]]; then
info "IPv4 转发已开启 ✓"
else
warn "IPv4 转发未开启,正在开启..."
sysctl -w net.ipv4.ip_forward=1 >/dev/null
# 持久化
if grep -q "^net.ipv4.ip_forward" /etc/sysctl.conf; then
sed -i 's/^net.ipv4.ip_forward.*/net.ipv4.ip_forward = 1/' /etc/sysctl.conf
else
echo "net.ipv4.ip_forward = 1" >>/etc/sysctl.conf
fi
sysctl -p >/dev/null 2>&1
info "IPv4 转发已开启并持久化 ✓"
fi
# ======================== 4. 自动检测网卡名称 ========================
info "自动检测默认网卡..."
DEFAULT_IFACE=$(ip route show default | awk '/default/ {print $5}' | head -n1)
if [[ -z "$DEFAULT_IFACE" ]]; then
# fallback: 尝试从 ip link 获取第一个非 lo 的网卡
DEFAULT_IFACE=$(ip -o link show | awk -F': ' '{print $2}' | grep -v lo | head -n1)
fi
if [[ -z "$DEFAULT_IFACE" ]]; then
error "无法自动检测网卡名称,请手动指定"
fi
info "检测到默认网卡: ${CYAN}${DEFAULT_IFACE}${NC} ✓"
# ======================== 5. 生成三组密钥对 ========================
info "生成密钥对..."
mkdir -p "$KEY_DIR"
chmod 700 "$KEY_DIR"
generate_keypair() {
local name=$1
local priv_file="${KEY_DIR}/${name}_private.key"
local pub_file="${KEY_DIR}/${name}_public.key"
if [[ -f "$priv_file" && -f "$pub_file" ]]; then
warn "${name} 密钥对已存在,跳过生成"
else
wg genkey | tee "$priv_file" | wg pubkey >"$pub_file"
chmod 600 "$priv_file"
info "${name} 密钥对已生成 ✓"
fi
}
generate_keypair "server"
generate_keypair "phone"
generate_keypair "jump"
# 读取密钥内容
SERVER_PRIV=$(cat "${KEY_DIR}/server_private.key")
SERVER_PUB=$(cat "${KEY_DIR}/server_public.key")
PHONE_PRIV=$(cat "${KEY_DIR}/phone_private.key")
PHONE_PUB=$(cat "${KEY_DIR}/phone_public.key")
JUMP_PRIV=$(cat "${KEY_DIR}/jump_private.key")
JUMP_PUB=$(cat "${KEY_DIR}/jump_public.key")
# ======================== 6. 获取服务器公网 IP ========================
info "获取服务器公网 IP..."
SERVER_PUBLIC_IP=$(curl -s4 ifconfig.me || curl -s4 icanhazip.com || curl -s4 ip.sb || echo "")
if [[ -z "$SERVER_PUBLIC_IP" ]]; then
warn "无法自动获取公网 IP,配置文件中将使用占位符 <YOUR_SERVER_PUBLIC_IP>"
SERVER_PUBLIC_IP="<YOUR_SERVER_PUBLIC_IP>"
else
info "公网 IP: ${CYAN}${SERVER_PUBLIC_IP}${NC} ✓"
fi
# ======================== 7. 生成 Server 配置文件 ========================
info "生成 Server 配置文件..."
cat >"${WG_DIR}/wg0.conf" <<EOF
[Interface]
PrivateKey = ${SERVER_PRIV}
Address = ${SERVER_IP}/24
ListenPort = ${WG_PORT}
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ${DEFAULT_IFACE} -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ${DEFAULT_IFACE} -j MASQUERADE
# --- Peer: Phone ---
[Peer]
PublicKey = ${PHONE_PUB}
AllowedIPs = ${PHONE_IP}/32
# --- Peer: Jump ---
[Peer]
PublicKey = ${JUMP_PUB}
AllowedIPs = ${JUMP_IP}/32,${LAN_SUBNETS}
EOF
chmod 600 "${WG_DIR}/wg0.conf"
info "Server 配置已写入 ${WG_DIR}/wg0.conf ✓"
# ======================== 8. 生成 Phone 配置文件(供导入小火箭) ========================
info "生成 Phone 配置文件..."
cat >"${WG_DIR}/phone.conf" <<EOF
[Interface]
PrivateKey = ${PHONE_PRIV}
Address = ${PHONE_IP}/24
DNS = 1.1.1.1, 8.8.8.8
[Peer]
PublicKey = ${SERVER_PUB}
Endpoint = ${SERVER_PUBLIC_IP}:${WG_PORT}
AllowedIPs = ${WG_SUBNET}.0/24,${LAN_SUBNETS}
PersistentKeepalive = 25
EOF
chmod 600 "${WG_DIR}/phone.conf"
info "Phone 配置已写入 ${WG_DIR}/phone.conf ✓"
# ======================== 9. 生成 Jump 配置文件 ========================
info "生成 Jump 配置文件..."
cat >"${WG_DIR}/jump.conf" <<EOF
[Interface]
PrivateKey = ${JUMP_PRIV}
Address = ${JUMP_IP}/24
MTU = 1420
# 注意:如果跳板机的网卡名不同,请手动修改下面的网卡名
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ${DEFAULT_IFACE} -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ${DEFAULT_IFACE} -j MASQUERADE
[Peer]
PublicKey = ${SERVER_PUB}
Endpoint = ${SERVER_PUBLIC_IP}:${WG_PORT}
AllowedIPs = ${WG_SUBNET}.0/24
PersistentKeepalive = 25
EOF
chmod 600 "${WG_DIR}/jump.conf"
info "Jump 配置已写入 ${WG_DIR}/jump.conf ✓"
# ======================== 10. 输出汇总信息 ========================
echo ""
echo -e "${CYAN}======================================${NC}"
echo -e "${CYAN} 部署完成!配置汇总${NC}"
echo -e "${CYAN}======================================${NC}"
echo ""
echo -e " 默认网卡: ${GREEN}${DEFAULT_IFACE}${NC}"
echo -e " 监听端口: ${GREEN}${WG_PORT}/UDP${NC}"
echo -e " 隧道网段: ${GREEN}${WG_SUBNET}.0/24${NC}"
echo -e " 公网 IP: ${GREEN}${SERVER_PUBLIC_IP}${NC}"
echo ""
echo -e " ${CYAN}[IP 分配]${NC}"
echo -e " Server: ${GREEN}${SERVER_IP}${NC}"
echo -e " Phone: ${GREEN}${PHONE_IP}${NC}"
echo -e " Jump: ${GREEN}${JUMP_IP}${NC}"
echo ""
echo -e " ${CYAN}[配置文件位置]${NC}"
echo -e " Server: ${GREEN}${WG_DIR}/wg0.conf${NC} ← 本机使用"
echo -e " Phone: ${GREEN}${WG_DIR}/phone.conf${NC} ← 导入小火箭"
echo -e " Jump: ${GREEN}${WG_DIR}/jump.conf${NC} ← 拷贝到跳板机"
echo ""
echo -e " ${CYAN}[密钥文件位置]${NC}"
echo -e " ${GREEN}${KEY_DIR}/${NC}"
echo ""
echo -e " ${CYAN}[下一步操作]${NC}"
echo -e " 1. 启动 Server: ${YELLOW}sudo wg-quick up wg0${NC}"
echo -e " 2. 开机自启: ${YELLOW}sudo systemctl enable [email protected]${NC}"
echo -e " 3. 将 ${GREEN}jump.conf${NC} 拷贝到跳板机的 ${GREEN}/etc/wireguard/wg0.conf${NC}"
echo -e " 注意修改配置中的网卡名为跳板机实际的网卡名"
echo -e " 4. 根据 ${GREEN}phone.conf${NC} 的内容到小火箭中配置WireGuard"
echo -e " 5. 在小火箭中添加分流规则: ${YELLOW}${WG_SUBNET}.0/24${NC} 和 ${YELLOW}${LAN_SUBNETS}${NC}"
echo ""
echo -e "${CYAN}======================================${NC}"
echo -e "${GREEN} 记得在云主机安全组中放行 UDP ${WG_PORT} 端口!${NC}"
echo -e "${CYAN}======================================${NC}"
echo ""脚本支持通过环境变量自定义参数,例如:
# 自定义端口和局域网网段
sudo WG_PORT=51820 LAN_SUBNETS="192.168.1.0/24,192.168.50.0/24" bash wireguard_setup.sh