0x00 前言

nuc用了一段时间了,路由功能还是很稳定,用之前的方案做透明代理还是挺折腾的,一直搁置了。

不过最近找到了一个裸延迟20ms的vps,于是打算开始折腾透明代理。

网络:openvpn+kcptun

路由:iptables+iproute2+xtables(geoip)

DNS: dnsmasq

0x01 基础网络

vps的延迟本身还是很低的,但是由于某些原因,tcp的连接还是很不稳定,所以加了一层kcptun( apt install kcptun 安装) 服务端运行:

1
kcptun-server -t "127.0.0.1:1194" -l ":1234" -mode fast3 -nocomp -sockbuf 16777217 -dscp 46

将udp的1234端口映射到1194(openvpn)

客户端(本地路由器)运行:

1
kcptun-client -r "{vps ip}:1234" -l "127.0.0.1:1024" -mode fast3 -nocomp -autoexpire 900 -sockbuf 16777217 -dscp 46

这样就将本地的1024端口映射到vps的1194端口了

然后就是openvpn的配置,相关细节比较复杂,这里就不展开了,只是说结果。在openvpn连接好之后,在两端都会有一个tap设备(虚拟网卡),设备的地址在同一个网段之内,可以直接Ping通,这时候可以把vps看做一个直接连到本地路由器(nuc)的网关了。如果我们把本地的默认路由设置为vps(tap端的地址),并在在vps做了nat,就可以实现全部流量走vps了。

0x02 路由

这里我们需要根据数据包的目的地进行路由,国内的走我们正常的网关(光猫),国外的走vps。可以用netfilter的一个扩展 Xtables-addons 来实现,装好xtables之后,iptable就多了一项geoip的模块,以识别ip地址位置。然后需要下载geoip数据并构建,以便geoip模块查询,这里不展开了。iptables命令:

1
2
iptables -t mangle -A OUTPUT -m geoip ! --destination-country CN -j MARK --set-mark 10
iptables -t mangle -A PREROUTING -m geoip ! --destination-country CN -j MARK --set-mark 10

这样就给所有目的地不是国内的包打上了标签10(前一条用于nuc,第二条用于内网设备),接下来就可以跟进标签来查表。

1
2
ip rule add fwmark 10 lookup 10
ip route add default via 10.8.0.1 table 10

第一条创建了一个编号为10的路由表,所有被mark上10的数据包都会查询这个路由表,第二条增加一条默认路由,所有走这个路由表的数据包都转发到10.8.0.1去(vps端tap设备地址)

基础路由配置就是这样,按需求自行添加。需要注意的是要关掉 rp_filter 内核选项,在 sysctl.conf 中配置:

1
2
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.all.rp_filter=0

否则内核会丢弃掉从tap设备回来的包。

到此为止,所有连着nuc的设备访问国外网址的时候,都会直接走vps。

0x03 DNS

当我们访问Google的时候,首先会去查询dns,如果是国内的dns,很可能会被污染或者指向一个奇怪的地方,所以很自然的我们会用 8.8.8.8 之类的dns。但是这样会有一个问题,我们访问 8.8.8.8 的时候,是通过vps访问的,这时候解析出来的地址都是基于国外的地址。如果我们访问一个国内网站,返回的结果可能是该网站一个国外的cdn,导致速度变慢。

这里就需要做dns分流,默认用国内的dns,国外的网站走8.8.8.8,对每一个需要8.8.8.8解析的域名,在dnsmasq里面加入配置:

1
2
3
4
5
6
7
server=/google.com/8.8.8.8
server=/facebook.com/8.8.8.8
server=/twitter.com/8.8.8.8
server=/steamcommunity.com/8.8.8.8
server=/googlevideo.com/8.8.8.8
server=/youtube.com/8.8.8.8
server=/github.com/8.8.8.8

这里我就手动添加了,如何自动导入啥的等后面有时间再看吧。

0x04 DDNS

在配置的透明代理之后,如果ddns客户端是通过web之类的方法获取公网ip,并且web的地址是个国外地址,就会导致ddns被设置成vps的地址。解决办法就是将web获取服务器设置为国内的某个地址,或者vps本身。这里就用后者,直接在vps的nginx配置:

1
2
3
4
location /get_ip {
		default_type text/plain;
		return 200 "$remote_addr\n";
	}