5_iptables现代运维

1. Iptables 的前端工具

iptables 直接操作 netfilter,但很多发行版提供了更高层的前端。它们之间不是替代关系,而是不同层级的操作入口,最终都作用于内核的 netfilter。

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
    A["用户"] --> B["firewalld"]
    A --> C["ufw"]
    A --> D["iptables"]
    A --> E["nftables"]
    B --> F["netfilter(内核)"]
    C --> F
    D --> F
    E --> F

    classDef user fill:#F59E0B,stroke:#D97706,color:#fff
    classDef frontend fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef kernel fill:#10B981,stroke:#059669,color:#fff

    class A user
    class B,C,D,E frontend
    class F kernel

1.1 firewalld(CentOS 7+ / RHEL 7+)

firewalld 引入了 zone(区域)的概念,将网卡分配到不同区域,每个区域有预定义的安全策略。

特性firewalldiptables 直接操作
操作方式通过 D-Bus 接口动态增删,不中断现有连接-A/-D 也是增量操作,但 iptables-restore 全量重载会短暂中断
抽象层级zone 场景化管理,不需要理解四表五链需要精确理解链和表的关系
底层调用 iptables 或 nftables直接操作 netfilter

firewalld 和 iptables 命令不要同时使用,会造成规则冲突。选一个用。

1.2 ufw(Ubuntu)

UFW(Uncomplicated Firewall)是 Ubuntu 默认的防火墙前端,追求极简操作:ufw allow 22/tcpufw enable 即可完成基本配置。

⚠️ ufw 启用后,INPUT 和 FORWARD 的默认策略是 DROP。此时如果执行 iptables -F,ufw 管理的放行规则会被清空,但 DROP 策略还在——SSH 断开,你被锁在外面。用了 ufw 就不要再碰 iptables 命令

1.3 云安全组

云厂商(AWS / 阿里云 / 腾讯云)的安全组运行在虚拟化层,和 iptables 是不同层面的东西。可以理解为:安全组是小区大门的门禁系统(进不了小区就到不了你家),iptables 是你家的门锁。

安全组iptables
作用层面虚拟化平台(宿主机之外)虚拟机内部
作用方向南北向 + 东西向(控制实例级别所有出入站流量)南北向 + 东西向
生效顺序先于 iptables后于安全组
关系互补,不替代互补,不替代

安全组和 iptables 都要配置。安全组做粗粒度控制,iptables 做细粒度控制。

了解了前端工具的定位,接下来看 iptables 的下一代替代方案——nftables。

2. nftables:iptables 的继任者

2.1 为什么要有 nftables?

iptables 随 Linux 2.4 内核(2001 年)正式发布,经过 20 多年的发展,暴露出一些架构性问题:

问题说明
工具碎片化IPv4 用 iptables、IPv6 用 ip6tables、网桥用 ebtables、ARP 用 arptables——4 套语法
规则更新慢每次修改规则都要全量替换整个表,规则多时开销大
语法不一致不同扩展模块的参数风格各异
缺少集合支持想匹配一批 IP 需要借助外部工具 ipset

nftables 于 2014 年在内核 3.13 中首次引入,在内核 4.x 后逐步成熟可用,用一个统一的框架解决了这些问题。

目前状态:

  • Debian 10+、Ubuntu 20.04+、CentOS/RHEL 8+ 已默认使用 nftables 作为后端
  • 很多系统上执行的 iptables 命令实际是 iptables-nft(兼容层)

2.2 核心概念对比

iptablesnftables说明
预定义 4 张表用户自定义表nftables 没有预设表,完全由你定义
预定义 5 条链用户自定义链需要手动创建并绑定到钩子
每条规则一个动作一条规则多个动作可以同时 log 和 drop
需要 ipset 管理 IP 集合内置 set 和 map原生支持集合和字典
iptables / ip6tables / ebtables统一使用 nft 命令一个工具管所有

2.3 语法对比速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# ========== 查看规则 ==========
# iptables
iptables -nvL INPUT

# nftables
nft list table inet filter
nft list chain inet filter input

# ========== 添加规则 ==========
# iptables:拒绝来自 1.1.1.1 的包
iptables -A INPUT -s 1.1.1.1 -j DROP

# nftables
nft add rule inet filter input ip saddr 1.1.1.1 drop

# ========== 删除规则 ==========
# iptables
iptables -D INPUT -s 1.1.1.1 -j DROP

# nftables(先查 handle 编号,再按编号删)
nft -a list chain inet filter input
nft delete rule inet filter input handle 5

# ========== NAT ==========
# iptables
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE

# nftables(注意:inet family 的 NAT 需要内核 4.18+,旧内核用 ip family)
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 masquerade

# ========== 使用集合(nftables 的优势) ==========
# iptables 需要 ipset
ipset create blacklist hash:ip
ipset add blacklist 1.1.1.1
ipset add blacklist 2.2.2.2
iptables -A INPUT -m set --match-set blacklist src -j DROP

# nftables 原生支持
nft add set inet filter blacklist { type ipv4_addr\; }
nft add element inet filter blacklist { 1.1.1.1, 2.2.2.2 }
nft add rule inet filter input ip saddr @blacklist drop

2.4 iptables-nft 兼容层

很多现代发行版上,/usr/sbin/iptables 实际指向 iptables-nft——你用 iptables 语法写命令,后端自动翻译成 nftables 规则。

1
2
3
4
# 检查你的系统用的是哪个
iptables -V
# iptables v1.8.7 (nf_tables) ← 后端是 nftables
# iptables v1.8.4 (legacy) ← 后端是旧版 iptables

如果输出包含 nf_tables,说明你已经在用 nftables 后端了,只是语法还是 iptables 的。了解底层变化有助于排障。

理论层面讲完了,接下来看 iptables 在 Docker 和 Kubernetes 中的实际应用。

3. Docker 与 iptables

Docker 容器运行在独立的网络命名空间中,拥有自己的 IP 地址(通常是 172.17.0.x),外部无法直接访问。Docker 通过自动创建 iptables 规则来解决这个问题——端口映射、容器出网、网络隔离,底层全靠 iptables。

对运维而言,理解这些自动规则至关重要:它们可能和你手写的规则冲突,排障时也需要知道流量经过了哪些 Docker 创建的链。

3.1 Docker 自动生成的 iptables 规则

Docker 启动后会自动创建一系列 iptables 规则:

1
2
3
4
5
# 查看 Docker 创建的 nat 规则
iptables -t nat --line -nvL

# 查看 Docker 创建的 filter 规则
iptables --line -nvL

Docker 创建的核心链:

链名所在表作用
DOCKERnat + filternat 表中做 DNAT(端口映射),filter 表中做端口放行
DOCKER-USERfilter用户自定义规则的入口,优先于 Docker 自动规则
DOCKER-ISOLATION-STAGE-1/2filter实现不同 Docker 网络之间的隔离

3.2 -p 端口映射的 iptables 原理

docker run -p 8080:80 nginx 的含义是:把宿主机的 8080 端口映射到容器的 80 端口。外部访问宿主机的 8080,实际到达容器内的 nginx。

Docker 如何实现这一点?用篇 4 学过的 DNAT:把目标地址从 宿主机IP:8080 改写为 容器IP:80,再通过 FORWARD 链转发到容器。

%%{init: {'theme': 'base', 'themeVariables': {'actorBkg': '#3B82F6', 'actorTextColor': '#fff', 'actorBorder': '#2563EB', 'signalColor': '#1E3A5F', 'activationBkgColor': '#DBEAFE', 'activationBorderColor': '#3B82F6'}}}%%
sequenceDiagram
    participant C as "外部客户端"
    participant NAT as "nat 表"
    participant R as "路由判断"
    participant FW as "filter 表"
    participant D as "容器 nginx"

    C->>NAT: "请求 宿主机:8080"
    activate NAT
    Note over NAT: "PREROUTING → DOCKER 链"
    NAT->>R: "DNAT → 172.17.0.2:80"
    deactivate NAT
    R->>FW: "转发到 docker0 网桥"
    activate FW
    Note over FW: "FORWARD → DOCKER-USER → DOCKER"
    FW->>D: "放行,到达容器"
    deactivate FW
    D-->>C: "响应"

对应的 iptables 规则(简化):

1
2
3
4
5
# nat 表:DNAT 端口映射
-A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80

# filter 表:放行到容器的转发流量
-A DOCKER -d 172.17.0.2 -p tcp --dport 80 -j ACCEPT

Docker 的端口映射 = DNAT + FORWARD 放行。这和篇 4 手动配置的 DNAT 原理完全一样。

需要注意,从宿主机本地访问 localhost:8080 时,流量走的是 OUTPUT → nat:OUTPUT → DOCKER 链,而非 PREROUTING。如果外部访问正常但本机 curl localhost:8080 不通,要检查 OUTPUT 链路径。

3.3 DOCKER-USER 链:安全加固入口

Docker 的规则会自动放行所有映射的端口,-p 8080:80 会让 8080 端口对全网开放。要限制访问源,在 DOCKER-USER 链中加规则:

1
2
3
4
5
6
7
# 只允许 10.0.0.0/8 访问 Docker 映射的端口
iptables -I DOCKER-USER -i eth0 ! -s 10.0.0.0/8 -j DROP

# 更精确:只允许特定 IP 访问特定容器端口
# DOCKER-USER 链位于 DNAT 之后,--dport 是容器端口(80),不是宿主机映射端口(8080)
iptables -I DOCKER-USER -i eth0 -p tcp --dport 80 -s 192.168.1.0/24 -j ACCEPT
iptables -I DOCKER-USER -i eth0 -p tcp --dport 80 -j DROP

⚠️ 不要在 INPUT 链加规则来限制 Docker 端口——Docker 流量走的是 FORWARD 链,不经过 INPUT。

3.4 排障:容器网络不通

场景一:容器无法访问外网

1
2
3
4
5
6
7
8
# 检查 IP 转发是否开启
cat /proc/sys/net/ipv4/ip_forward # 应该是 1

# 检查 nat 表 POSTROUTING 是否有 MASQUERADE 规则
iptables -t nat -nvL POSTROUTING | grep MASQUERADE

# 检查 FORWARD 链默认策略
iptables -nvL FORWARD # 如果 policy DROP 且没有放行规则,容器出不去

场景二:端口映射不生效

1
2
3
4
5
6
7
8
# 确认 DNAT 规则存在
iptables -t nat -nvL DOCKER | grep <容器端口>

# 确认 FORWARD 链放行了到容器的流量
iptables -nvL DOCKER | grep <容器IP>

# 检查 DOCKER-USER 链有没有误拦
iptables -nvL DOCKER-USER

Docker 场景掌握后,再进一步看 Kubernetes 如何基于同样的原理实现 Service 负载均衡。

4. Kubernetes 与 iptables

先说一个背景问题。K8s 中应用以 Pod 形式运行,Pod 的 IP 随时会变(扩容、重启、迁移节点)。如果其他服务直连 Pod IP,一旦 Pod 重建就断了。

K8s 用 Service 解决这个问题:给一组 Pod 分配一个固定的虚拟 IP(ClusterIP),访问这个 IP 的流量自动分发到后端 Pod。这个”自动分发”的底层实现,在默认模式下就是 iptables 规则。

理解这套规则,是排查 K8s 网络不通问题的前提。

4.1 kube-proxy iptables 模式原理

kube-proxy 是每个节点上运行的组件,负责把 Service 的虚拟 IP 翻译成实际 Pod 的地址。它支持两种主要模式:

iptables 模式IPVS 模式
实现方式用 iptables 规则做 DNAT + 概率负载均衡用内核 IPVS 模块做负载均衡
规则复杂度每个 Service × 每个 Pod 生成多条规则,O(n) 增长规则数量与 Service 数无关
性能拐点Service 超过 ~5000 个时延迟明显上升大规模集群仍保持稳定
负载均衡算法仅随机(概率)支持 rr、lc、sh 等多种算法
适用场景中小规模集群(默认模式)大规模生产集群

以下以 iptables 模式为例,看 kube-proxy 如何用 iptables 规则实现 Service 转发:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
    A["Pod/外部请求 → ClusterIP:Port"] --> B["KUBE-SERVICES 链(总入口)"]
    B --> C["KUBE-SVC-XXXX(某个 Service)"]
    C -->|"概率 50%"| D["KUBE-SEP-AAAA(Pod A)"]
    C -->|"概率 50%"| E["KUBE-SEP-BBBB(Pod B)"]
    D --> F["DNAT → Pod A 的 IP:Port"]
    E --> G["DNAT → Pod B 的 IP:Port"]

    classDef entry fill:#F59E0B,stroke:#D97706,color:#fff
    classDef svc fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef pod fill:#10B981,stroke:#059669,color:#fff

    class A entry
    class B,C svc
    class D,E,F,G pod

kube-proxy 创建的核心链:

链名作用
KUBE-SERVICES总入口,匹配所有 Service 的 ClusterIP
KUBE-SVC-<hash>每个 Service 一条链,负责负载均衡
KUBE-SEP-<hash>每个 Pod 端点一条链,执行 DNAT
KUBE-NODEPORTS处理 NodePort 类型的 Service

4.2 实际规则解读

假设有一个 Service my-web(ClusterIP: 10.96.0.100:80)后端有 2 个 Pod:

1
2
3
4
5
6
7
8
9
10
11
12
# 1. KUBE-SERVICES 中匹配 ClusterIP
-A KUBE-SERVICES -d 10.96.0.100/32 -p tcp --dport 80 -j KUBE-SVC-XXXX

# 2. KUBE-SVC-XXXX 做负载均衡(概率随机)
-A KUBE-SVC-XXXX -m statistic --mode random --probability 0.5 -j KUBE-SEP-AAAA
-A KUBE-SVC-XXXX -j KUBE-SEP-BBBB

# 3. KUBE-SEP-AAAA 做 DNAT 到实际 Pod
-A KUBE-SEP-AAAA -p tcp -j DNAT --to-destination 10.244.1.5:80

# 4. KUBE-SEP-BBBB 做 DNAT 到另一个 Pod
-A KUBE-SEP-BBBB -p tcp -j DNAT --to-destination 10.244.2.8:80

K8s Service 本质上是 iptables 的 DNAT + 基于概率的负载均衡。每加一个后端 Pod,kube-proxy 就自动更新这些规则。

4.3 NodePort 的实现

NodePort 类型的 Service 会在每个节点上监听一个端口(30000-32767),iptables 规则将该端口的流量转发到后端 Pod:

1
2
3
# KUBE-NODEPORTS 链匹配 NodePort
-A KUBE-NODEPORTS -p tcp --dport 30080 -j KUBE-SVC-XXXX
# 后续流程与 ClusterIP 相同

4.4 排障:Service 不通时的排查思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 确认 Service 和 Endpoints 正常
kubectl get svc my-web
kubectl get endpoints my-web

# 2. 查看 kube-proxy 生成的规则
iptables -t nat -nvL KUBE-SERVICES | grep <ClusterIP>
iptables -t nat -nvL KUBE-SVC-XXXX

# 3. 看计数器判断流量走到了哪一步
iptables -t nat --line -nvL KUBE-SVC-XXXX
# pkts 为 0 说明流量没到这条链 → 问题在上游
# pkts > 0 但 Pod 没收到 → 问题在 DNAT 之后(网络插件/Pod 本身)

# 4. 抓包验证
tcpdump -i any host <Pod-IP> -nn

# 5. 检查 conntrack 条目
conntrack -L | grep <ClusterIP>

理解了 Docker 和 K8s 的 iptables 集成,接下来看生产环境中的最佳实践和排障手段。

5. 生产环境最佳实践

5.1 规则组织:用自定义链分类

1
2
3
4
5
6
7
8
9
10
11
12
# 按功能创建自定义链
iptables -N ADMIN_ACCESS # 管理访问规则
iptables -N WEB_ACCESS # Web 服务规则
iptables -N RATE_LIMIT # 限速规则

# INPUT 链只做分发
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j WEB_ACCESS
iptables -A INPUT -p tcp --dport 22 -j ADMIN_ACCESS
iptables -A INPUT -p icmp -j RATE_LIMIT
iptables -A INPUT -j REJECT

好处:规则清晰、调试方便、团队协作时不容易冲突。

5.2 ipset:大规模 IP 管理

逐条添加 iptables 规则封禁上千个 IP 效率极低(O(n) 线性匹配)。ipset 用哈希表实现 O(1) 查找——相当于把 “ 保安逐张翻照片对比 “ 升级为 “ 人脸识别系统秒速匹配 “。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建 IP 集合
ipset create blacklist hash:ip maxelem 100000

# 批量添加 IP
ipset add blacklist 1.1.1.1
ipset add blacklist 2.2.2.2
# 或者从文件批量导入
while read ip; do ipset add blacklist "$ip"; done < bad_ips.txt

# 一条 iptables 规则引用整个集合
iptables -I INPUT -m set --match-set blacklist src -j DROP

# 查看集合
ipset list blacklist

# 持久化
ipset save > /etc/ipset.rules
ipset restore < /etc/ipset.rules

5.3 性能考量

因素影响建议
规则数量线性匹配,规则越多越慢。经验值:超过 5000 条时每包延迟增加数百微秒,K8s 大集群因此从 iptables 迁移到 IPVS高频匹配的规则放前面,大规模场景用 ipset 或 IPVS
conntrack 表大小表满了新连接被丢弃(dmesg 中出现 table full, dropping packet高并发调大 nf_conntrack_max,默认值通常为 65536
LOG 规则大量日志写磁盘有 I/O 开销生产中用 --limit 限制日志频率
MASQUERADE vs SNATMASQUERADE 每次都要查网卡 IP固定 IP 场景用 SNAT

5.4 变更管理

1
2
3
4
5
6
7
8
# 修改前备份
iptables-save > /root/iptables-$(date +%Y%m%d-%H%M%S).rules

# 定时任务自动备份
echo "0 2 * * * root iptables-save > /root/iptables-backup.rules" >> /etc/crontab

# 误操作后回滚
iptables-restore < /root/iptables-backup.rules

远程修改的安全网:at 定时回滚

修改远程服务器的 iptables 前,先设置一个定时回滚任务。如果改错导致 SSH 断开,5 分钟后规则自动恢复:

1
2
3
4
5
6
7
8
9
10
11
# 1. 先备份
iptables-save > /root/iptables-safe.rules

# 2. 设置 5 分钟后自动回滚
at now + 5 minutes <<< 'iptables-restore < /root/iptables-safe.rules'

# 3. 执行你的修改...
iptables -I INPUT ...

# 4. 确认一切正常后,取消定时回滚
atrm $(atq | awk '{print $1}')

永远不要在没有其他登录方式(VNC / 控制台)或定时回滚保护的情况下修改远程服务器的 iptables 规则。

6. 常见故障排查

6.1 排障思路总览

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
    A["网络不通"] --> B{"能 ping 通吗?"}
    B -->|"不能"| C["检查 ICMP 是否被拦截"]
    B -->|"能"| D{"服务端口通吗?"}
    D -->|"不通"| E["检查 DROP/REJECT 规则"]
    D -->|"通但异常"| F{"Docker/K8s 环境?"}
    F -->|"是"| G["检查 FORWARD 和 DOCKER-USER"]
    F -->|"否"| H["检查 conntrack 表是否满了"]
    E --> I["查看规则计数器定位匹配规则"]
    I --> J["用 TRACE 追踪完整路径"]

    classDef start fill:#EF4444,stroke:#DC2626,color:#fff
    classDef decision fill:#F59E0B,stroke:#D97706,color:#fff
    classDef action fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef trace fill:#10B981,stroke:#059669,color:#fff

    class A start
    class B,D,F decision
    class C,E,G,H,I action
    class J trace

6.2 TRACE 调试(终极排障工具)

当你不确定数据包到底被哪条规则匹配了,用 TRACE 追踪包经过的每一条链和规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 加载 TRACE 日志模块
# 内核 < 5.x
modprobe nf_log_ipv4
# 内核 >= 5.x(nf_log_ipv4 已移除,改用 nf_log_syslog)
modprobe nf_log_syslog

# 2. 在 raw 表中添加 TRACE 规则(raw 表最先执行,能捕获全部路径)
iptables -t raw -A PREROUTING -p tcp --dport 80 -s 1.1.1.1 -j TRACE
iptables -t raw -A OUTPUT -p tcp --sport 80 -d 1.1.1.1 -j TRACE

# 3. 查看 TRACE 输出
# CentOS: /var/log/messages
# Ubuntu: /var/log/kern.log 或 dmesg
tail -f /var/log/kern.log | grep TRACE

# 如果 kern.log 没有输出,尝试 xtables-monitor(nftables 兼容层)
xtables-monitor --trace

TRACE 输出示例:

1
2
3
4
TRACE: raw:PREROUTING:policy:2 IN=eth0 SRC=1.1.1.1 DST=10.0.0.5 PROTO=TCP DPT=80
TRACE: mangle:PREROUTING:policy:1 ...
TRACE: nat:PREROUTING:policy:2 ...
TRACE: filter:INPUT:rule:3 ... ← 在这里被规则 3 匹配到了!

⚠️ TRACE 产生大量日志,排查完后务必删除规则

1
2
iptables -t raw -D PREROUTING -p tcp --dport 80 -s 1.1.1.1 -j TRACE
iptables -t raw -D OUTPUT -p tcp --sport 80 -d 1.1.1.1 -j TRACE

6.3 常见故障速查表

故障现象可能原因排查命令
SSH 突然断开iptables -F 清空了规则但默认策略是 DROP通过 VNC/控制台登录,iptables -P INPUT ACCEPT
新连接被拒绝,已有连接正常conntrack 表满dmesg | grep conntrack
Docker 端口映射不生效FORWARD 链被默认 DROPiptables -nvL FORWARD 检查策略
容器之间无法通信DOCKER-ISOLATION 链拦截了跨网络流量iptables -nvL DOCKER-ISOLATION-STAGE-1
K8s Service 不通kube-proxy 规则异常或 Endpoints 为空kubectl get ep + iptables -t nat -nvL KUBE-SVC-*
规则添加了但不生效被前面的规则抢先匹配iptables --line -nvL 检查规则顺序和计数器
出站流量异常OUTPUT 链有意外规则iptables -nvL OUTPUT

7. 全系列总结

篇章核心内容
篇 1:全景认知netfilter 架构、四表五链设计动机、规则本质(匹配 + 动作)
篇 2:规则实战CRUD 操作、匹配条件、黑白名单、规则持久化
篇 3:进阶与连接跟踪conntrack 状态管理、扩展模块、SYN Flood 防护、自定义链
篇 4:NAT 与网络防火墙SNAT/DNAT、FORWARD 链、企业级网关实战
篇 5:现代运维(本篇)前端工具、nftables 迁移、Docker/K8s 集成、生产实践、TRACE 调试

8. 参考资料