强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 15 章:Docker 与 iptables

第 15 章:Docker 与 iptables

本章目标:理解 Docker 如何使用 iptables 管理容器网络,掌握 Docker 自定义链的结构,学会解决 Docker 端口映射与防火墙规则的冲突。


15.1 Docker 的 iptables 规则体系

15.1.1 Docker 为什么要操作 iptables

Docker 需要通过 iptables 实现以下功能:

功能说明
容器间通信通过 bridge 网络实现同一主机内容器互联
端口映射将容器端口映射到宿主机端口
容器访问外网通过 MASQUERADE 实现容器的出站 NAT
网络隔离不同 Docker 网络之间的隔离
防火墙规则控制容器的网络访问

15.1.2 Docker 创建的自定义链

Docker 启动时会创建以下自定义链:

┌──────────────────────────────────────────────────────┐
│ nat 表                                                │
│                                                       │
│  PREROUTING                                           │
│      │                                                │
│      ▼                                                │
│  DOCKER (自定义链) ← 只处理端口映射的入站流量          │
│      │                                                │
│      ├── -p 80:80 → DNAT → 172.17.0.2:80             │
│      └── -p 443:443 → DNAT → 172.17.0.2:443          │
│                                                       │
│  POSTROUTING                                          │
│      │                                                │
│      ▼                                                │
│  DOCKER-ISOLATION-STAGE-1 (自定义链)                  │
│  DOCKER-ISOLATION-STAGE-2 (自定义链)                  │
│  DOCKER (自定义链) ← MASQUERADE 出站流量              │
│                                                       │
└──────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────┐
│ filter 表                                             │
│                                                       │
│  FORWARD                                              │
│      │                                                │
│      ▼                                                │
│  DOCKER-USER (自定义链) ← 用户自定义规则(推荐)       │
│  DOCKER-ISOLATION-STAGE-1 (自定义链)                  │
│  DOCKER-ISOLATION-STAGE-2 (自定义链)                  │
│  DOCKER (自定义链) ← Docker 内部规则                   │
│                                                       │
└──────────────────────────────────────────────────────┘

15.1.3 查看 Docker 创建的规则

# 查看 filter 表的完整规则
sudo iptables -L -n -v --line-numbers

# 查看 nat 表的完整规则
sudo iptables -t nat -L -n -v --line-numbers

# 只查看 Docker 自定义链
sudo iptables -L DOCKER -n -v
sudo iptables -L DOCKER-USER -n -v
sudo iptables -L DOCKER-ISOLATION-STAGE-1 -n -v
sudo iptables -L DOCKER-ISOLATION-STAGE-2 -n -v

# 查看 nat 表的 DOCKER 链
sudo iptables -t nat -L DOCKER -n -v

15.2 Docker 网络模式与 iptables

15.2.1 bridge 模式(默认)

# 查看 Docker bridge 网络
docker network ls
docker network inspect bridge

# bridge 模式的 iptables 规则:
# 1. 容器访问外网:MASQUERADE
sudo iptables -t nat -L DOCKER -n -v
# -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

# 2. 端口映射:DNAT
# docker run -p 8080:80 nginx
# -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80

15.2.2 host 模式

# host 模式:容器直接使用宿主机网络栈
# 不需要 iptables 做 DNAT/SNAT
docker run --network host nginx

15.2.3 none 模式

# none 模式:无网络
# 不创建 iptables 规则
docker run --network none alpine

15.2.4 overlay 模式(Swarm)

# overlay 模式:跨主机容器通信
# 使用 VXLAN 隧道 + iptables NAT
docker swarm init
docker network create -d overlay my-overlay
docker service create --name web --network my-overlay -p 80:80 nginx

15.3 DOCKER-USER 链

15.3.1 为什么需要 DOCKER-USER 链

Docker 每次启动容器时会自动修改 iptables 规则。如果你直接修改 DOCKER 链中的规则,Docker 重启后你的修改会被覆盖。

DOCKER-USER 链是 Docker 专门为用户保留的自定义链,Docker 不会修改这个链中的规则。

FORWARD 链处理顺序:

数据包 → DOCKER-USER → DOCKER-ISOLATION → DOCKER → 最终处理
           ↑
           在这里添加自定义规则(Docker 不会覆盖)

15.3.2 使用 DOCKER-USER 链

# ═══════════════════════════════════════════════════
# 在 DOCKER-USER 链中添加自定义规则
# ═══════════════════════════════════════════════════

# 限制外部只能访问容器的 80 和 443 端口
iptables -I DOCKER-USER -i eth0 -p tcp --dport 80 -j ACCEPT
iptables -I DOCKER-USER -i eth0 -p tcp --dport 443 -j ACCEPT
iptables -I DOCKER-USER -i eth0 -j DROP

# 允许内部网络访问所有容器端口
iptables -I DOCKER-USER -i eth1 -j ACCEPT

# 限制特定 IP 访问容器
iptables -I DOCKER-USER -i eth0 -s 192.168.1.0/24 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER-USER -i eth0 -p tcp --dport 3306 -j DROP

15.3.3 DOCKER-USER 注意事项

⚠️ 规则顺序很重要:DOCKER-USER 链中的规则在 DOCKER 链之前处理。使用 -I 插入规则时,顺序会影响结果。

⚠️ 不要在 DOCKER 链中添加规则:Docker 会覆盖 DOCKER 链的规则。

⚠️ DOCKER-USER 只影响 FORWARD 链:它不影响 INPUT 和 OUTPUT 链。


15.4 端口映射冲突

15.4.1 问题描述

当 Docker 使用 -p 参数映射端口时,它会通过 DNAT 将流量直接转发到容器,绕过了宿主机的 INPUT 链

                    ┌─────────────────────────┐
                    │ 宿主机 INPUT 链          │
                    │                         │
外部 → 80 端口 ────→│  不经过 INPUT 链!       │──→ PREROUTING → DNAT → 容器
                    │  因为 DNAT 在            │
                    │  PREROUTING 就发生了      │
                    └─────────────────────────┘

这意味着:在 INPUT 链中阻止 80 端口的规则对 Docker 映射的端口无效!

15.4.2 验证

# 启动一个 Docker 容器并映射端口
docker run -d -p 8080:80 --name test-nginx nginx

# 在 INPUT 链中拒绝 8080 端口
iptables -A INPUT -p tcp --dport 8080 -j DROP

# 但外部仍然可以访问!因为流量走了 FORWARD 链,不走 INPUT 链
curl http://your-server-ip:8080  # 仍然能访问

15.4.3 解决方案

# ═══════════════════════════════════════════════════
# 方案 1:使用 DOCKER-USER 链限制入站流量
# ═══════════════════════════════════════════════════

# 只允许特定 IP 访问容器的 80 端口
iptables -I DOCKER-USER -i eth0 ! -s 192.168.1.0/24 \
  -p tcp --dport 80 -j DROP

# 全面限制:只允许特定 IP 访问容器端口
iptables -I DOCKER-USER -i eth0 -s 192.168.1.100 -j ACCEPT
iptables -I DOCKER-USER -i eth0 -s 192.168.1.101 -j ACCEPT
iptables -I DOCKER-USER -i eth0 -j DROP
# ═══════════════════════════════════════════════════
# 方案 2:只绑定到 localhost(不对外暴露)
# ═══════════════════════════════════════════════════

# 只监听 127.0.0.1
docker run -d -p 127.0.0.1:8080:80 nginx

# 现在只有本机能访问
curl http://127.0.0.1:8080  # 能访问
curl http://192.168.1.10:8080  # 不能访问
# ═══════════════════════════════════════════════════
# 方案 3:使用 Docker 网络隔离
# ═══════════════════════════════════════════════════

# 创建内部网络(无外部访问)
docker network create --internal my-internal-net

# 在内部网络中运行数据库容器
docker run -d --network my-internal-net --name db mysql

# 只有同一网络的容器能访问数据库

15.5 Docker 网络隔离

15.5.1 DOCKER-ISOLATION 链

Docker 使用 DOCKER-ISOLATION-STAGE-1 和 DOCKER-ISOLATION-STAGE-2 实现不同 Docker 网络之间的隔离:

# 查看隔离规则
iptables -L DOCKER-ISOLATION-STAGE-1 -n -v
iptables -L DOCKER-ISOLATION-STAGE-2 -n -v

15.5.2 跨网络通信

# 创建两个独立的 Docker 网络
docker network create net-a
docker network create net-b

# net-a 中的容器
docker run -d --network net-a --name web-a nginx

# net-b 中的容器
docker run -d --network net-b --name web-b nginx

# web-a 不能直接访问 web-b(被 DOCKER-ISOLATION 规则阻止)

# 如果需要跨网络通信,可以将容器加入多个网络
docker network connect net-b web-a

15.6 Docker + iptables 实战

15.6.1 安全的 Docker 宿主机防火墙

#!/bin/bash
# ═══════════════════════════════════════════════════
# Docker 宿主机防火墙配置
# ═══════════════════════════════════════════════════

# ─── 1. 清空非 Docker 的规则 ───
# 注意:不要清空 Docker 的自定义链!
iptables -F INPUT
iptables -F OUTPUT

# ─── 2. INPUT 链规则(宿主机保护)───

# 已建立的连接
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 回环接口
iptables -A INPUT -i lo -j ACCEPT

# Docker bridge 网络(允许宿主机与容器通信)
iptables -A INPUT -i docker0 -j ACCEPT

# SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Docker API(如果需要远程管理,限制来源)
# iptables -A INPUT -p tcp --dport 2376 -s 10.0.0.0/8 -j ACCEPT

# 日志并拒绝
iptables -A INPUT -j LOG --log-prefix "HOST-DROP: "
iptables -A INPUT -j DROP

# ─── 3. DOCKER-USER 链规则(容器访问控制)───

# 清空 DOCKER-USER 链
iptables -F DOCKER-USER

# 允许已建立的连接
iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN

# 允许内部网络访问容器
iptables -A DOCKER-USER -i eth1 -j RETURN

# 允许外部访问容器的 Web 端口
iptables -A DOCKER-USER -i eth0 -p tcp -m multiport --dports 80,443 -j RETURN

# 允许外部访问容器的 SSH(用于调试)
iptables -A DOCKER-USER -i eth0 -p tcp --dport 2222 -j RETURN

# 拒绝外部访问其他容器端口
iptables -A DOCKER-USER -i eth0 -j DROP

# 默认返回(允许 Docker 内部通信)
iptables -A DOCKER-USER -j RETURN

# ─── 4. OUTPUT 链(宿主机出站)───
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -j ACCEPT
iptables -P OUTPUT ACCEPT

echo "Docker host firewall configured."

15.6.2 多容器应用的防火墙规则

#!/bin/bash
# ═══════════════════════════════════════════════════
# 微服务架构的防火墙配置
# ╌── nginx (80,443) → api (8080) → db (5432)
# ╰── redis (6379) ← api
# ═══════════════════════════════════════════════════

# 创建自定义网络
docker network create app-net

# 运行容器
docker run -d --network app-net --name db postgres:14
docker run -d --network app-net --name redis redis:7
docker run -d --network app-net --name api my-api:latest
docker run -d --network app-net -p 80:80 -p 443:443 --name nginx nginx

# DOCKER-USER 链:限制外部访问
iptables -F DOCKER-USER

# 允许已建立连接
iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN

# 允许外部访问 80 和 443
iptables -A DOCKER-USER -i eth0 -p tcp -m multiport --dports 80,443 -j RETURN

# 拒绝外部直接访问 API、DB、Redis
iptables -A DOCKER-USER -i eth0 -j DROP

# 允许容器间通信
iptables -A DOCKER-USER -j RETURN

15.7 Docker daemon 配置

15.7.1 禁用 iptables 管理

// /etc/docker/daemon.json
{
    "iptables": false
}

警告:禁用 iptables 后,Docker 不会自动创建 NAT 和转发规则,你需要手动配置所有网络规则。只推荐高级用户使用。

15.7.2 其他网络相关配置

// /etc/docker/daemon.json
{
    "ip": "0.0.0.0",
    "ip-forward": true,
    "ip-masq": true,
    "iptables": true,
    "default-address-pools": [
        {"base": "172.17.0.0/12", "size": 24}
    ]
}
参数说明默认值
iptables是否管理 iptables 规则true
ip-forward是否启用 IP 转发true
ip-masq是否启用 IP 伪装true
ip默认绑定地址0.0.0.0

15.8 Kubernetes 与 iptables

15.8.1 kube-proxy iptables 模式

Kubernetes 的 kube-proxy 也有 iptables 模式,用于实现 Service 的负载均衡:

# 查看 Kubernetes 创建的 iptables 规则
iptables -t nat -L KUBE-SERVICES -n -v
iptables -t nat -L KUBE-POSTROUTING -n -v

# 查看特定 Service 的规则
iptables -t nat -L KUBE-SVC-XXXXX -n -v

15.8.2 Kubernetes 的自定义链

# Kubernetes 创建的主要自定义链
iptables -L KUBE-SERVICES -n         # Service 入口
iptables -L KUBE-EXTERNAL-SERVICES -n # 外部 Service
iptables -L KUBE-POSTROUTING -n      # 出站 MASQUERADE
iptables -L KUBE-MARK-MASQ -n        # 标记需要 MASQUERADE 的包
iptables -L KUBE-FIREWALL -n         # 防火墙规则

15.8.3 注意事项

⚠️ 不要手动修改 Kubernetes 的 iptables 链:kube-proxy 会自动管理这些链,手动修改会被覆盖。

⚠️ 使用 NetworkPolicy 代替手动 iptables:Kubernetes 提供了 NetworkPolicy 资源,可以声明式地控制 Pod 间的网络访问。


15.9 注意事项

⚠️ Docker 会重置规则:Docker daemon 重启时会清空并重新创建自己的自定义链。不要在 Docker 链中添加自定义规则。

⚠️ 使用 DOCKER-USER 链:所有对 Docker 容器网络的自定义控制都应该放在 DOCKER-USER 链中。

⚠️ 端口映射绕过 INPUT 链:Docker 的 -p 端口映射通过 DNAT 实现,不经过 INPUT 链。用 DOCKER-USER 链来控制。

⚠️ UFW 和 firewalld 的冲突:Ubuntu 的 UFW 和 CentOS 的 firewalld 可能与 Docker 的 iptables 规则冲突。建议只使用其中一种。


15.10 扩展阅读

资源说明
Docker 官方文档 - iptableshttps://docs.docker.com/network/iptables/
Docker 官方文档 - 网络https://docs.docker.com/network/
Kubernetes 官方文档 - kube-proxykube-proxy iptables 模式文档

本章小结

概念要点
Docker 自定义链DOCKER, DOCKER-USER, DOCKER-ISOLATION
DOCKER-USER用户自定义规则的安全位置
端口映射通过 DNAT 实现,绕过 INPUT 链
解决方案使用 DOCKER-USER 链或绑定 localhost
网络隔离DOCKER-ISOLATION 链实现网络间隔离

下一章第 16 章:nftables 迁移指南,将学习如何从 iptables 迁移到 nftables。