Linux 上的透明代理:这次是 nftables、cgroups v2 和 systemd.slice
本文距离上次更新已经超过 900 天。因此,其中的信息可能已经过时。
透明代理是本站著名冷饭了。nftables 支持 cgroups v2 也算大事,那就把这饭再炒一次。
Re:Linked 在一年半多之前写了篇用 iptables 等做透明代理的文章。当时有这么几个没解决的问题:
- 能不能用
nftables
?- 不能。因为 nftables 当时还不支持 cgroups v2。
- 能不能把 IPv4 和 IPv6 的规则一起处理了?
- 不能。iptables 在这点分得很开。IPv6 要用 ip6tables 重写一遍。
- cgroups v2 还要手动开启,好麻烦啊。和 Docker 还不兼容。
- 对啊。
那么这几个问题陆陆续续都有解决,于是就有了这篇文章。
本来十月发的第一篇文章应该是关于谢斯塔,但既然那篇还没写完,就先把这篇比较好写的技术向文章发出来吧。笔者对白毛女主的爱就往后推一推。
「客观情况发生重大变化」
这篇文章写得出来,要多亏了这么几件事:
- Docker 20.10 正式支持 cgroups v2。
- 大家逐渐开始默认使用 cgroups v2,包括 Fedora 31+、Ubuntu 20.04+、Debian 11+,以及近期的 Arch Linux 和 openSUSE。
- 以及最重要的:nftables 支持 cgroups v2(自 0.9.9 版本)
优势与不足
和其它许多方案,以及上次的方案相比,主要的好处有这么几点:
- IPv4 和 IPv6 规则几乎可以放在一起
nftables
的语法比iptables
的要好看多得多(个人理解)- 不再需要使用
ipset
定义静态地址范围
不过也会产生一些代价:
- Docker 内容器走全局代理的需要单独配置(因为它的各种规则还在 iptables 那边)
- (本文暂时不提及关于容器走全局代理的配置。)
示例
XTLS 的文档给出了一份很不错的配置。这里稍作修改和解释:
#!/usr/sbin/nft -f
# 清空之前的 nftables 规则,方便覆盖
flush ruleset
# 定义总是不代理的地址
define RESERVED_IP = {
10.0.0.0/8,
100.64.0.0/10,
127.0.0.0/8,
169.254.0.0/16,
172.16.0.0/12,
192.0.0.0/24,
224.0.0.0/4,
240.0.0.0/4,
255.255.255.255/32,
# ....
}
table ip trxproxy {
chain prerouting {
type filter hook prerouting priority mangle; policy accept;
# 这里我们并没有配置作为网关的一些设置,所以不处理非本地请求
iif != "lo" return
ip daddr $RESERVED_IP return
ip protocol {tcp, udp} tproxy to 127.0.0.1:7890 meta mark set 0x2333
}
chain output {
type route hook output priority mangle; policy accept;
ip daddr $RESERVED_IP return
# cgroup 在这里!
socket cgroupv2 level 1 "bypassed.slice" return
ip protocol {tcp, udp} meta mark set 0x2333
}
}
以及路由配置。记得 fwmark
需要和上文中规则定义的一致:
ip route add local 0/0 dev lo table 100
ip rule add fwmark 0x2333 lookup 100