记一次OpenWrt TPROXY透明代理踩坑之旅

v2ray + OpenWrt + TPROXY 实现透明代理纪实。

之前文章写过了,我有两台软路由,都装了OpenWrt系统,自然要在上面跑v2ray,也自然要配个透明代理。最开始是配REDIRECT透明代理,但是随后发现,REDIRECT只能代理TCP流量,无法转发UDP流量,然后可以使用TPROXY透明代理,就可以转发UDP流量了,虽然我暂时没有转发UDP流量的需求,但是TPROXY看起来更优,我自然要选择更优的方案了。

TPROXY透明代理说明

v2ray配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"listen": "127.0.0.1",
"tag": "transparent",
"port": 10000,
"protocol": "dokodemo-door",
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy",
"mark": 255
}
}
}

这块的配置网上很多,没啥多说的,但是在后续调试中,没办法通过v2ray得知12345是否接收到链接,所以我用python实现了tproxy监听:

1
2
3
4
5
6
7
8
9
10
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_IP, socket.IP_TRANSPARENT, 1)
s.bind(("127.0.0.1", 10000))
s.listen(10)
while True:
t = s.accept()
t[0].close()
print(t[1])

iptables 配置

1
2
3
4
// 局域网透明代理
# iptables -t mangle -A PREROUTING -p tcp -j TPROXY --on-port 10000 --on-ip 127.0.0.1 --tproxy-mark 0x1
// 本地路由器透明代理
# iptables -t mangle -A OUTPUT -j MARK --set-mark 0x1

路由表配置

1
2
# ip rule add fwmark 1 table 100
# ip route add local default dev lo table 100

问题说明

讲道理,完成了上述的配置以后,透明代理就能配置成功。

我两台路由器A和B,完成了上述配置后,其中A的确是成功了,但是B却失败了。这是之前遇到的问题,一直没能解决,我也就暂时搁置了,随后让A继续用TPROXY透明代理,让B使用REDIRECT透明代理。

但是这几天,路由器A又不行了,所以我又一次开始研究这个TPROXY透明代理。

透明代理流程

通过我的研究,我发现透明代理的步骤大致如下:

  1. 局域网设备C请求网站,发送SYN包,到达路由器A
  2. 路由器A的iptables mangle表的PREROUTING,匹配到这个SYN包,打上了0x1的mark,然后要发送给127.0.0.1:10000
  3. 因为SYN包有mark=1的标记,所以匹配到路由表100的规则,流量在lo网卡上进行本地回环
  4. 然后应该就能到127.0.0.1:10000的应用上了

如果是路由器本地进行透明代理则是:

  1. 路由器A请求网站,发送SYN包
  2. 路由器A的iptables mangle表的OUTPUT,匹配到这个SYN包,给其打上0x1的mark
  3. 路由器A的iptables mangle表的PREROUTING,匹配到,打上0x1的mark,然后转发给127.0.0.1:10000
  4. 因为SYN包有mark=1的标记,所以匹配到路由表100的规则,流量在lo网卡上进行本地回环
  5. 然后应该就能到127.0.0.1:10000的应用上了

调试思路

在本地进行透明代理是没问题的,问题只出在局域网的透明代理上。

iptalbes可以使用如下命令进行调试:iptables -t mangle -I PREROUTING -p tcp -j LOG

或者使用iptables -t mangle -vL可用查看规则是否命中。

openwrt1

然后根据上图中的流程进行调试,查看包流到了哪。

但是路由表的处理却不知道如何调试,tcpdump也抓不到包。

通过调试,发现能成功流经filter表的INPUT规则,但是后续却不知道数据包应该去哪了,也不知道该如何调试了。

随后思考,也有可能是sysctl的问题,或者内核驱动的问题。

所以找了一个同事,他家的设备TPROXY的透明代理能正确运行。和他设备的/proc/sys/net/ipv4目录下的怀疑的一些配置进行对比,但是失败了。

随后,我同事说他之前在docker环境下配置也失败了,但是不知道为啥。这让我突然想到,我的路由器B是装了docker环境的,路由器A最近也装了,而且好像透明代理失败也是在我装了docker之后。

所以我把怀疑点放在了docker上,首先是iptables的规则,把路由器B的docker卸载了,然后重启,使用iptables-save > 1.firewall保存规则,然后再把docker装上,再应用规则iptables-restore < 1.firewall

但是失败了,所以问题应该不在docker的iptables规则上。

随后又对比了有docker和没有docker环境下lsmod的信息,也没有得到啥有用的信息。

随后,我按照的我安装命令:opkg install dockerd luci-app-dockerman docker-compose luci-lib-docker

我去查找这些包的信息,随后在dockerd包中发现了:https://github.com/openwrt/packages/blob/openwrt-21.02/utils/dockerd/files/etc/sysctl.d/sysctl-br-netfilter-ip.conf

这个文件,然后我尝试sysctl net.bridge.bridge-nf-call-iptables=0

这回透明代理成功了,然后就确定了,的确是docker的问题,让我的TPROXY透明代理失效了。

随后也查找了一些信息:https://feisky.gitbooks.io/sdn/content/linux/params.html#bridge-nf

得知该配置的作用,但是还是没搞清楚该配置为啥会让透明代理失效,因为我查找了iptables所有表的FORWARD规则,并没有命中的。然后使用NOTRACK,也没用。

又搜了半天,但是还是没找到该配置会导致TPROXY透明失效的根本原因,不过至少解决了TPROXY透明代理的问题。也测试了,在这个配置设置为0的情况下,也并没有影响docker网络的正常运行。

记一次OpenWrt TPROXY透明代理踩坑之旅

https://nobb.site/2022/02/18/0x72/

Author

Hcamael

Posted on

2022-02-18

Updated on

2022-02-18

Licensed under