Yuio
发布于 2026-11-03 / 1 阅读
0
0

ufw没放行的端口为什么还能访问?原因可能在Docker

前言

在上一篇《宝塔面板明明放行了端口,为什么还是连不上?》中,我们分析了很多端口问题的原因,我们留下了一个尾巴没讲——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端口问题.jpeg

怎么发现是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 链。

pexels-konrads-photo-14188848.jpg


评论