贫穷再一次激起了人的创造力
上一次:[技术杂谈]低成本windows远程控制直连方案(无需VPN组网)
前言
这次事情的起因是最近换了新的传输协议xhttp,三台电脑上平时在用的xray和v2ray内核都成功跑通了,本以为可以在我的16e上愉快的使用时,却发现自己的小火箭内核版本过低,传输协议选项中没有xhttp
那么看到这解决办法肯定就是要去更新,但由于我的apple id目前在国区,要更新就得换到当时买火箭的美区。但又因为我在国区有apple music,此时如果换区就会导致我的资料库被删掉,就此触发音乐圈经典老番🤣👉

我可不想自己整理了大半年的音乐库凭空消失,所以换区这条路肯定是不行了。此时肯定有聪明的观众会说可以换一个在美区并且有火箭的apple id,但这又会导致我apple钱包里的卡被全部解绑,换回我自己的国区id时得一张一张重新绑。更重要的是,必须是”有火箭“的美区id,相当于我又得重新花二十多块钱买个火箭,或者还得找人去借一个

所以,求人不如求己,一个黑客最基本的素养就是要想办法花最少的钱办最大的事,就有了这篇博客
(另外顺便解释一下为什么需要小火箭这类网卡层代理,而不使用无线网络连接时设置里的http/socks5代理?原因其实很简单,一是DNS泄漏的问题(可以看[技术杂谈]一篇聊完DNS投毒、泄露),二是iphone上绝大多数应用会无视这个设置导致仍然无法使用)
需要准备
一台闲置linux服务器。用来跑xray/v2ray/singbox/clash等各种代理内核,我用的是家里的一台闲置电脑。如果你没有,也可以随便拿台vps,不过最好不要是国内厂商的,原因你懂的
(选用)一个公网ipv4地址。最好的办法就是随便找台带公网v4地址的vps,我自己正好手头有台闲置的阿里云。这台vps就可以是国内厂商,并且最好是在国内,因为是出门在外做转发会用到,下文会介绍原因以及为什么不能是ipv6。
当然,如果你只是想在自己家里科学上网,并且你的iOS设备可以和你的闲置电脑在一个网段下,那完全可以不需要这个公网v4地址。
设计思路
因为iOS本身支持的vpn协议类型有限,所以必须要搭建一个符合其所支持协议的vpn服务,并将它的流量统一转发给代理内核本身提供的的socks5,因此就可以通过连接路径粗略总结出搭建路径
iphone -> vpn服务(用docker搭建并创建专属网段) [ ->编写流量规则,在防火墙上把vpn服务接到的流量做标记 -> linux内核将做过标记的流量根据路由表发给特定网卡 -> 网卡转交给socks5 ] ->xray/v2ray/singbox提供的socks5代理
开始
先来介绍一下iOS自带的VPN功能。就拿我目前正在使用的iOS 18举例,一般来说,如果你此前从未使用过类似小火箭这样的软件,那么自带VPN入口大概率会在"设置"->“通用”->“VPN与设备管理"底下

如果你用过,那么在网络连接相关设置附近应该也能看到

在添加VPN配置中查看可添加的VPN类型,可以发现只支持这三种

其实第一种和第三种他们本质上都是第二种IPsec的"套壳”,从传输特征和加解密复杂度上看没有太大区别,只不过在验证逻辑和配置上略有不同。我们待会儿会用到第三种"L2TP"
来到我准备好的闲置linux服务器上,先配置好自己的代理内核,可以是你的clash,又或者v2ray等等,这里我就不做介绍了,直接拿准备好的代理测试一下
成功代理到了我的美中节点,那么此时我们的目标其实很清晰,就是想办法让我的iphone通过自带vpn所支持的协议与我的服务器建立链接,并将我iphone上的请求全部转交到服务器的socks5代理上
也就是我们需要搭建一个vpn服务端供我的iphone连接
实现在家可用
因为我不想脏了自己的环境,毕竟我的服务器还要跑别的东西,同时为了方便后续做流量规则,所以我选择用把vpn服务架在docker。先给我的vpn服务创建一个专属网段
docker network create --subnet=172.20.0.0/16 vpn_net
接着直接用别人包好的hwdsl2/ipsec-vpn-server镜像即可一键部署。如果对vpn有研究的观众并且用过这个镜像可能会有问为什么不设置固定的dns,以防dns泄露?其实如果不设置就可以将dns请求也一并交给代理节点,也就没有dns泄漏的问题
docker run -d --name ikev2-vpn \
--restart=always \
--privileged \
--net vpn_net \
--ip 172.20.0.2 \
--name vpn\
-p 500:500/udp \
-p 4500:4500/udp \
-e "VPN_IPSEC_PSK=预共享密钥(随便填)" \
-e "VPN_USER=用户名" \
-e "VPN_PASSWORD=密码" \
hwdsl2/ipsec-vpn-server
如果成功,你应该能在日志里看到类似这样的输出

此时如果你拿着下面的输出信息去你的iphone上连是没有问题的,不过我们现在只完成了最基本的vpn隧道搭建,也就是只完成了你的iphone到服务器这一步,服务器还并没有把它接收到的都数据转交给socks5代理,所以接下来我们需要用到另一个工具tun2socks。这个工具可以虚拟出一张网卡,并将这张网卡上的流量转交给指定的socks5代理
可以直接wget最新版
wget https://github.com/xjasonlyu/tun2socks/releases/download/v2.6.0/tun2socks-linux-amd64.zip
下完后在服务器上解压,并将他放到你的PATH所及之处。比方说我的PATH现在包括了/usr/bin,我可以就把它放到那里,方便后续调用
mv tun2socks-linux-amd64 /usr/bin/tun2socks
chmod +x /usr/bin/tun2socks
因为我知道一会儿自己要用tun2socks起一张叫tun0的网卡,并且已经在启用vpn服务容器时知道了对应的ip地址,所以我们可以先来写防火墙的分流规则。这里就用我比较熟悉的nftables来写
vim /etc/nftables_vpn.conf
table ip vpn_divert {
chain prerouting {
type filter hook prerouting priority mangle; policy accept;
udp sport { 500, 4500 } return
ip saddr != 172.20.0.2 return
ip daddr { 10.10.0.0/16, 192.168.0.0/16 } return #这里要根据你的实际情况改,比如放行192.168的内网段就能实现异地访问家里的其他服务等
meta mark set 0x00000001
}
}
核心规则其实是这句
meta mark set 0x00000001
将从172.20.0.2流出的所有数据包都标记一个1
写完分流规则,我们需要先对系统配置做一点调整,比如让linux内核开启转发、识别被nft标记为“1”的数据包、对tun2socks将创建的tun0网卡关闭RP过滤等。把它写到一个脚本里
vim /usr/bin/vpn-routing.sh
#!/bin/bash
sysctl -w net.ipv4.ip_forward=1
echo 0 > /proc/sys/net/ipv4/conf/tun0/rp_filter
ip route add default dev tun0 table 100 2>/dev/null || true
ip rule add fwmark 1 lookup 100 2>/dev/null || true
nft -f /etc/nftables_vpn.conf
为了方便,我选择写一个systemd服务来管理tun2socks和上面的脚本
vim /etc/systemd/system/tun2socks.service
[Unit]
Description=Tun2Socks
After=network.target
[Service]
Type=simple
ExecStartPost=/usr/bin/vpn-routing.sh
ExecStartPre=-/usr/sbin/ip tuntap add dev tun0 mode tun
#启动前给tun0分配一个假ip,防止系统不认
ExecStartPre=/usr/sbin/ip addr add 10.10.10.1/24 dev tun0
ExecStartPre=/usr/sbin/ip link set tun0 up
ExecStart=/usr/bin/tun2socks --device tun0 --proxy socks5://127.0.0.1:10808
ExecStopPost=-/usr/sbin/ip link set tun0 down
ExecStopPost=-/usr/sbin/ip tuntap del dev tun0 mode tun
Restart=always
[Install]
WantedBy=multi-user.target
最后重载systemd并运行,再重启docker容器即可
systemctl daemon-reload
systemctl enable --now tun2socks
docker restart vpn
此时回到iphone的vpn设置,选择"L2TP",按以下填入
- 描述:随便
- 服务器:服务器内网ip地址
- 账户:启动容器时填的用户名
- RSA SecurID:保持关闭
- 密码:启动容器时填的密码
- 密钥:启动容器时填的预共享密钥
- 发送所有流量:保持开启
- 代理:关闭
开启连接即可使用


实现出门可用
即然在内网可以访问,剩下的其实就是穿透的事而已,而穿透就需要用到准备材料里所说的公网ipv4。这里就拿我自己的情况,有一台国内的闲置带公网v4的vps举例子
先来看看最理想的情况

我们的手机在外想要连上vps很容易,因为它有公网地址。主要配置在于公网到家里这段的穿透配置
我们的目标有两个
- 可以正常使用
- 不被运营商掐掉
要做到第一点很容易,现在主流的frp类软件都能做到。但我不是很喜欢这玩意,因为要写配置文件。所以我选择用我更熟悉的gost
至于第二点,为什么运营商会掐掉?是因为你在用VPN吗?也许是,各个地方运营商对具有明显VPN特征的流量管制严格度并不同。但不管怎么说因为我的服务器也在国内,图中流量两端终点也都是国内地址,并不跨境,运营商也只能知道你在搞VPN但看不到你具体在做什么,所以更大的原因其实在于IPsec协议本身基于udp。一旦你出门后想上yt看个无损4k视频,或者下个大文件,动辄就是几个G甚至几十个G的udp在运营商面前跑,在你看来也许很正常,但在运营商看来这跟你在发动你家宽带对着你的vps打ddos并没有什么区别🫵
所以,为了让被掐的概率减少一点,我们就不能让运营商看着我们的裸VPN流量在他面前来回跑,而是要将它伪装成一种基于TCP,且在绝大多数时候确实会用来进行流媒体传输或大文件下载的传输协议:Websocket
思路有了,实践起来就容易了。因为我选择使用gost,只要在家里的服务器和vps两端都启用wss://协议(纯websocket其实是ws://,wss://是websocket over TLS,看起来更正常,而且非明文就更检测不到你是在跑VPN)即可
在两台机子上都下载gost,在vps上执行(用户名密码端口号随便设置)
./gost -L socks5+wss://用户名:密码@:端口?bind=true&udp=true
在家里的服务器执行
./gost -L rudp://:500/127.0.0.1:500 -L rudp://:4500/127.0.0.1:4500 -F socks5+wss://用户名:密码@ip地址:端口
此时我们的流量传输也就变成了下面这样

最后将iphone VPN设置中"服务器"的ip地址改为vps的即可
至于从手机到vps这段,并不需要担心裸奔出问题,因为除了偶尔用一用一些地方的wifi以外,更多的使用场景其实是用手机卡的流量,而运营商对待这就和前面讲的家宽完全是两套逻辑了,就我自己的联通5G卡,说好的5G,平峰期都跑不到100Mbps😑,人家压根懒得鸟我这买来就限速的扣搜穷鬼套餐……