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

浙公网安备 33010602011771号