Docker 容器无法访问宿主机端口排查:1Panel 防火墙导致 Docker Bridge 网络异常
最近在折腾服务器的时候遇到了一个比较诡异的问题。
我的 Docker 容器一直运行正常:
- 可以访问公网
- 可以正常解析 DNS
- 可以 ping 通宿主机
- Docker bridge 网络也没有异常
但是唯独一个问题:
容器无法访问宿主机上的任何端口。
比如宿主机:
192.168.1.14上面运行了一个服务:
192.168.1.14:8096宿主机自己访问:
curl -v http://192.168.1.14:8096/正常返回:
HTTP/1.1 302 Found
Location: web/index.html说明服务本身没有问题。
但是进入 Docker 容器:
wget -S --spider http://192.168.1.14:8096/直接卡住:
Connecting to 192.168.1.14:8096最后 timeout。
第一反应:是不是 Docker 网络坏了?
因为 Docker 容器访问宿主机失败,很多时候第一反应都会怀疑:
- docker0 网桥
- iptables NAT
- 路由
- FORWARD 链
于是先检查 Docker NAT。
iptables -t nat -L POSTROUTING -n -v可以看到:
MASQUERADE 172.17.0.0/16
MASQUERADE 172.19.0.0/16Docker 默认 NAT 规则正常。
继续看 FORWARD:
iptables -L FORWARD -n -v发现:
DOCKER-USER
DOCKER-FORWARD并且 docker0 有 ACCEPT:
ACCEPT all -- docker0 *所以 Docker 本身应该没有问题。
tcpdump 给出了关键线索
网络问题最直接的方法还是抓包。
执行:
tcpdump -ni any host 192.168.1.14 and port 8096然后在容器里面访问:
wget -S --spider http://192.168.1.14:8096/抓到了:
172.17.0.4.59974 > 192.168.1.14.8096: Flags [S]说明:
Docker 容器发出的 SYN 已经到达宿主机。
但是后面不断重复:
Flags [S]
Flags [S]
Flags [S]却没有看到:
192.168.1.14.8096 > 172.17.0.4: Flags [S.]也就是:
宿主机收到了请求,但是没有回复。
问题基本锁定到了宿主机防火墙。
检查 iptables INPUT 链
查看:
iptables -L INPUT -n -v发现:
Chain INPUT (policy DROP)
1PANEL_BASIC_BEFORE
1PANEL_BASIC
1PANEL_BASIC_AFTER
ufw-before-input
...这里出现了一个关键点。
我的机器同时安装了:
- 1Panel 防火墙
- UFW
原本以为 UFW 会处理规则。
但是实际上:
INPUT
|
+-- 1Panel 防火墙
|
+-- UFW也就是说:
流量先经过 1Panel。
如果 1Panel 丢掉了,根本轮不到 UFW。
真正的罪魁祸首:1Panel BASIC_AFTER
继续查看:
iptables -L 1PANEL_BASIC_AFTER -n -v结果:
Chain 1PANEL_BASIC_AFTER
DROP tcp
DROP udp看到这里基本确定了。
1Panel 在最后阶段做了兜底:
没有提前允许的 TCP/UDP
|
v
DROP而 Docker 容器:
172.17.0.4访问:
192.168.1.14:8096属于进入宿主机 INPUT。
所以流程实际上是:
Docker Container
172.17.0.4
|
| TCP SYN
v
Host INPUT
|
v
1PANEL_BASIC_AFTER
|
v
DROP于是表现出来就是:
- ping 没问题
- TCP 全部 timeout
临时验证
为了确认判断,我直接插入两条规则:
iptables -I INPUT 1 -s 172.17.0.0/16 -j ACCEPT
iptables -I INPUT 1 -s 172.19.0.0/16 -j ACCEPT然后重新测试:
wget -S --spider http://192.168.1.14:8096/马上恢复:
HTTP/1.1 302 Found
Location: web/index.html问题解决。
后续处理
临时规则肯定不是最终方案。
因为:
iptables -I INPUT重启以后可能消失。
正确做法应该是在 1Panel 防火墙里面允许 Docker 网段。
例如:
172.17.0.0/16
172.18.0.0/16
172.19.0.0/16
172.20.0.0/16
172.21.0.0/16或者把规则放到 1Panel 的前置链:
iptables -I 1PANEL_BASIC_BEFORE 1 \
-s 172.17.0.0/16 \
-j ACCEPT原因也很简单:
后面有:
1PANEL_BASIC_AFTER
DROP必须提前放行。
总结
这次问题最后定位下来,其实不是 Docker 的问题。
也不是 UFW。
真正原因是:
1Panel 防火墙规则优先级高于 UFW,并且默认 DROP 了 Docker bridge 网段访问宿主机的 TCP 流量。
以后遇到:
Docker 容器能联网,但是访问不了宿主机端口
不要第一时间怀疑 Docker。
排查顺序:
- 看服务是否正常监听
curl host:port- 看 Docker 网络
iptables -t nat -L POSTROUTING -n -v- 看 FORWARD
iptables -L FORWARD -n -v- 抓包确认 SYN 有没有回来
tcpdump -ni any port xxxx- 最后检查 INPUT 链
尤其注意:
- 1Panel
- 宝塔
- UFW
- firewalld
- nftables
多个防火墙同时存在的时候,规则顺序比规则内容更加重要。
这次也算踩了一个坑:
Docker 容器访问宿主机,很多人会关注 FORWARD,但实际上访问宿主机服务最终走的是 INPUT 链。
记录一下,希望以后遇到类似问题的人少走点弯路。