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 kernel1.1 firewalld(CentOS 7+ / RHEL 7+)
firewalld 引入了 zone(区域)的概念,将网卡分配到不同区域,每个区域有预定义的安全策略。
| 特性 | firewalld | iptables 直接操作 |
|---|---|---|
| 操作方式 | 通过 D-Bus 接口动态增删,不中断现有连接 | -A/-D 也是增量操作,但 iptables-restore 全量重载会短暂中断 |
| 抽象层级 | zone 场景化管理,不需要理解四表五链 | 需要精确理解链和表的关系 |
| 底层 | 调用 iptables 或 nftables | 直接操作 netfilter |
firewalld 和 iptables 命令不要同时使用,会造成规则冲突。选一个用。
1.2 ufw(Ubuntu)
UFW(Uncomplicated Firewall)是 Ubuntu 默认的防火墙前端,追求极简操作:ufw allow 22/tcp、ufw 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 核心概念对比
| iptables | nftables | 说明 |
|---|---|---|
| 预定义 4 张表 | 用户自定义表 | nftables 没有预设表,完全由你定义 |
| 预定义 5 条链 | 用户自定义链 | 需要手动创建并绑定到钩子 |
| 每条规则一个动作 | 一条规则多个动作 | 可以同时 log 和 drop |
| 需要 ipset 管理 IP 集合 | 内置 set 和 map | 原生支持集合和字典 |
| iptables / ip6tables / ebtables | 统一使用 nft 命令 | 一个工具管所有 |
2.3 语法对比速查
1 | # ========== 查看规则 ========== |
2.4 iptables-nft 兼容层
很多现代发行版上,/usr/sbin/iptables 实际指向 iptables-nft——你用 iptables 语法写命令,后端自动翻译成 nftables 规则。
1 | # 检查你的系统用的是哪个 |
如果输出包含
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 | # 查看 Docker 创建的 nat 规则 |
Docker 创建的核心链:
| 链名 | 所在表 | 作用 |
|---|---|---|
| DOCKER | nat + filter | nat 表中做 DNAT(端口映射),filter 表中做端口放行 |
| DOCKER-USER | filter | 用户自定义规则的入口,优先于 Docker 自动规则 |
| DOCKER-ISOLATION-STAGE-1/2 | filter | 实现不同 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 | # nat 表:DNAT 端口映射 |
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 | # 只允许 10.0.0.0/8 访问 Docker 映射的端口 |
⚠️ 不要在 INPUT 链加规则来限制 Docker 端口——Docker 流量走的是 FORWARD 链,不经过 INPUT。
3.4 排障:容器网络不通
场景一:容器无法访问外网
1 | # 检查 IP 转发是否开启 |
场景二:端口映射不生效
1 | # 确认 DNAT 规则存在 |
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 podkube-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 | # 1. KUBE-SERVICES 中匹配 ClusterIP |
K8s Service 本质上是 iptables 的 DNAT + 基于概率的负载均衡。每加一个后端 Pod,kube-proxy 就自动更新这些规则。
4.3 NodePort 的实现
NodePort 类型的 Service 会在每个节点上监听一个端口(30000-32767),iptables 规则将该端口的流量转发到后端 Pod:
1 | # KUBE-NODEPORTS 链匹配 NodePort |
4.4 排障:Service 不通时的排查思路
1 | # 1. 确认 Service 和 Endpoints 正常 |
理解了 Docker 和 K8s 的 iptables 集成,接下来看生产环境中的最佳实践和排障手段。
5. 生产环境最佳实践
5.1 规则组织:用自定义链分类
1 | # 按功能创建自定义链 |
好处:规则清晰、调试方便、团队协作时不容易冲突。
5.2 ipset:大规模 IP 管理
逐条添加 iptables 规则封禁上千个 IP 效率极低(O(n) 线性匹配)。ipset 用哈希表实现 O(1) 查找——相当于把 “ 保安逐张翻照片对比 “ 升级为 “ 人脸识别系统秒速匹配 “。
1 | # 创建 IP 集合 |
5.3 性能考量
| 因素 | 影响 | 建议 |
|---|---|---|
| 规则数量 | 线性匹配,规则越多越慢。经验值:超过 5000 条时每包延迟增加数百微秒,K8s 大集群因此从 iptables 迁移到 IPVS | 高频匹配的规则放前面,大规模场景用 ipset 或 IPVS |
| conntrack 表大小 | 表满了新连接被丢弃(dmesg 中出现 table full, dropping packet) | 高并发调大 nf_conntrack_max,默认值通常为 65536 |
| LOG 规则 | 大量日志写磁盘有 I/O 开销 | 生产中用 --limit 限制日志频率 |
| MASQUERADE vs SNAT | MASQUERADE 每次都要查网卡 IP | 固定 IP 场景用 SNAT |
5.4 变更管理
1 | # 修改前备份 |
远程修改的安全网:at 定时回滚
修改远程服务器的 iptables 前,先设置一个定时回滚任务。如果改错导致 SSH 断开,5 分钟后规则自动恢复:
1 | # 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 trace6.2 TRACE 调试(终极排障工具)
当你不确定数据包到底被哪条规则匹配了,用 TRACE 追踪包经过的每一条链和规则:
1 | # 1. 加载 TRACE 日志模块 |
TRACE 输出示例:
1 | TRACE: raw:PREROUTING:policy:2 IN=eth0 SRC=1.1.1.1 DST=10.0.0.5 PROTO=TCP DPT=80 |
⚠️ 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 链被默认 DROP | iptables -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 调试 |