docker-host 如何能在不违反 iptables DNAT 规则的情况下到达导出端口?
补充:为什么 iptables 日志消息只显示curl 127.0.0.1:8082
而不显示curl localhost:8082
?
更新:类似于https://stackoverflow.com/questions/29629620,连接到容器也适用于空的 iptables 规则(# systemctl stop iptables
清除所有表,设置要接受的策略)。
设置
docker-compose.yml
version: '3.2'
services:
webapp:
image: httpd:latest
networks:
- webapp
ports:
- "8082:80"
networks:
webapp:
driver: bridge
#port mapping does not work for internal networks
# internal: true
观察到的行为
- 数据包从 OUTPUT 链开始
- 包裹离开(lo),再次到达(lo)
- 被*filter中的 conntrack 目标接受输入链
预期行为
$ curl 127.0.0.1:8082
尽管 nat-table [每个连接仅遍历一次][1],DNAT 的计数器不应为零;第一个数据包的遍历:
- 数据包从 OUTPUT 链开始
- 数据包保持不变 (-> lo),再次到达 (lo)
- 数据包仍未改变:*nat PREROUTING -jump-> *nat DOCKER
- DNAT --到目的地 172.18.0.2:80
- 路由决策 --> *过滤器FORWARD (目的地是外部主机)
- *filter FORWARD -jump-> *filter DOCKER ang 被接受,其他数据包已被 conntrack 目标接受
计数器
*nat (UPDATE: full output instead of DOCKER-chain)
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 44 packets, 4248 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 44 packets, 4248 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !br-cac172d73716 172.18.0.0/16 0.0.0.0/0
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 172.18.0.2 172.18.0.2 tcp dpt:80
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- br-cac172d73716 * 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !br-cac172d73716 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8082 to:172.18.0.2:80
*filter
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- !br-cac172d73716 br-cac172d73716 0.0.0.0/0 172.18.0.2 tcp dpt:80
和:
docker network ls
NETWORK ID NAME DRIVER SCOPE
cac172d73716 docker_webapp bridge local
答案1
TLDR:由于兼容性原因,docker 默认运行 TCP 转发代理,--userland-proxy=false 将端口暴露完全切换到 iptables
Docker 还默认启动一个代理服务器。此服务是一个简单的 TCP 转发代理,接受*filter INPUT 链上的连接并将其转发到容器中。
根据本文,这些是此代理存在的主要原因:
要*过滤FORWARD 规则,必须配置设备进行路由,例如
net.ipv4.ip_forward
必须设置为 1。默认情况下不启用此功能。Linux 默认不允许在环回接口 (lo) 上进行路由,可以通过设置
net.ipv4.route_localnet
为 1 来启用此功能docker 团队希望支持运行 2.6 内核的 RHEL 6.5。正如讨论的那样这里,存在数据包必须返回到其来源桥的情况。这只有在桥发夹弯哪个不可用在这个系统上。
ss -lnp | grep 8082
显示:
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp LISTEN 0 128 *:8082 *:* users:(("docker-proxy",pid=3066,fd=4))
如果内核支持桥接发夹结构,则--userland-proxy=false
可以使用该交换机。docker 手册状态:
--userland-proxy 参数默认为 true,为容器间和容器外通信提供用户空间实现。禁用后,Docker 将同时使用额外的 MASQUERADE iptable 规则和 net.ipv4.route_localnet 内核参数,允许主机通过常用的环回地址连接到本地容器公开端口:出于性能原因,此替代方案是首选。