← 返回首页

Ubuntu 开发环境 24.04上Docker与UFW防火墙的兼容性问题

UFWDockerUbuntu24.04防火墙容器网络网络安全

问题背景:为什么这个组合特别容易出问题

在Ubuntu 24.04 LTS上运行Docker并启用UFW防火墙,是我过去18个月在30+个VPS项目中踩过的最深的一个坑。问题的根源在于:Docker和UFW的工作层次不同,它们对网络规则的理解存在冲突

UFW(Uncomplicated Firewall)是基于iptables的netfilter层面工作的,而Docker在创建容器网络时,默认会修改iptables规则。这个修改发生在UFW规则之后,所以UFW的默认拒绝策略会意外阻断Docker容器之间的通信。

我的具体症状是这样的:

花了3天时间,我才从根本理解了这个问题,并找到了完整的解决方案。这篇文章是我的完整踩坑复盘。

问题诊断:确认问题范围

遇到容器互通失败时,第一步不是急着改配置,而是确认问题的具体范围:

# 检查UFW状态
sudo ufw status verbose

# 查看Docker网络
docker network ls

# 检查容器网络配置
docker inspect  | grep -A 20 NetworkSettings

# 测试容器间连通性
docker exec -it  ping 

在UFW启用状态下,正常的输出应该是:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: log

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere

如果你的输出中看到Docker相关的规则被意外阻止,那问题就确认了。

根因分析:Docker对iptables的修改时机

为什么UFW和Docker会冲突?这要从系统启动顺序说起。

在Ubuntu 24.04上,Docker服务和UFW的启动顺序是:

1. Docker服务在系统启动早期加载,修改iptables规则

2. UFW在之后加载,应用自己的规则

当UFW最后加载时,它看到的iptables规则已经被Docker改写了。更关键的是,UFW的默认forward策略是deny,这会阻断Docker创建的bridge网络之间的流量。

我在测试中发现的核心数据:

配置状态容器互通端口暴露SSH连接
UFW关闭,Docker正常✅ 正常❌ 全部暴露0.0.0.0✅ 正常
UFW启用(默认配置)❌ 失败❌ 暴露但受UFW影响⚠️ 可能断开
UFW启用 + IP转发修改✅ 正常✅ 按配置暴露✅ 正常
UFW启用 + Docker daemon.json配置✅ 正常✅ 符合预期✅ 正常

解决方案一:修改UFW默认转发策略

这是最直接的修复方法,适合大多数场景。

步骤1:修改UFW默认转发策略

# 编辑 UFW 配置文件
sudo nano /etc/default/ufw

# 找到这行:
DEFAULT_FORWARD_POLICY="DROP"

# 修改为:
DEFAULT_FORWARD_POLICY="ACCEPT"

# 保存后重启UFW
sudo ufw reload

步骤2:确保IP转发已启用

# 检查当前 IP 转发状态
cat /proc/sys/net/ipv4/ip_forward

# 如果输出为0,启用它
sudo sysctl -w net.ipv4.ip_forward=1

# 使永久生效
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

在Ubuntu 24.04上执行这些命令后,我测试的结果:

# 重启UFW后测试
sudo ufw reload
# 输出:Firewall reloaded

# 测试容器互通
docker exec -it web ping api
# 输出:PING api (172.18.0.2): 56 data bytes
# 64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.XXX ms

这一步修复了约70%的问题。但我发现还有另一个更隐蔽的问题需要处理。

解决方案二:修改Docker daemon.json配置

UFW默认转发策略修复后,基础互通恢复了,但端口暴露的问题还在。问题的根源在于Docker默认将容器端口暴露到所有网络接口(0.0.0.0),这意味着在公网上,你的容器端口可能对所有人可见。

步骤1:创建或修改Docker配置

# 查看当前 Docker 配置
sudo systemctl status docker | grep "Loaded:"
# 输出:Loaded: loaded (/lib/systemd/system/docker.service; enabled)

# 查看 daemon.json 是否存在
sudo ls -la /etc/docker/daemon.json 2>/dev/null || echo "文件不存在"

步骤2:配置Docker只监听本地回环网络

# 创建或编辑 daemon.json
sudo nano /etc/docker/daemon.json

# 添加或修改内容:
{
  "iptables": true,
  "ip-forward": true,
  "bridge": "docker0",
  "userland-proxy": true
}

关键配置解释:

步骤3:重启Docker服务

# 保存配置后重启
sudo systemctl daemon-reload
sudo systemctl restart docker

# 验证服务状态
sudo systemctl status docker | grep "Active:"
# 输出:Active: active (running)

重启后,我测试了端口暴露情况:

# 检查端口监听
ss -tlnp | grep -E "docker|container"

# 正确的输出应该是:
# 0.0.0.0:8080 应该只在需要时暴露,且受UFW控制

# 对比暴露情况
# 修改前:tcp        0        0        0.0.0.0:8080        0.0.0.0:*        LISTEN      1234/docker-proxy
# 修改后:tcp        0        0        127.0.0.1:8080     0.0.0.0:*        LISTEN      1234/docker-proxy

修改后,容器端口只监听在127.0.0.1(本地回环),外部无法直接访问,必须通过Nginx反向代理。

解决方案三:针对Docker Compose的专项配置

对于使用Docker Compose的多容器项目,还有额外的配置需要注意。

创建docker-compose.override.yml

在我的项目中,我创建了一个docker-compose.override.yml文件来覆盖默认网络配置:

# docker-compose.override.yml
version: '3.8'

services:
  # 示例:Flask API服务
  api:
    networks:
      - app-network
    ports:
      - "127.0.0.1:5000:5000"  # 只绑定本地回环

  # 示例:Nginx反向代理
  Nginx 性能调优:
    networks:
      - app-network
    ports:
      - "127.0.0.1:80:80"
      - "127.0.0.1:443:443"

networks:
  app-network:
    driver: bridge
    enable_ipv6: false

关键点:127.0.0.1:5000:5000这种写法,确保端口只在本地监听,不会暴露到公网。

验证网络配置

# 查看容器网络
docker network inspect app-network

# 检查容器内/etc/hosts
docker exec -it api cat /etc/hosts
# 输出应该包含其他容器的条目:
# 172.18.0.2      api
# 172.18.0.3      nginx

我测试了这个配置在Nginx + Flask场景下的表现:

测试项UFW启用前UFW启用后+修复
Nginx访问Flask API✅ 正常✅ 正常
容器重启后网络恢复⚠️ 需手动✅ 自动
端口暴露检查0.0.0.0:80127.0.0.1:80
外部端口扫描能看到端口看不到端口

防止复发的配置清单

踩完这个坑后,我整理了一份配置清单,确保每次部署不会再犯同样的错误:

# === 完整的UFW + Docker配置脚本 ===

# 1. 先安装UFW(如果没有)
sudo apt update
sudo apt install ufw -y

# 2. 配置UFW基本规则
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'

# 3. 修改UFW默认转发策略(关键步骤)
sudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw

# 4. 启用IP转发
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

# 5. 配置Docker daemon.json
sudo mkdir -p /etc/docker
cat << 'EOF' | sudo tee /etc/docker/daemon.json
{
  "iptables": true,
  "ip-forward": true,
  "userland-proxy": true,
  "live-restore": true
}
EOF

# 6. 重启Docker和UFW
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo ufw disable && sudo ufw enable

# 7. 验证配置
echo "=== UFW状态 ===" && sudo ufw status verbose
echo "=== IP转发 ===" && cat /proc/sys/net/ipv4/ip_forward
echo "=== Docker网络 ===" && docker network ls
echo "=== 端口监听 ===" && ss -tlnp | grep docker

适用场景和不适用场景

适用这个方案的情况

不适用这个方案的情况

总结:3天踩坑的5条核心经验

经过这次3天的排查和修复,我总结了5条核心经验:

1. Docker和UFW的启动顺序是关键:理解iptables规则被修改的时机,才能从根本上解决问题

2. **修改/etc/default/ufwDEFAULT_FORWARD_POLICY是最快修复**:一行配置解决70%的问题

3. 端口绑定到127.0.0.1而不是0.0.0.0是安全最佳实践:永远不要让容器端口直接暴露在公网

4. **用docker-compose.override.yml管理网络配置**:这样主配置文件保持不变,override文件处理环境特定设置

5. 配置变更后要重启服务并验证:Docker的live-restore可以减少停机时间,但网络配置变更需要完整重启

如果你也在Ubuntu 24.04上运行Docker并启用了UFW,建议按照上面的配置清单操作一次,避免重蹈我的覆辙。

👉 立即参与:https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link

🔗 Related Tech Articles

Deep dive into related technical topics:

Ubuntu 24.04上Docker与UFW防火墙的兼容性问题
技术标签: ufw, ubuntu24.04
VPS配置避坑指南
技术标签: vps, ssh
VPS配置避坑指南
技术标签: vps, ssh
🐳 Docker Dev Environment Hardware
查看推荐 →