连接docker客户端时iptables遍历

连接docker客户端时iptables遍历

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

观察到的行为

  1. 数据包从 OUTPUT 链开始
  2. 包裹离开(lo),再次到达(lo)
  3. 被*filter中的 conntrack 目标接受输入

预期行为

$ curl 127.0.0.1:8082

尽管 nat-table [每个连接仅遍历一次][1],DNAT 的计数器不应为零;第一个数据包的遍历:

  1. 数据包从 OUTPUT 链开始
  2. 数据包保持不变 (-> lo),再次到达 (lo)
  3. 数据包仍未改变:*nat PREROUTING -jump-> *nat DOCKER
  4. DNAT --到目的地 172.18.0.2:80
  5. 路由决策 --> *过滤器FORWARD (目的地是外部主机)
  6. *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 内核参数,允许主机通过常用的环回地址连接到本地容器公开端口:出于性能原因,此替代方案是首选。

相关内容