前言
在上一篇《宝塔面板明明放行了端口,为什么还是连不上?》中,我们分析了很多端口问题的原因,我们留下了一个尾巴没讲——Docker。
由于Docker的便利性,现在很多开发者都会使用Docker,因此在排查端口问题时,我们也应该好好排查一番Docker,不过很多人都会忽略这一点,今天就来详细的解析一下这个问题。
具体现象:机器上启用了 ufw,ufw status 里没有任何 8080 的放行记录,甚至明确 deny 过它,但从外网照样能访问 8080 上的容器服务。防火墙的规则像是被无视了。
Docker为什么能绕过ufw
先看清楚两套规则各自管哪段路。
ufw 写的规则作用在 iptables 的 INPUT 链上,管的是"进入本机"的流量;
docker run -p 8080:80发布端口时,Docker 会往 nat 表和 filter 表写入自己的 DOCKER 链,容器进来的流量走的是 PREROUTING 转发路径,压根不经过 INPUT 链。
也就是说,流量在路口就被 Docker 劫走送进容器了,ufw 的大门口站得再严也拦不到它。这不是 ufw 失效,是这两条路根本不重叠,管不了。
Docker 这么做有它的道理:容器端口的生死由它自己负责,保证 -p 发布出来的端口一定可用,不被宿主机防火墙误伤。但副作用就是——凡是发布出去的端口,宿主机防火墙对它一律失效。
可以动手验证一下。Debian/Ubuntu 上:
sudo ufw deny 8080
docker run -d -p 8080:80 nginx 然后从外网 curl 一下机器的 8080 端口,能通。再执行:
sudo iptables -L DOCKER -n 就能看到 Docker 写进来的放行规则,nginx 的 8080 明明白白列在里面。
两点先划清边界:这套行为只影响 -p 发布到公网的端口,没发布的端口不受影响;容器之间的访问、本机访问容器也不受影响。所以问题只出在"想对公网关起门来"的端口上。

怎么发现是Docker在插手
装了 Docker 的机器排查端口问题:
docker ps先看有哪些端口被发布——发布列表里的端口,ufw 说了不算;sudo iptables -L DOCKER -n看DOCKER链,对照 ufw status——凡是"ufw里没有、DOCKER链里有"的端口,就是被Docker接管了。
案例:一台机器只打算让反向代理对外服务,数据库端口只允许本机连。检查时发现有人图省事把 Redis 用 -p 6379:6379 直接发布了,ufw 里没放行,大家就都以为它出不去——实际上整个公网都能连上这个 Redis。这类"以为关着其实开着"的端口,是安全上最危险的一类。
解法一:发布端口时绑定本机,交给反向代理
最干净的解法,是不让服务直接暴露:发布端口时把宿主机一侧绑成 127.0.0.1——
docker run -d -p 127.0.0.1:8080:80 nginx 这样容器只能被本机访问,外面要进来,由反向代理统一转发:
sudo ufw allow 80 对外只开反代的 80/443,容器端口全部收在机器内部。
ufw 从头到尾都能管住门口,Docker 和防火墙互不打架。需要公网直接访问的服务才直接 -p,其他一律走这条路,是装了 Docker 的机器比较推荐的默认姿势。
解法二:DOCKER-USER链,Docker留的口子
如果某个发布端口就是需要按来源过滤(比如只允许公司出口 IP 连管理端口),不要去改 ufw,改了也没用,看看 Docker 官方留的 DOCKER-USER 链,用来给使用者写规则,流量会先过这一道:
sudo iptables -I DOCKER-USER -p tcp --dport 8080 ! -s 203.0.113.10 -j DROP 这条规则的含义是:除了指定 IP,其他来源访问 8080 一律丢弃。写完后再用 sudo iptables -L DOCKER-USER -n --line-numbers 核对。
注意:手动写进 iptables 的规则,重启后不会自己回来,需要配 iptables-persistent 这类持久化工具;规则较多的话,也可以用 GitHub 上的 ufw-docker 脚本,它专门帮你在 DOCKER-USER 链里管理 ufw 风格的规则,改完 ufw 直接同步生效。
解法三:让Docker别碰iptables?慎用
网上流传最广的方案是改 /etc/docker/daemon.json:
{ "iptables": false} 改完 sudo systemctl restart docker。这样 Docker 确实不再写规则,防火墙归 ufw 管了——但代价是容器的端口映射、容器间通信要自己负责,-p 发布的端口外面通不通、怎么通,全得自己动手配。这个方案只适合对 iptables 很熟、有明确自定义网络需求的人,多数情况属于"修好了防火墙,弄坏了容器",不建议默认采用。
最后补一层纵深。公网暴露的端口越少越好,管理类面板、数据库这类端口尤其别直接发布;在系统防火墙之外,商家侧的过滤也能兜底,控制台的网络安全组能先对进站流量过滤一道,就算系统里哪个端口漏防了,它在系统外面还能挡一层,拿不准的拦截可以发工单确认。
总结
ufw 没放行、端口还通,多半不是防火墙坏了,是 -p 发布的端口走了 Docker 的 DOCKER 链,根本没经过 ufw 把守的 INPUT 链。
管住它有三条路:
服务收进 127.0.0.1 交给反向代理,是最省心的默认姿势;
按来源过滤去 DOCKER-USER 链写规则,记得做持久化;
"iptables": false看着痛快,代价是端口映射全要自己扛,慎用。
装了 Docker 的机器,防火墙得按 ufw 和 Docker 两套规则一起理解,端口排查时也比上篇的排查链多看一层 docker ps 和 DOCKER 链。
