docker - TPROXY 与 Docker 的兼容性

标签 docker sockets iptables transparentproxy

我正在尝试了解 TPROXY 的工作原理,以便为 Docker 容器构建透明代理。

经过大量研究,我设法创建了一个网络命名空间,向其中注入(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.21.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

该模块可以通过iptablesip6tablesarptables 过滤桥接数据包。 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/

相关文章:

c# - Socket 编程 c#/客户端-服务器通信

linux - 通过本地用户名进行 Tshark 日志记录

docker - Docker 中 RabbitMQ 的端口转发失败

docker - 在多阶段构建中使用容器时,Docker 是否执行入口点?

c - 在单个 DNS 查询中请求 A 和 AAAA 记录

tomcat - 当我尝试打开任何其他网页时,为什么会出现 Tomcat 的欢迎页面?

ubuntu - 如何永久更新 iptables

docker - 如何在 Elastic Beanstalk Docker 环境中配置 HTTP 基本身份验证?

docker - 如何在Docker Compose中从服务器设置环境变量?

c - VxWorks sockLib 是否支持 setsockopt 可以使用的 SO_RCVTIMEO 选项来设置 recv 调用的超时,就像在 winsock 中一样?