使用 clash 和路由表實現透明代理

本文內容如有錯誤,請在評論區指出

要進行全局代理,常見的方式是轉發到特定端口、使用 tun 虛擬設備和使用 TPROXY。clash 有 redir port 可以直接轉發實現代理,但是隻支持 TCP,IPv6 的支持也尚未合併。TPROXY 是 v2ray-core 的推薦方式。而另一種方式則是 tun。本文介紹的是使用 tun 的方式

要使用這種方式,首先需要一個使用 tun 轉發流量的工具,目前 clash 主線並不支持 tun,clash 作者有一個支持 tun 的閉源版本,另外 comzyh 有一個支持 tun 的分支,性能不錯,穩定性一般。然後有一個通用的工具 tun2socks,穩定性較好但性能一般。後兩者都不錯。

comzyh 的 clash 分支

go-tun2socks

基本的路由表、iptables 操作

首先需要使用 ip tuntap add <tun name> mode tun user <tun user> 創建一個 tun 設備,並使用 ip link set <tun name> up 啓用。

然後設置路由 ip address replace <tun address> dev <tun name>,tun address 取一個不常用的 IP 段,比如 kr328 的腳本中使用 172.31.255.253/30,又比如 go-tun2socks 默認的 10.255.0.1/24。

然後設置路由規則

1
2
ip route replace default dev <tun name> table <route table id>
ip rule add fwmark <fwmark id> lookup <route table id>

把有特定 fwmark 標記的流量路由到 tun 其中 fwmark 號和 route 表號可以自行設定(不要和其他的規則重複)。

然後用 iptables 在 mangle 表上,把需要代理的流量打上 fwmark 標記,使用 -j MARK --set-mark <fwmark id> 即可

利用 iptables 支持的豐富規則,我們可以靈活地繞過各種流量,列出幾個比較常用的:

  • -m owner --uid-owner <username or uid> 匹配某個用戶的流量,類似的還有 --gid-owner
  • -p <'tcp', 'udp' or 'icmp'> 匹配某種類型流量
  • --dport <port num> 匹配目的端口,類似的 --sport 匹配源端口,需要和 -p tcp 等連用
  • -d <network name, hostname, subnet(IP CIDR) or IP> 匹配目的網絡/主機名/子網/IP
  • -m set --match-set <ipset name> <'dst' or 'src'> 匹配 ipset,可以用於簡化規則,ipset 用法自行搜索
  • -m cgroup --cgroup <cgroup id> 匹配 cgroup

有較多規則時可以創建規則鏈,並把 OUTPUT 或 PREROUTING 鏈中的流量轉入該鏈(ip6tables 也一樣):

1
2
3
iptables -t mangle -N <chain name>
iptables -t mangle -F <chain name>
iptables -t mangle -I OUTPUT -j <chain name>

然後在規則鏈上編寫規則即可,需要打標記走代理就 -j MARK --set-mark <fwmark id>,需要繞過就 -j RETURN,一般建議先寫繞過規則,最後無條件地打標記。注意一定要繞過 clash 的流量(建議 uid/gid 或 cgroup)

cgroup 是 Linux 內核的一個功能,這裏需要用到它的 net_cls 子系統。使用起來簡單,我們首先運行以下命令創建一個組:

1
2
3
mkdir -p /sys/fs/cgroup/net_cls/<cgroup name>
echo <cgroup id> > /sys/fs/cgroup/net_cls/bypass_proxy/net_cls.classid
chmod 666 /sys/fs/cgroup/net_cls/<cgroup name>/tasks

上述命令可以在開機時運行

這樣,如果一個進程的 pid 在 /sys/fs/cgroup/net_cls/<cgroup name>/tasks 中,它就會被 iptables 的 -m cgroup --cgroup <cgroup id> 規則匹配到,並且這些進程的子進程 pid 會自動被添加。如果對安全比較敏感,你可以對該文件進行適當的權限控制。

可以使用一個腳本,以它作爲 wrapper 來運行需要的命令

1
2
3
#!/bin/bash
echo $$ > /sys/fs/cgroup/net_cls/<cgroup name>/tasks
exec "$@"

與 v2ray 不同,clash 不能通過其規則把流量轉到 clash 的內置 DNS,且 clash 不支持 sni 解析域名,需要內置 DNS 反查域名。

因此,需要使用 nat 表把 DNS 流量轉發到 clash 的 DNS 端口,對 UDP 53 端口的流量,設置適當的繞過規則過濾後,-j REDIRECT --to-ports <clash dns port> 即可。

comzyh 的 clash 分支需要在配置文件中設置以下內容

1
2
3
tun:
  enable: true
  device-url: dev://<tun name>

go-tun2socks 用以下命令啓動

tun2socks -tunName <tun name> -proxyServer <clash socks server> -tunAddr <tun address> -tunGw <tun gateway> -tunMask <tun mask> -tunPersist -blockOutsideDns

選一個不常用的 subnet,並取該 subnet 內兩個不同地址作爲 tun gateway 和 tun address。

比如要使用 172.31.255.253/30 子網。則 tun mask 爲 255.255.255.252,tun gateway 可以用 172.31.255.253,tun address 可以用 172.31.255.254,默認參數對應的子網是 10.255.0.1/24