现在网上已经有不少用 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。
from 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
------------------------
systemd.slice + iptables + redir:如何在 ArchLinux 上配置透明代理
又是久违了的技术教程文。这次说的是如何使用 redir (shadowsocks 也好,clash 也好)在 Arch Linux 设备上配置透明代理,以及方便地运行绕过代理的程序。因为要动 iptables,我就假设想这么做的你拥有 root 权限啦。
以下仅针对 IPv4 环境下的 TCP。
Cgroups 是什么?
Arch Linux Wiki 说:cgroups (Control groups) 是 Linux 内核的特性,允许用户管理/限制/审计一组进程。
…算了,LMGTFY 好了。
总之,我们要做的就是,把绕过代理的进程(包括代理程序本身)放到一个 cgroup 里,让 iptables 放行它们的请求,而把其外的所有请求转送给代理的 redir 端口。Arch Linux 上管理 cgroup 的最方便方法就是用 systemd,所以我们在这里用到 systemd 的 slice 来建立/访问 cgroup。
运行一个「绕过全局代理」的 systemd slice
如果你的代理是一个 systemd 服务的话,在 [Service] 下面这么写。这里我们把这个临时命名为 test2 (也可以用别的名字啦):
[Service] Slice=test2.slice
重载(systemctl daemon-reload
),然后运行这个服务,你的代理服务就跑在 test2.slice 里了。
在 cgroup 的目录里应该也会出现一个相应的 test2.slice:
# ls /sys/fs/cgroup/unified/test2.slice/ cgroup.controllers cgroup.max.descendants cgroup.threads io.pressure cgroup.events cgroup.procs cgroup.type memory.pressure cgroup.freeze cgroup.stat cpu.pressure run-u1925.scope/ cgroup.max.depth cgroup.subtree_control cpu.stat
如果不是服务的话,就用 systemd-run
:
sudo systemd-run --slice test2.slice --scope clash -c /etc/clash/config.yaml
如果你想开启一个绕过代理的程序,也用这样的方法。例如运行一个 Firefox:
sudo systemd-run --slice test2.slice --scope firefox
如果想在 shell 里测试的话,直接用 systemd-run 开一个运行在某个 slice 里的 shell:
sudo systemd-run --slice test2.slice --scope -S # -S 代表启动 shell
这样你就有了一个跑在 test2.slice 里的 shell。
快速试一试
在 test2.slice 里运行一个永不停止的 ping,然后在 iptables 里切断它的连接:
iptables -A OUTPUT -m cgroup --path "test2.slice" -j DROP
然后这个 ping 应该会无法 ping 通:
ping: sendmsg: Operation not permitted
好的,我们的 slice 和 iptables 成功地联动了。那么先把这条规则删除掉吧:
iptables -L OUTPUT --line-numbers # 找到一行类似 # 2 DROP all -- anywhere anywhere cgroup test2.slice # 的行,记录它开头的编号(这里是 2) iptables -D OUTPUT 2 # 把 2 换成你记录的编号
好的本节课程的新知识讲完了以下是复习内容(
又是 iptables
以下大部分是抄 Dreamacro/clash#158 和 这里 的作业,呃…
# 先建条链处理透明代理问题。因为后面要 REDIRECT,所以要在 nat 表。 iptables -t nat -N TP-TCP # 这条链的配置: # 1. 绕过代理的 slice 的数据包,直接放行回原来的链 iptables -t nat -A TP-TCP -m cgroup --path "test2.slice" -j RETURN # 2. 本地的各种地址也一律放行 iptables -t nat -A TP-TCP -d 0.0.0.0/8 -j RETURN iptables -t nat -A TP-TCP -d 127.0.0.0/8 -j RETURN iptables -t nat -A TP-TCP -d 10.0.0.0/8 -j RETURN iptables -t nat -A TP-TCP -d 169.254.0.0/16 -j RETURN iptables -t nat -A TP-TCP -d 172.16.0.0/12 -j RETURN iptables -t nat -A TP-TCP -d 192.168.0.0/16 -j RETURN iptables -t nat -A TP-TCP -d 224.0.0.0/4 -j RETURN iptables -t nat -A TP-TCP -d 240.0.0.0/4 -j RETURN # 3. 其它的去到 redir 的端口(clash 默认 7892,你的情况可能不同(YMMV)) iptables -t nat -A TP-TCP -p tcp -j REDIRECT --to-ports 7892 # 4. 所有出口包到 TP-TCP 表上 iptables -t nat -A OUTPUT -p tcp -j TP-TCP
试着访问一些网站,看看它们是不是走了代理(把浏览器的代理设置关闭)?以及运行
iptables -L TP-TCP -t nat -v -n
看看开头的 pkts 和 bytes 是不是有所变化?如果是,那么我们的透明代理大成功!
(这里应该放一张爱酱大胜利的图)
(不是那个快凉了的爱酱…)
后续工作
配置服务绕过代理
可能有一些正在使用的服务本身需要绕过代理,我们也来修改一下配置,让它们也能绕过代理。这里以 unbound 为例:
systemctl edit unbound
加一段:
[Service] Slice=test2.slice
然后重载(systemd daemon-reload
)并重新启动服务就可以啦。
iptables 配置持久化
我们的 iptables 配置在重新启动后会消失。为了让它长期保存,我们需要对其进行持久化:
iptables-save -f /etc/iptables/iptables.rules
以及在每次启动时自动导入:
systemctl enable iptables.service
附录
注:以下内容我没试过,不保证有效性。
你的代理也向内网其它主机提供服务?
(还是只有 TCP + IPv4)
首先处理路由问题。这里假设你的内网主机在 192.168.0.0/16。注意在比较大的网络中,你的内网主机可能在 10.0.0.0/8,这种情况下请在下方进行相应的更改:
iptables -t nat -A PREROUTING -p tcp -s 192.168/16 -j TP-TCP iptables -t nat -A POSTROUTING -s 192.168/16 -j MASQUERADE
然后你大概需要开启网卡的转发功能:
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf sysctl -p
UDP?
-j REDIRECT
是没法处理 UDP 了。为了处理 UDP,我们需要 -j TPROXY
。而使用 TPROXY,我们还需要加两条 ip route
和 ip rule
,所以这里就比较复杂了。
以下命令仅供参考,不保证有效(说实话我不知道为什么其它实现总想着),我也没试过:
# 先建条链处理透明代理问题。 iptables -t mangle -N TP-UDP # 这条链的配置: # 1. 绕过代理的 slice 的数据包,直接放行回原来的链 iptables -t mangle -A TP-UDP -m cgroup --path "test2.slice" -j RETURN # 2. 本地的各种地址也一律放行 iptables -t mangle -A TP-UDP -d 0.0.0.0/8 -j RETURN iptables -t mangle -A TP-UDP -d 127.0.0.0/8 -j RETURN iptables -t mangle -A TP-UDP -d 10.0.0.0/8 -j RETURN iptables -t mangle -A TP-UDP -d 169.254.0.0/16 -j RETURN iptables -t mangle -A TP-UDP -d 172.16.0.0/12 -j RETURN iptables -t mangle -A TP-UDP -d 192.168.0.0/16 -j RETURN iptables -t mangle -A TP-UDP -d 224.0.0.0/4 -j RETURN iptables -t mangle -A TP-UDP -d 240.0.0.0/4 -j RETURN # 2.5. 你很有可能会想让 DNS 不走代理 iptables -t mangle -A TP-UDP -p udp --dport 53 -j RETURN # 3. 其它的去到 redir 的端口(clash 默认 7892,你的情况可能不同(YMMV)) iptables -t mangle -A TP-UDP -p udp -j TPROXY --tproxy-mark 0x2333/0x2333 --on-ip 127.0.0.1 --on-port 7892 # 4. 所有出口包到 TP-UDP 表上 iptables -t mangle -A OUTPUT -p udp -j TP-UDP # 5. 还没完,我们还需要路由配置 # 新建路由表 100,将所有数据包发往 loopback 网卡 ip route add local 0/0 dev lo table 100 # 添加路由策略,让所有经 TPROXY 标记的 0x2333/0x2333 udp 数据包使用路由表 100 ip rule add fwmark 0x2333/0x2333 lookup 100
内网其它主机的连接问题,同上一节「你的代理也向内网其它主机提供服务?」。记得把命令里的 TCP 改为 UDP。
你还有 IPv6?
请将所有的 iptables 换成 ip6tables,同时各种本地地址段可能也需要改变。
from https://matters.news/@outloudvi/systemd-slice-iptables-redir-%E5%A6%82%E4%BD%95%E5%9C%A8-arch-linux-%E4%B8%8A%E9%85%8D%E7%BD%AE%E9%80%8F%E6%98%8E%E4%BB%A3%E7%90%86-bafyreia56osrovpylifeiuzdcrxr52gmbo5rkn5zmlvlho5tr7mjejqshy
No comments:
Post a Comment