理论上将适合任何架构,任何系统的linux
网络拓扑
+-----------+ +------------+ +------------+
| 虚拟机 | HostOnly | GatewWay | 共享上网 | 宿主机 | wifi
|10.129.37.5|----------------|10.129.3.1 |----------------|10.130.2.1 |----------------INTERNET
| | eth1 |10.130.2.1 | eth0 | |
+-----------+ +------------+ +------------+
虚拟机和Gateway都运行在虚拟机中,虚拟机器将默认网关设置为GateWay。
最终目标是,将HostOnly的所有网络流量,都通过代理的形式,发送到远程服务器。让虚拟机认为自己处在远程网络中。防止出现意外(例如暴露真实ip)
注意,以下如无说明,都在GateWay中操作
内核的转发功能
处于安全考虑,Linux默认未允许转发目的地不是本机的数据包,需要在/etc/sysctl.conf中写入 net.ipv4.ip_forward = 1以开启转发,完成后需要执行sysctl -p刷新。
透明代理配置
主要有两种配置方法,分别是通过wireguard配置和x2ray。注意,为了保证速度,HostOnly网关应该设置为gateway
wireguard
wg-quick up wg0 # 启动wireguard 配置 wg启动一个网卡,并设置系统的默认路由到wg0接口
sudo iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE # 配置出口网卡为wg0
通过这种方式可以快速将默认网关的流量全部代理到wg中,出口为国外网络
然后再安装dnscrypt-proxy
记得在/etc/dnscrypt-proxy中操作,dnscrypt-proxy生成system 会把当前目录作为当前配置文件中的工作目录
dnscrypt-proxy -service install
dnscrypt-proxy -service start
把配置文件中,监听0.0.0.0:53 即可
配置开机自连接wireguard
针对iptables项目开机自恢复,参考x2ray下面的那个配置就可以
-
在 /etc/systemd/system/ 目录下创建一个名为 wg.service 的文件,然后添加以下内容并保存。
[Unit]
Description=Tproxy rule
After=network.target
Wants=network.target
[Service]
Type=oneshot
#注意分号前后要有空格
ExecStart=/usr/bin/wg-quick up wg0 ; /sbin/iptables-restore /etc/iptables/rules.v4
[Install]
WantedBy=multi-user.target
-
执行下面的命令使 wg.service 可以开机自动运行。
systemctl enable wg
x2ray部分
在这里把vmess改成你自己的真实服务器,其他不要动,尤其是sockopt部分。sockopt部分同时也添加到你自己的vmess服务器出口配置中
wireguard via X2ray
这样可以解决wireguard特征过于明显,利用x2ray加速的双重特性。同时也实现了双重代理
先决知识
我们先来了解以下wireguard全局代理的原理 在ubuntu系统上为了实现策略路由,配合iptables的mark,引入了ip list。首先会根据ip报文的标签,决定具体转发到哪张路由表。也就是说系统同时有了很多路由表。我们看一下启动wireguard后的系统的路由表和策略表。
在Linux里,总共可以定义232个优先级的规则,一个优先级别只能有一条规则,即理论上总共可以有条规则。其中有3个规则是默认的0:匹配任何条件,查询路由表local(ID 255),该表local是一个特殊的路由表,包含对于本地和广播地址的优先级控制路由。rule 0非常特殊,不能被删除或者覆盖。
32766:匹配任何条件,查询路由表main(ID 254),该表是一个通常的表,包含所有的无策略路由。系统管理员可以删除或者使用另外的规则覆盖这条规则。
32767:匹配任何条件,查询路由表default(ID 253),该表是一个空表,它是后续处理保留。对于前面的策略没有匹配到的数据包,系统使用这个策略进行处理,这个规则也可以删除。
注:不要混淆路由表和策略:规则指向路由表,多个规则可以引用一个路由表,而且某些路由表可以策略指向它。如果系统管理员删除了指向某个路由表的所有规则,这个表没有用了,但是仍然存在,直到里面的所有路由都被删除,它才会消失。
在默认情况下进行路由时,首先会根据规则0 在本地路由表里寻找路由,如果目的地址是本网络,或是广播地址的话,在这里就可以找到合适的路由;如果路由失败,就会匹配下一个不空的规则,在这里只有32766规则,在这里将会在主路由表里寻找路由;如果失败,就会匹配32767规则,即寻找默认路由表。如果失败,路由将失败。重这里可以看出,策略性路由是往前兼容的。
Let’s start from the 32764 rule: as it has a lower number, it’s considered first.
32764: from all lookup main suppress_prefixlength 0
The rule has no selector, making the kernel consult the main table for every single packet. If this was the whole rule, every packet would be routed by the main table, never reaching the VPN. This is why the action also contains a suppressor: suppress_prefixlength 0. From the ip-rule(8) man page
suppress_prefixlength NUMBER
reject routing decisions that have a prefix length of NUMBER or less.
也就是查表并拒绝包。直连网段的路由表,可以被查询到,而且掩码肯定不为0.那么正常转发。但是如果是默认路由的话,那么查询后的掩码肯定0,那么就拒绝转发。suppress的意思是抑制。
Here “prefix” refers to the address or range of addresses matched in the routing table. So if you have a route for 10.2.3.4, its prefix length is 32 (bits); if you change it to 10.0.0.0/8, the prefix length will be 8. What is a prefix of length 0 or less? It’s the empty prefix, 0.0.0.0/0, corresponding to the default route. So if the packet was routed by the default route from main, that routing decision is ignored; otherwise, it’s respected. To summarize, the effect of this rule is to respect all manual routes that the administrator might have added to the main table. However, if the packet didn’t match any of the specific routes, then instead of applying the default route, we’re proceeding to the next rule.
“not from all fwmark 0xca6c lookup 51820”的意思是说,满足条件“from all fwmark 0xca6c“(wireguard发出的都带fwmark 0xca6c )请忽略本条规则,继续往下走,即peer的endpoint地址会走main路。否则,请使用51820路由表,通过wg隧道出去。
对于wg接口发包自带的0xca6c,继续走下一条规则,也就是匹配默认路由表,从eth0接口发送出去。
现在我们了解一下X2ray的iptables配置原理
根据iptables五链三表的顺序规则,假如一个包从本机发出,那么首先会经过OUTPUT链,在这里的mangle表,为tcp,udp的报文打上标签为1。也就是下面两条命令
iptables -t mangle -A X2RAY_MASK -p udp -j MARK --set-mark 1 # 给 UDP 打标记,重路由
iptables -t mangle -A X2RAY_MASK -p tcp -j MARK --set-mark 1 # 给 TCP 打标记,重路由
给 OUTPUT 链的 TCP 和 UDP 打个标记 1(OUTPUT 应用 X2RAY_MASK 链)。由于 Netfilter 的特性,在 OUTPUT 链打标记会使相应的包重路由到 PREROUTING 链上,在已经配置好了 PREROUTING 相关的透明代理的情况下,OUTPUT 链也可以透明代理了。其实打标签,打什么无所谓,重要的是让数据包重路由
所以在PREROUTING链上,会有下面两条规则。
iptables -t mangle -A X2RAY -p udp -j TPROXY --on-ip 127.0.0.1 --on-port 12345 --tproxy-mark 1 # 给 UDP 打标记 1,转发至 12345 端口
iptables -t mangle -A X2RAY -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port 12345 --tproxy-mark 1 # 给 TCP 打标记 1,转发至 12345 端口
只要从PREROUTING进来的包,就给打个1,然后修改数据包的目的ip到12345,也就是我们x2ray代理的地址。打完标签后,并修改目的端口,并不会路由。但是这时候会开始匹配策略路由,所以这时候我们要加一条关于标签1的策略路由,带有标签1的数据包转发到本地。这样,被修改目的端口的数据都会让本机处理,也就是尝试连接本地的12345端口。这时候监听12345即可(也就是x2ray的任意门) 相关文档:https://www.kernel.org/doc/Documentation/networking/tproxy.txthttps://ipset.netfilter.org/iptables-extensions.man.html
ip rule add fwmark 0xca6c table 101
ip route add local 0.0.0.0/0 dev lo table 101
如何配置
在连接wg0的基础上,既然wg0接口自己已经加标签了(0xca6c),那么我们在OUTPUT处,将已经加0xca6c的报文,目的重新修改标签为1,触发报文的重路由,也就是重新到PREROUTING处。当然 x2ray的出口标签是FF,我们要放行。
iptables -t mangle -I OUTPUT 1 -p udp -j MARK --set-mark 1 -m mark --mark 0xca6c
iptables -t mangle -A OUTPUT -j RETURN -m mark --mark 0xff
然后添加一条策略路由,针对标签1的报文,交给本地处理,
ip rule add fwmark 1 table 101
ip route add local 0.0.0.0/0 dev lo table 101
现在wireguard的报文 都会走x2ray了。但是x2ray的报文却不会出去了,因为他的标签是0xff,根据上面我们解释的规则,又会继续转发到wg0口。相当于路由环路。所以我们一定要在wg默认策略前面,新增0xff报文转发到main
ip rule add fwmark 0xff table main
现在针对PREROUTING进入的报文,有两种情况,第一别的机器发来的报文,对于这种我们不要做任何处理,因为给wireguard,让他做路由交换。第二就是刚才重路由的,我们转发到本机的12345,利用上面的策略路由
iptables -t mangle -I PREROUTING 1 -j TPROXY -p tcp --on-port 12345 --tproxy-mark 1 -m mark --mark 1
iptables -t mangle -I PREROUTING 1 -j TPROXY -p udp --on-port 12345 --tproxy-mark 1 -m mark --mark 1
最终策略路由是这个样子的
root@router:/home/lzb# ip rule list
0: from all lookup local
32761: from all fwmark 0xff lookup main
32762: from all fwmark 0x1 lookup 101
32763: from all lookup main suppress_prefixlength 0
32764: not from all fwmark 0xca6c lookup 51820
32765: from all fwmark 0x1 lookup 100
32766: from all lookup main
32767: from all lookup default
iptables是这样的
root@router:/home/lzb# iptables -t mangle -L -v -n
Chain PREROUTING (policy ACCEPT 55789 packets, 60M bytes)
pkts bytes target prot opt in out source destination
4521 1297K TPROXY udp -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x1 TPROXY redirect 0.0.0.0:12345 mark 0x1/0xffffffff
0 0 TPROXY tcp -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x1 TPROXY redirect 0.0.0.0:12345 mark 0x1/0xffffffff
38039 41M CONNMARK udp -- * * 0.0.0.0/0 0.0.0.0/0 /* wg-quick(8) rule for wg0 */ CONNMARK restore
Chain INPUT (policy ACCEPT 39693 packets, 42M bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 20286 packets, 20M bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 27519 packets, 22M bytes)
pkts bytes target prot opt in out source destination
4521 1297K MARK udp -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0xca6c MARK set 0x1
6628 1567K RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0xff
Chain POSTROUTING (policy ACCEPT 47805 packets, 42M bytes)
pkts bytes target prot opt in out source destination
2011 574K CONNMARK udp -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0xca6c /* wg-quick(8) rule for wg0 */ CONNMARK save
ip 策略路由在INPUT OUTPUT中间,和FORWARD在一起
参考https://sleeplessbeastie.eu/2018/06/21/how-to-create-iptables-firewall-using-custom-chains/
原文始发于微信公众号(宽字节安全):Linux多跳透明网关配置