docker启动容器iptables分析

filter 表规则

iptables -L -n
# Warning: iptables-legacy tables present, use iptables-legacy to see them
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         
DOCKER-USER  all  --  0.0.0.0/0            0.0.0.0/0           
DOCKER-ISOLATION-STAGE-1  all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:80

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination         
DOCKER-ISOLATION-STAGE-2  all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
target     prot opt source               destination         
DROP       all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0

nat 表规则

iptables -t nat -L -n
# Warning: iptables-legacy tables present, use iptables-legacy to see them
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:80

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.2:80

现在这两组规则就能完整串起来了。你执行的是:

docker run --name nginx -d -p 80:80 nginx:1.27

Docker 启动 nginx 容器后,自动增加了:

  • filter 表中的 DOCKER 放行规则
  • nat 表中的 DNAT 规则
  • POSTROUTING 的 MASQUERADE 规则

一、先看容器实际情况

你的规则已经明确告诉我们:

容器 IP:172.17.0.2
容器端口:80
宿主机端口:80

也就是:

宿主机:80
    ↓
172.17.0.2:80

对应:

-p 80:80

二、filter 表的 DOCKER 规则

你现在:

Chain DOCKER (1 references)

ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80

这是 Docker 为端口映射自动添加的放行规则

注意这里的目标:

172.17.0.2:80

不是宿主机的 :80

因为在 NAT 阶段,目标地址已经被转换了。

完整过程是:

客户端
  │
  │ 访问 宿主机IP:80
  ▼
PREROUTING
  │
  ▼
DNAT
  │
  │ 宿主机:80
  │     ↓
  │ 172.17.0.2:80
  ▼
FORWARD
  │
  ▼
DOCKER
  │
  ▼
ACCEPT tcp → 172.17.0.2:80
  │
  ▼
nginx容器

所以这条:

ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80

就是允许外部流量最终转发到这个容器的 80 端口。


三、FORWARD 的执行顺序

你的 FORWARD:

Chain FORWARD (policy DROP)

1  DOCKER-USER
2  DOCKER-ISOLATION-STAGE-1
3  ACCEPT ctstate RELATED,ESTABLISHED
4  DOCKER
5  ACCEPT
6  ACCEPT

当外部客户端访问:

宿主机IP:80

DNAT 完成以后,进入:

FORWARD

然后依次经过:

DOCKER-USER
    ↓
DOCKER-ISOLATION-STAGE-1
    ↓
RELATED,ESTABLISHED
    ↓
DOCKER

最终匹配:

ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80

所以:

FORWARD policy DROP

并不会导致这个 Docker 容器访问失败。

因为:

FORWARD
   │
   ├── ...
   │
   └── DOCKER
          │
          └── ACCEPT → 172.17.0.2:80

已经明确允许了。


四、nat 表:最核心的是 DNAT

你的:

Chain PREROUTING

DOCKER all
    ADDRTYPE match dst-type LOCAL

把目标是本机的流量送到:

DOCKER

然后:

Chain DOCKER

RETURN all -- 0.0.0.0/0 0.0.0.0/0

DNAT tcp -- 0.0.0.0/0 0.0.0.0/0
tcp dpt:80 to:172.17.0.2:80

这里有一个非常重要的问题:

你贴出来的 RETURN 顺序不正常

如果这确实是当前规则的真实顺序:

1 RETURN
2 DNAT tcp dpt:80 → 172.17.0.2:80

那么:

RETURN

会首先匹配所有流量。

也就是说,理论上:

TCP/80

到这里就:

RETURN

后面的:

DNAT

不会执行。

但 Docker 正常情况下不会生成这种顺序。

因此这进一步说明你机器上的:

iptables-nft
iptables-legacy

混用情况值得重点确认。


五、为什么你的 filter DOCKER 规则已经有了?

因为:

docker run -p 80:80 ...

至少需要两类处理:

第一类:NAT

把:

宿主机:80

变成:

172.17.0.2:80

也就是:

DNAT

第二类:FORWARD

允许:

外部
 ↓
宿主机
 ↓
容器

也就是:

ACCEPT

所以你看到:

nat:
DNAT tcp dpt:80 → 172.17.0.2:80

以及:

filter:
ACCEPT tcp → 172.17.0.2:80

是配套的。


六、POSTROUTING 的 MASQUERADE

你现在:

MASQUERADE all
    -- 172.17.0.0/16

这是 Docker bridge 网络访问外部网络使用的。

例如 nginx 容器执行:

docker exec nginx curl https://www.baidu.com

容器发出的包:

SRC = 172.17.0.2
DST = 外部IP

经过:

POSTROUTING
MASQUERADE

变成:

SRC = 宿主机IP
DST = 外部IP

所以外部网络看到的是:

宿主机IP

而不是:

172.17.0.2

七、你的第二条 MASQUERADE

MASQUERADE tcp
    -- 172.17.0.2 172.17.0.2
    tcp dpt:80

这是 Docker 为端口映射相关流量建立的 NAT 处理规则。

主要目的是确保某些:

容器 ←→ 宿主机

以及端口发布场景下的回程流量能够正确通过 conntrack/NAT 返回。

你可以把 Docker -p 80:80 理解成不是简单的一条规则,而是一整套:

DNAT
+
FORWARD ACCEPT
+
MASQUERADE
+
conntrack

共同完成。


八、整个访问链路

假设:

宿主机:
10.10.10.100

容器:
172.17.0.2

nginx:
172.17.0.2:80

外部机器:

10.10.10.200

执行:

curl http://10.10.10.100:80

完整过程:

┌──────────────────────────────┐
│       10.10.10.200           │
│          Client              │
└──────────────┬───────────────┘
               │
               │ TCP 10.10.10.100:80
               ▼
┌──────────────────────────────┐
│        Linux Host            │
│                              │
│  PREROUTING                  │
│       │                      │
│       ▼                      │
│  DOCKER                      │
│       │                      │
│       │ DNAT                 │
│       │ :80                  │
│       ▼                      │
│  172.17.0.2:80               │
│       │                      │
│       ▼                      │
│  FORWARD                     │
│       │                      │
│       ▼                      │
│  DOCKER                      │
│       │                      │
│       │ ACCEPT               │
│       ▼                      │
│      docker0                 │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│      nginx container         │
│                              │
│      172.17.0.2:80           │
│                              │
│      nginx                   │
└──────────────────────────────┘

回包:

nginx
  │
  ▼
172.17.0.2
  │
  ▼
conntrack/NAT
  │
  ▼
10.10.10.100:80
  │
  ▼
10.10.10.200

客户端感觉自己始终是在访问:

10.10.10.100:80

它根本不知道后面还有:

172.17.0.2:80

posted @ 2026-09-03 14:57  ZovoeY  阅读(12)  评论(0)    收藏  举报