Linux多跳透明網關配置
理論上將適合任何架構,任何系統的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/