Ubuntu+Docker环境下ufw无法拦截容器端口流量的问题排查
我有一台运行Ubuntu 20的服务器,已安装Docker并运行多个容器。其中名为reverseproxy的Nginx容器负责接收80、443端口的流量,并将其路由至其他容器,该功能运行正常。现在我希望通过ufw拦截除80、443及SSH(22端口)外的所有入站流量,但发现容器发布的3000、3001、8081、15672端口仍能被外部访问。请问这是什么原因?如何通过ufw实现全流量拦截?
当前ufw配置
www@broowqh:~$ sudo ufw status verbose Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), deny (routed) New profiles: skip To Action From -- ------ ---- 22/tcp ALLOW IN Anywhere 9000 ALLOW IN Anywhere 3001 DENY IN Anywhere 3001/tcp DENY IN Anywhere 3001/udp DENY IN Anywhere 22/tcp (v6) ALLOW IN Anywhere (v6) 9000 (v6) ALLOW IN Anywhere (v6) 3001 (v6) DENY IN Anywhere (v6) 3001/tcp (v6) DENY IN Anywhere (v6) 3001/udp (v6) DENY IN Anywhere (v6)
Docker容器状态
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 48709042d67f nginx:1.23-alpine "/docker-entrypoint.…" 10 hours ago Up 10 hours. 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp. reverseproxy 401d6576b3e0 adminer:4.8.1 "entrypoint.sh docke…" 10 hours ago Up 10 hours. 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp adminer c47966cae717 postgres:14.1-alpine "docker-entrypoint.s…" 10 hours ago Up 10 hours. 5432/tcp db 1c3709a07fb0 www:current "docker-entrypoint.s…" 15 hours ago Up 10 hours. 0.0.0.0:3001->3001/tcp, :::3001->3001/tcp www db252e2833bc postgrest/postgrest:v10.0.0 "/bin/postgrest" 18 hours ago Up 10 hours. 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp api 68396bebcaa8 rabbitmq:3.9.13-management-alpine "docker-entrypoint.s…" 19 hours ago Up 10 hours. 0.0.0.0:5672->5672/tcp, 0.0.0.0:15672->15672/tcp broker
Nginx反向代理配置
upstream www { server www:3001; } upstream api { server api:3000; } upstream adminer { server adminer:8080; } upstream rabbit { server broker:15672; } server { listen 80; listen [::]:80; server_name example.com location / { return 301 https://example.com$request_uri; } } server { listen 443 default_server ssl http2; listen [::]:443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/live/smartplaylist.me/example.crt; ssl_certificate_key /etc/nginx/ssl/live/smartplaylist.me/example.key; location /adminer/ { proxy_pass http://adminer/; } location /rabbit/ { proxy_pass http://rabbit/; } location /api/ { proxy_pass http://api/; } location / { proxy_pass http://www/; } }
Docker在启动容器并进行端口映射(0.0.0.0:端口->容器端口)时,会直接修改系统的iptables规则,添加允许这些端口入站流量的规则,且这些规则优先级高于ufw规则。这导致即使在ufw中配置了拒绝规则,外部仍能访问这些端口——iptables会先处理Docker添加的规则,直接放行流量。
另外,从容器状态可见,除reverseproxy外,adminer、www、api、broker都直接将端口映射到主机的0.0.0.0地址,等于直接向外部暴露端口,完全绕开了Nginx反向代理的设计。
步骤1:移除不必要的容器端口映射
既然已通过Nginx反向代理路由所有外部流量,其他容器无需向主机暴露端口——Docker容器可通过内部网络直接通信(只需处于同一个Docker网络)。
- 停止并删除现有容器(注意备份postgres等容器的关键数据):
docker stop adminer www api broker db docker rm adminer www api broker db
- 创建Docker自定义网络,让所有容器加入实现内部通信:
docker network create app-network
- 重新启动容器,仅保留
reverseproxy的80、443端口映射,其他容器不添加端口映射:- 启动postgres:
docker run -d --name db --network app-network postgres:14.1-alpine- 启动adminer:
docker run -d --name adminer --network app-network adminer:4.8.1- 启动www:
docker run -d --name www --network app-network www:current- 启动api:
docker run -d --name api --network app-network postgrest/postgrest:v10.0.0- 启动broker:
docker run -d --name broker --network app-network rabbitmq:3.9.13-management-alpine- 将reverseproxy加入自定义网络(若未加入):
docker stop reverseproxy docker network connect app-network reverseproxy docker start reverseproxy
步骤2:修复ufw配置
当前ufw配置中存在冗余规则,需清理并仅保留必要的允许规则:
- 删除不必要的规则:
sudo ufw delete allow 9000 sudo ufw delete deny 3001 sudo ufw delete deny 3001/tcp sudo ufw delete deny 3001/udp # 删除对应的IPv6规则 sudo ufw delete allow 9000 (v6) sudo ufw delete deny 3001 (v6) sudo ufw delete deny 3001/tcp (v6) sudo ufw delete deny 3001/udp (v6)
- 添加80、443端口的允许规则:
sudo ufw allow 80/tcp sudo ufw allow 443/tcp
- 重新加载ufw:
sudo ufw reload
步骤3:配置Docker尊重ufw规则(可选)
若担心未来误映射端口导致暴露,可修改Docker配置,禁止其自动修改iptables规则:
- 编辑Docker守护进程配置文件:
sudo nano /etc/docker/daemon.json
- 添加以下内容:
{ "iptables": false }
- 重启Docker服务:
sudo systemctl restart docker
注:此设置会让Docker不再自动管理iptables规则,需确保所有容器均通过反向代理访问,不直接映射端口,或手动管理容器网络流量。
内容的提问来源于stack exchange,提问作者jkulak

