Linux / 网络工程 · 20 9 月, 2025 0

设置iptables对Docker不起作用:原因与解决

背景

笔者用Docker部署了WordPress,将宿主机的TCP:8000端口映射到容器的80端口。为了只允许特定IP访问容器,本想通过iptables设置:

iptables -A INPUT -s x.x.x.x -p tcp --dport 8000 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP

阻止外网对8000端口的访问,然而设置后发现并没有效果,外网依然能通过8000端口访问WordPress页面。

解决办法

iptables修改Docker的防火墙行为,需要使用DOCKER-USER,而不是INPUT。以下是修改后的设置:

iptables -A  DOCKER-USER -s x.x.x.x -p tcp --dport 80 -j ACCEPT
iptables -A DOCKER-USER -p tcp --dport 80 -j DROP
# 这里dport设置的端口写成转发后的80而不是8000并不是因为写错。原因后面说。

设置完成后,外网不再能通过8000打开WordPress页面。

🤔为什么会这样?

一言以蔽之,访问Docker的流量不会经过INPUT

Docker在启动时会在nat表的PREROUTING链中插入DNAT规则。该规则会将访问容器的流量导入到Docker创建的FORWARD链中,而不经过INPUT链。

举个例子。假设宿主机IP为192.168.1.100,Docker端口设置为宿主机8000转发到容器80。那么当有数据包访问宿主机的8000端口时,该数据包:

  1. nat表的PREROUTING链中,被Docker设置的规则将传送目标从192.168.1.100:8000修改为172.17.0.2:80172.17.0.2为Docker网桥创建的IP)。
  2. Linux内核识别出目标地址172.17.0.2不是宿主机的IP(宿主机是172.17.0.1,即Docker网桥IP),于是将数据包从INPUT路径切换到FORWARD路径
  3. FORWARD链指定数据包被送到DOCKER-USER
  4. DOCKER-USER链经过规则匹配后返回FORWARD链,继续匹配后续的规则(如DOCKER-ISOLATION-STAGEDOCKER等)。

这也解释了在DOCKER-USER链中设置的目标端口为什么是80:数据包的传送目标IP、端口被修改之后才会匹配DOCKER-USER定义的规则。

🤔DOCKER-USER具体在什么地方?

iptables -L FORWARD查看Docker对FORWARD链的设置。可以看到DOCKER-USER链是FORWARD链中的第一条规则,所以对其设置的规则会最先被匹配,优先级最高。

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  all  --  0.0.0.0/0            0.0.0.0/0
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0