用 Firewalld + systemd.slice + redir 實現 TCP + UDP 透明代理

https://matters.news/@whisper/%E7%94%A8-firewalld-systemd-slice-redir-%E5%AF%A6%E7%8F%BE-tcp-udp-%E9%80%8F%E6%98%8E%E4%BB%A3%E7%90%86-bafyreig5keicgrjoall54ujagzxkd6ocmdoekbnoov7d3rzeicq3xcqv3y

现在网上已经有不少用 iptables 或 nftables 实现透明代理(Tproxy)的例子了。绝大多数关于透明代理的教程都是通过 iptables 实现的,也有少量使用新一代的 nftables 内核防火墙前端来实现。然而,消费级电脑上通常会装有更简化的防火墙程序,例如 Fedora/CentOS/SUSE 自带的 Firewalld,或 Ubuntu 的 UFW。在用这些简化前端的同时,直接操作 nftables 和 iptables 的话可能会造成冲突,更好的解法是全部工作都由 firewalld/ufw 来做。

本文就以 firewalld 为例,用它的 direct rules 来实现透明代理(没错,firewalld 并不提供关于透明代理的抽象,所以并不比 iptables 有优势)。

想要实现的效果

  • TCP 和 UDP 流量自动转给 Clash(或 shadowsocks 等)
  • Clash 本身不会把流量转发给自己
  • 可以让某些 程序 不转发给 clash,直接联网

Get started

先来设置变量,指向 clash 的 redir-port

export proxy_port=7892 # 按你 clash 的设置修改

接着设置 firewalld 和 ip route。 Firewalld 的 direct rules 语法几乎等同于 iptables(因为实际上就是调用 iptables 来执行 direct rules)。所以这里直接照抄 systemd.slice + iptables + redir:如何在 Arch Linux 上配置透明代理 的规则,稍有修改:

#tcp
sudo firewall-cmd --direct --add-chain ipv4 nat clash
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -m cgroup --path "clash.slice" -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 0.0.0.0/8 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 10.0.0.0/8 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 127.0.0.0/8 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 169.254.0.0/16 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 172.16.0.0/12 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 192.168.0.0/16 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 224.0.0.0/4 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -d 240.0.0.0/4 -j RETURN

sudo firewall-cmd --direct --add-rule ipv4 nat clash 2 -p tcp -j REDIRECT --to-port "$proxy_port"

sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 1 -p tcp -j clash

#udp
sudo ip rule add fwmark 1 table 100
sudo ip route add local default dev lo table 100
sudo firewall-cmd --direct --add-chain ipv4 mangle clash
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -m cgroup --path "clash.slice" -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 0.0.0.0/8 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 10.0.0.0/8 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 127.0.0.0/8 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 169.254.0.0/16 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 172.16.0.0/12 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 192.168.0.0/16 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 224.0.0.0/4 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 1 -d 240.0.0.0/4 -j RETURN
sudo firewall-cmd --direct --add-rule ipv4 mangle clash 2 -p udp -j TPROXY --on-port "$proxy_port" --tproxy-mark 1
sudo firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 0 -p udp -j clash # 这里可能有错(`failed: iptables-restore: line 3 failed`),欢迎指正
sudo firewall-cmd --direct --add-chain ipv4 nat CLASH_DNS
sudo firewall-cmd --direct --remove-rules ipv4 nat CLASH_DNS 
sudo firewall-cmd --direct --add-rule ipv4 nat CLASH_DNS 1 -p udp -j REDIRECT --to-port 1053
sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -p udp --dport 53 -j CLASH_DNS

其中,sudo firewall-cmd --direct --add-rule ipv4 nat clash 1 -m cgroup --path "clash.slice" -j RETURN 将放到 clash.slice 下的 service 给绕过 clash 了。

Clash 要以 systemd unit 的形式 来运行。为了让 clash 不把自己的流量循环转回给自己,给它的 unit 分配给 clash.slice

[Service]
Slice=clash.slice

其它不想走 clash 的程序也可以以类似方式分配给 clash.slice。

没有评论: