我试图了解TPROXY如何工作,以为Docker容器构建透明代理。
经过大量研究,我设法创建了一个网络 namespace ,向其中注入(inject)了一个veth接口(interface),并添加了TPROXY规则。以下脚本可在干净的Ubuntu 18.04.3上运行:
ip netns add ns0
ip link add br1 type bridge
ip link add veth0 type veth peer name veth1
ip link set veth0 master br1
ip link set veth1 netns ns0
ip addr add 192.168.3.1/24 dev br1
ip link set br1 up
ip link set veth0 up
ip netns exec ns0 ip addr add 192.168.3.2/24 dev veth1
ip netns exec ns0 ip link set veth1 up
ip netns exec ns0 ip route add default via 192.168.3.1
iptables -t mangle -A PREROUTING -i br1 -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port 1234 --tproxy-mark 0x1/0x1
ip rule add fwmark 0x1 tab 30
ip route add local default dev lo tab 30
之后,我从Cloudflare blog启动了一个玩具Python服务器:
import socket
IP_TRANSPARENT = 19
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_IP, IP_TRANSPARENT, 1)
s.bind(('127.0.0.1', 1234))
s.listen(32)
print("[+] Bound to tcp://127.0.0.1:1234")
while True:
c, (r_ip, r_port) = s.accept()
l_ip, l_port = c.getsockname()
print("[ ] Connection from tcp://%s:%d to tcp://%s:%d" % (r_ip, r_port, l_ip, l_port))
c.send(b"hello world\n")
c.close()
最后,通过运行ip netns exec ns0 curl 1.2.4.8
,我可以观察到192.168.3.2
与1.2.4.8
的连接,并收到“hello world”消息。
问题在于,它似乎与Docker存在兼容性问题。在干净的环境中,所有工具都运行良好,但是一旦我启动Docker,事情就会开始出错。似乎TPROXY规则不再起作用。运行ip netns exec ns0 curl 192.168.3.1
会给出“连接重置”,并且运行ip netns exec ns0 curl 1.2.4.8
会超时(两者都应该产生“hello world”消息)。我尝试还原所有iptables规则,删除Docker生成的ip路由和规则并关闭Docker,但是即使我没有配置任何网络或容器,也没有任何效果。
幕后发生了什么,如何使TPROXY正常工作?
最佳答案
我使用strace -f dockerd
跟踪了Docker创建的所有进程,并查找了包含exec
的行。大多数命令是iptables
命令,我已经将它们排除在外,并且带有modprobe
的行看起来很有趣。我一个接一个地加载了这些模块,发现导致问题的模块是br_netfilter
。
该模块可通过iptables
,ip6tables
和arptables
过滤桥接数据包。可以通过执行iptables
禁用echo "0" | sudo tee /proc/sys/net/bridge/bridge-nf-call-iptables
部分。执行命令后,脚本再次运行,而不会影响Docker容器。
我仍然很困惑。我不了解这种设置的后果。我启用了数据包跟踪功能,但是在启用bridge-nf-call-iptables
之前和之后,这些数据包似乎都与一组完全相同的规则匹配,但是在前一种情况下,第一个TCP SYN数据包已交付给Python服务器,在后一种情况下,该数据包被丢弃了原因不明。
关于docker – TPROXY与Docker的兼容性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57710177/
https://www.coder.work/article/7359579