iptables —— Linux 内核包过滤 / NAT 规则
一句话定义
iptables 是 Linux 内核网络框架 netfilter 的用户态配置工具。所有进出本机的数据包都会经过 netfilter 钩子;iptables 决定怎么过滤、转发、NAT、改写这些包。K8s 的 kube-proxy 默认用 iptables 实现 service —— 你要看懂"为什么 ClusterIP 能通到 pod",必须看懂 iptables 链。
典型场景
训练营文档里 iptables 出场 42 次,主要在:
- 看 kube-proxy 生成的规则:
iptables -t nat -L KUBE-SERVICES - 排查 NodePort / ClusterIP 不通:链路追踪
- 集群清理:
iptables -F && iptables -t nat -F - 防火墙基础:放行/禁止端口
现代发行版正在迁移到 nftables(
iptables-nft),命令兼容、底层不同。但 K8s 1.31 之前仍以 iptables 模式为主,所以这篇内容仍然适用。
心智模型:5 个表 / 5 条链 / 链路顺序
表(table)—— 干啥用
| 表 | 用途 | 你需要懂 |
|---|---|---|
filter | 包过滤(接受 / 拒绝) | ✅ 必懂 |
nat | 地址转换(DNAT / SNAT) | ✅ K8s service 全靠它 |
mangle | 改包头(TTL / TOS / mark) | 看懂就行 |
raw | 跳过 conntrack | 性能调优 |
security | SELinux / MAC | 几乎不用 |
-t <table> 指定表,不指定默认是 filter。
链(chain)—— 什么时候跑
| 链 | 时机 |
|---|---|
PREROUTING | 包刚到,路由决策之前 |
INPUT | 路由后,发往本机的包 |
FORWARD | 路由后,要转发出去的包 |
OUTPUT | 本机产生的包 |
POSTROUTING | 出去之前,路由决策之后 |
不是所有表都有所有链:
| PREROUTING | INPUT | FORWARD | OUTPUT | POSTROUTING | |
|---|---|---|---|---|---|
| filter | ✓ | ✓ | ✓ | ||
| nat | ✓ | ✓ | ✓ | ✓ | |
| mangle | ✓ | ✓ | ✓ | ✓ | ✓ |
数据包流动路径
入站包(发给本机):
网卡 → raw PREROUTING → mangle PREROUTING → nat PREROUTING → 路由决策
│
├─发给本机→ mangle INPUT → filter INPUT → nat INPUT → 本地进程
│
└─转发→ mangle FORWARD → filter FORWARD → mangle POSTROUTING → nat POSTROUTING → 网卡
出站包(本机发起):
本地进程 → raw OUTPUT → mangle OUTPUT → nat OUTPUT → filter OUTPUT → mangle POSTROUTING → nat POSTROUTING → 网卡
不需要背 —— 但要知道:DNAT 在 PREROUTING,SNAT 在 POSTROUTING。这就是为什么 K8s 的 KUBE-SERVICES 主要挂在 nat 表的 PREROUTING 和 OUTPUT。
最常用命令(90% 用法)
iptables -L # 列 filter 表所有规则
iptables -L -n -v # -n 不解析名字、-v 含计数
iptables -L INPUT -n -v --line-numbers # 看 INPUT 链,加行号
iptables -t nat -L # 看 nat 表
iptables -t nat -L KUBE-SERVICES -n # 看 K8s 那条
iptables -S # 以"可重放"的命令形式列出
iptables -S KUBE-SERVICES # 单链
| flag | 含义 |
|---|---|
-L [chain] | 列出规则 |
-S [chain] | 以命令形式列出(复制粘贴友好) |
-t <table> | 指定表(默认 filter) |
-n | 不解析 IP / port 名字(必加,否则慢) |
-v | 含字节 / 包计数 |
--line-numbers | 加行号(删特定规则时要) |
记住 -t nat -L -n -v --line-numbers 这套组合:K8s 排错最常用。
添加 / 删除规则
# 添加:放行 8080 端口
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
# 添加到指定位置:放在第 1 行
iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT
# 删除:按内容
iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
# 删除:按行号
iptables -D INPUT 3 # 删第 3 条规则
# 清空一整条链
iptables -F INPUT # flush
# 清空所有规则
iptables -F # filter 表所有链
iptables -t nat -F
iptables -t mangle -F
iptables -X # 删自定义链
-A (append) / -I (insert) / -D (delete) / -F (flush) / -X (delete custom chain) / -N (new chain)。
常用 match 条件
-p tcp / udp / icmp # 协议
-s <src> / -d <dst> # 源/目的 IP(含 CIDR)
--sport <port> / --dport <port> # 源/目的端口
-i <iface> / -o <iface> # 入/出网卡
-m state --state ESTABLISHED,RELATED # 连接状态(依赖 conntrack)
-m conntrack --ctstate ESTABLISHED # 新写法
-m multiport --dports 80,443,8080 # 多端口
-m mark --mark 0x1 # 含 mark
-j 跳转(action)
| 目标 | 含义 |
|---|---|
ACCEPT | 接受 |
DROP | 丢弃(对方不知道,体现为超时) |
REJECT | 拒绝(发 ICMP unreachable,对方立刻知道) |
LOG | 记日志(dmesg 里看) |
RETURN | 从子链返回上一层 |
DNAT --to-destination IP:port | 目的 NAT(改目标 IP) |
SNAT --to-source IP | 源 NAT |
MASQUERADE | 动态 SNAT(出口 IP 自动选) |
| 自定义链名 | 跳到自定义链 |
K8s 视角:看 kube-proxy 生成的链
K8s 把 kube-proxy 创建的规则分成几条"主链":
filter 表:
KUBE-EXTERNAL-SERVICES ← external service
KUBE-FIREWALL ← 内部防火墙
KUBE-FORWARD ← pod 转发策略
KUBE-NODEPORTS ← NodePort 入口
nat 表:
KUBE-SERVICES ← 所有 service 的入口(最重要)
KUBE-NODEPORTS ← NodePort 服务
KUBE-MARK-MASQ ← 打标记给后续 SNAT
KUBE-POSTROUTING ← 做 SNAT
KUBE-SVC-XXXXX ← 每个 service 一条
KUBE-SEP-XXXXX ← 每个 endpoint 一条
看一个 service 怎么走
# 找你的 service
kubectl get svc -A | grep myapp
# default myapp ClusterIP 10.96.1.5 ... 80/TCP
# 在节点上看 nat 表 KUBE-SERVICES 链
iptables -t nat -L KUBE-SERVICES -n | grep 10.96.1.5
# Chain KUBE-SERVICES (2 references)
# target prot opt source destination
# KUBE-SVC-MYAPP123 tcp -- 0.0.0.0/0 10.96.1.5 /* default/myapp:http cluster IP */ tcp dpt:80
# 跳到那个 svc 链
iptables -t nat -L KUBE-SVC-MYAPP123 -n
# KUBE-SEP-EP1 ... /* probability 0.3333 */
# KUBE-SEP-EP2 ... /* probability 0.5 */
# KUBE-SEP-EP3 ... /* */
# 看一个 endpoint 链
iptables -t nat -L KUBE-SEP-EP1 -n
# DNAT tcp ... to:10.244.1.5:8080
ClusterIP 通信流程:
- pod 发请求到
10.96.1.5:80 - 包经
nat PREROUTING→ 进KUBE-SERVICES链 - 匹配
KUBE-SVC-MYAPP123子链 - 随机选一个 endpoint(用
statisticmatch probability) - 跳进
KUBE-SEP-EPx子链 - DNAT 改目的 IP 到
10.244.1.5:8080(真实 pod IP) - 路由表把包发到那个 pod
排查 ClusterIP 不通:沿这条链查规则在不在、计数有没有递增。
iptables -t nat -L KUBE-SERVICES -n -v | grep myapp
# 看 pkts 列:每次有人访问 ClusterIP 应该递增
# 不递增 = kube-proxy 没生成规则、或者 service selector / endpoint 有问题
NodePort 怎么走
iptables -t nat -L KUBE-NODEPORTS -n
# KUBE-EXT-MYAPP123 tcp ... tcp dpt:30080
30080 是 NodePort,进来后跳到 KUBE-EXT-MYAPP123 → 走和 ClusterIP 一样的链。
外部访问 <node-ip>:30080 通:
- 包到 PREROUTING
- 进 NodePorts 链
- DNAT 到某 pod IP
- 包出去(也可能要 SNAT)
conntrack(连接追踪)—— iptables 的"记忆"
# 看 conntrack 表
cat /proc/net/nf_conntrack | head
# 或者
apt install conntrack
conntrack -L | head
netfilter 用 conntrack 记忆每个连接的状态。-m state --state ESTABLISHED,RELATED 就是问 conntrack。
排查"连接数耗尽":
cat /proc/sys/net/netfilter/nf_conntrack_count # 当前连接数
cat /proc/sys/net/netfilter/nf_conntrack_max # 上限
满了:包被 nf_conntrack: table full, dropping packet(看 dmesg)。
调高:
sysctl -w net.netfilter.nf_conntrack_max=1048576
K8s 节点跑大流量必调。
看规则计数(排查"规则没生效")
iptables -L INPUT -n -v
# pkts bytes target prot opt in out source destination
# 12345 1.2M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
pkts 是命中这条规则的包数。每次连接进来应该递增。
规则没生效的常见诊断:
# 重置计数
iptables -Z
# 让目标流量跑一会
curl http://node-ip:30080
# 再看计数
iptables -L -n -v | grep 30080
# 如果计数 0 → 规则没匹配到,看规则顺序 / 条件
训练营典型用法
1. 装机审计:看默认规则
iptables -L -n -v
iptables -t nat -L -n -v
# 干净的 Ubuntu 应该几乎没规则(默认 policy ACCEPT)
# 如果 IDC 塞了一堆规则 → 可能干扰 K8s
2. reset 之后清理(Day1 / 故障排查必做)
iptables -F # 清 filter
iptables -X # 删自定义链
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT # 重置默认 policy
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
不清 iptables 直接
kubeadm reset再init是 K8s 重装失败的 #1 原因。kube-proxy 第二次 init 时遇到一堆历史规则,行为不可预测。
3. 排查 service 不通
# 1. service 有没有 endpoint
kubectl get endpoints myapp
# 没有 → selector 选不到 pod / pod 没 ready
# 2. iptables 有没有规则
iptables -t nat -L KUBE-SERVICES -n | grep myapp
# 没有 → kube-proxy 出问题、看它日志
# 3. 规则有没有被命中
iptables -t nat -L KUBE-SERVICES -n -v | grep myapp
# pkts 0 → 流量没到这里,路由问题
# 4. DNAT 之后路由
ip route get <pod-ip>
# 不通 → CNI 问题
4. 允许只从特定 IP ssh 进来
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
先 ACCEPT、再 DROP(顺序敏感)。否则 DROP 在前 → 谁都连不进。
iptables vs nftables vs IPVS
| 工具 | 用途 | 性能 |
|---|---|---|
iptables | 老工具 | 规则多时慢(线性查找) |
iptables-nft | 兼容包装,底层是 nftables | 同上但慢慢被推广 |
nft (nftables) | iptables 继任者 | 更好,但语法不同 |
| IPVS | 内核 L4 LB | 规则多时显著快,专为 LB 设计 |
K8s kube-proxy 支持两种模式:
kubectl get configmap -n kube-system kube-proxy -o yaml | grep mode
# mode: "" ← 默认 iptables
# mode: "ipvs" ← IPVS
万级 service 推荐切 IPVS。看 IPVS 用 ipvsadm 而不是 iptables。
持久化 iptables 规则
iptables 规则只在内存,重启就没。要持久:
Ubuntu
apt install iptables-persistent
# 提示是否保存当前规则到 /etc/iptables/rules.v4 和 rules.v6
netfilter-persistent save
netfilter-persistent reload
通用:导出 / 导入
iptables-save > /etc/iptables/rules.v4
iptables-restore < /etc/iptables/rules.v4
但 K8s 节点上 kube-proxy 自己管自己的规则——你不应该手动持久化它的链,否则升级 / reset 出乱。手动加的规则要放在 K8s 之外的链(比如自定义链)。
常见踩坑
坑 1:iptables 命令显示一堆乱七八糟,看不懂
iptables -L
# 一坨 chain,K8s 装完几百条规则
加 -n:
iptables -L -n -v --line-numbers
不加 -n iptables 会反向 DNS / 解析端口名,慢且没用。
坑 2:规则顺序错,DROP 先于 ACCEPT
iptables -A INPUT -j DROP # 先 DROP 一切
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 再放行 22 ← 永远到不了这里
iptables 按顺序匹配,第一个匹配的 -j 就执行了。ACCEPT 放前面 / DROP 放最后。
坑 3:用 iptables -F 顺手把自己锁外面
ssh m1 'iptables -F && iptables -P INPUT DROP'
# ❌ -P INPUT DROP 把默认 policy 设成 DROP,然后 flush 之后没规则放行 22
# 立刻断 ssh,再也连不上
修改 INPUT 链规则之前先:
iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT # 第 1 行放行 22(救命通道)
# 再做其它修改
并保留物理 console 备份。
坑 4:清不干净 K8s 残留
kubeadm reset
kubeadm init # 失败:iptables 残留规则冲突
reset 不清 iptables。手动:
iptables -F && iptables -t nat -F && iptables -t mangle -F
iptables -X && iptables -t nat -X && iptables -t mangle -X
或者 ipvsadm -C(IPVS 模式)。
坑 5:iptables 规则在但流量不通
可能是 nftables 兼容层 问题。看实际后端:
update-alternatives --display iptables
# /usr/sbin/iptables-nft ← 现在用 nftables 后端
# /usr/sbin/iptables-legacy ← 老的
# K8s 1.21+ 推荐 nft 模式
update-alternatives --set iptables /usr/sbin/iptables-nft
部分老组件(calico / flannel 老版本)只认 iptables-legacy,混用就乱套。
坑 6:在节点上看到的规则与 kube-proxy 期望不一致
kube-proxy 是个 daemon,每隔几秒 reconcile 一次规则。如果你手动改了 kube-proxy 的链 —— 几秒后被改回来。
要持久改:去改 kube-proxy 的 configmap、或者在它管不到的链上加。
坑 7:conntrack 表满,连接随机失败
# dmesg
nf_conntrack: table full, dropping packet
调高 net.netfilter.nf_conntrack_max、调短 conntrack timeout。K8s 节点 / Ingress 节点必要的运维项。
坑 8:用 service iptables save (CentOS 6 老语法)
service iptables save # ❌ 现代系统不存在这服务
用 iptables-save > file 或 netfilter-persistent save。