跳到主要内容

透明代理

nftables 规则

nftables.conf
#!/ur/sbin/nft -f
tabl inet proxy {

# 保留网段,参考:https://zh.wikipedia.org/zh-sg/%E4%BF%9D%E7%95%99IP%E5%9C%B0%E5%9D%80
set byp4 {
typeof ip daddr
flags interval
elements = {
0.0.0.0/8,
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,
192.0.2.0/24,
192.88.99.0/24,
192.168.0.0/16,
198.18.0.0/15,
198.51.100.0/24,
203.0.113.0/24,
224.0.0.0/4,
240.0.0.0-255.255.255.255
}
}
set byp6 {
typeof ip6 daddr
flags interval
elements = {
::,
::1,
::ffff:0:0:0/96,
100::/64,
64:ff9b::/96,
2001::/32,
2001:10::/28,
2001:20::/28,
2001:db8::/32,
2002::/16,
fc00::/7,
fe80::/10,
ff00::/8
}
}

chain prerouting {
type filter hook prerouting priority filter; policy accept;

meta l4proto != { tcp, udp } counter return

# 避免拦截公网回包
iifname ppp0 \
ct state new \
ct mark set 11 \
log prefix "Public Inbound:" \
counter \
return \
comment "Mark conntrack for connection from internet, avoid intercept the return packets"

# 不拦截从公网主动进来的包
iifname ppp0 counter return

# 避免源和目的都是本机公网 ip 时造成代理 UDP 无限回环
fib daddr type local counter return

# 对保留 IP(包括内网) 放行。
ip daddr @byp4 counter return
ip6 daddr @byp6 counter return
goto proxy
}
chain proxy {
# 对其它数据包(其它内网机器访问公网的包)进行拦截,全部转发给代理。
# 踩坑:一定 tproxy 到回环 ip (127.0.0.1和::1),如果 tproxy 到 0.0.0.0和::,运行一段时间后,可能会导致 UDP 疯狂不停回环打满 CPU(本机外部IP到本机外部IP一直在lo网卡打转)
meta l4proto { tcp, udp } tproxy ip to 127.0.0.1:12345 meta mark set 1 counter
meta l4proto { tcp, udp } tproxy ip6 to [::1]:12345 meta mark set 1 counter
}
chain output {
type route hook output priority filter; policy accept;

# 不拦截代理发出的包。
meta skgid eq 23333 counter return

# 不拦截公网回包
ct mark 11 counter return

# 对保留 IP(包括内网) 放行。
ip daddr @byp4 counter return
ip6 daddr @byp6 counter return

# 避免源和目的都是本机公网 ip 时造成代理 UDP 无限回环
fib daddr type local counter return

# 主机内进程发出的包打上 mark,让报文走策略路由重新进入 PREROUTING,以便被代理。
meta l4proto { tcp, udp } meta mark set 1 counter
}
}

  • 本机发出的包和转发内网其它设备的包都统一用策略路由拦截,使用 tproxy 将包重定向到透明代理监听的端口。
  • 不拦截内网和保留网段的流量。
  • 不拦截公网主动进来的流量。
  • 不拦截来自公网的回包。
  • 不拦截透明代理自身的包(使用进程 skgid 匹配,需让代理以特定 gid 启动)。
  • 不拦截目标 IP 是本机 IP 的包(避免无限回环)。