为什么 iptables INPUT 链拦截不到 Kubernetes Service 流量?

为什么 iptables INPUT 链拦截不到 Kubernetes Service 流量?

问题背景

很多运维同学在 Kubernetes 集群中遇到一个困惑:明明在节点的 iptables INPUT 链上添加了拒绝规则,但 Service 的流量依然可以正常访问。这是为什么?

一句话答案:Kubernetes Service 的流量通常不走 INPUT 链,而是走 FORWARD 链。


深入理解 Kubernetes 网络流量路径

Service 的 DNAT 机制

Kubernetes Service 使用 kube-proxy 的 iptables 模式实现负载均衡。当外部流量访问 Service IP 时,会发生以下过程:

外部流量 → 节点网卡 → PREROUTING 链 (DNAT: Service IP → Pod IP)
                                    ↓
                    内核重新进行路由决策(目标IP已改变)
                                    ↓
              ┌─────────────────────┴─────────────────────┐
              ↓                                           ↓
        Pod 在本节点                              Pod 在远程节点
              ↓                                           ↓
          INPUT 链                                  FORWARD 链
              ↓                                           ↓
        本地 Pod 接收                              通过 CNI 转发到远程节点

关键转折点:PREROUTING DNAT

这是问题的核心!kube-proxy 在 nat 表的 PREROUTING 链上做了 DNAT:

# 查看 kube-proxy 的 DNAT 规则
iptables -t nat -L KUBE-SVC-XXXXX -n -v

DNAT 之后,数据包的目标 IP 从 Service IP 变成了 Pod IP。内核重新路由时:

Pod 位置 路由决策 经过的链
本节点 本地交付 PREROUTING → INPUT
远程节点 转发 PREROUTING → FORWARD

结论:只有当 Pod 运行在当前节点时,流量才会经过 INPUT 链。跨节点流量走 FORWARD 链。


实战演示

场景设置

集群拓扑:
  节点1 (192.168.1.10): Pod A (10.244.1.5)
  节点2 (192.168.1.11): Pod B (10.244.2.5)

Service: my-service (10.96.100.100:80)
后端: Pod A + Pod B (轮询)

错误做法:在 INPUT 链拦截

# ❌ 这只能拦截访问本节点 Pod 的流量
iptables -A INPUT -d 10.96.100.100 -j DROP

结果
– 访问 Service → Pod A(本节点):✅ 被拦截
– 访问 Service → Pod B(远程节点):❌ 拦截失败

正确做法:在 FORWARD 链拦截

# ✅ 拦截所有转发流量(包括跨节点 Service 流量)
iptables -A FORWARD -d 10.96.100.100 -j DROP

# 或者更精确:只拦截特定端口
iptables -A FORWARD -p tcp -d 10.96.100.100 --dport 80 -j DROP

三种拦截方案对比

方案 1:FORWARD 链拦截(推荐)

# 拦截 Service IP
iptables -I FORWARD -d 10.96.100.100 -j DROP

# 拦截特定 Service 端口
iptables -I FORWARD -p tcp -d 10.96.100.100 --dport 80 -j DROP

# 拦截整个命名空间的 Pod 流量(需要知道 CIDR)
iptables -I FORWARD -s 10.244.2.0/24 -j DROP

优点
– ✅ 覆盖所有流量路径(本节点 + 跨节点)
– ✅ 规则简单直观
– ✅ 性能开销小

缺点
– ⚠️ 影响所有转发流量,需要精确匹配


方案 2:PREROUTING 链拦截(更早)

# 在 DNAT 之前拦截(nat 表)
iptables -I nat PREROUTING -d 10.96.100.100 -j DROP

# 或者使用 raw 表(最早阶段)
iptables -t raw -I PREROUTING -d 10.96.100.100 -j DROP

优点
– ✅ 在 DNAT 之前拦截,规则匹配的是原始 Service IP
– ✅ 性能最优(最早丢弃,不消耗后续资源)

缺点
– ⚠️ raw/nat 表的规则优先级需要仔细调整
– ⚠️ 可能影响 kube-proxy 的正常 DNAT


方案 3:Kubernetes NetworkPolicy(最佳实践)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-service-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: my-app
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.96.100.100/32  # 拒绝访问此 Service

优点
– ✅ Kubernetes 原生方式,声明式管理
– ✅ 自动处理 Pod 移动、扩缩容
– ✅ 与 K8s 生态无缝集成
– ✅ 支持命名空间级别策略

缺点
– ⚠️ 需要 CNI 插件支持(Calico、Cilium 等)
– ⚠️ 学习曲线稍高


调试技巧

1. 查看流量路径

# 追踪路由决策
ip route get 10.96.100.100

# 查看连接跟踪
conntrack -L | grep 10.96.100.100

# 抓包分析
tcpdump -i any host 10.96.100.100 -nn

2. 检查 kube-proxy 规则

# 查看 Service 对应的 iptables 规则
iptables -t nat -L -n -v | grep -A10 "KUBE-SVC"

# 查找特定 Service
kubectl get svc -o wide | grep 10.96.100.100
kubectl get endpoints my-service -o yaml

3. 验证规则是否生效

# 添加 LOG 规则调试
iptables -I FORWARD -d 10.96.100.100 -j LOG --log-prefix "BLOCKED: "

# 查看内核日志
dmesg | grep "BLOCKED"
journalctl -k | grep "BLOCKED"

# 检查规则计数器
iptables -L FORWARD -n -v | grep 10.96.100.100

4. 完整诊断脚本

#!/bin/bash
# k8s-iptables-debug.sh

SERVICE_IP="10.96.100.100"
SERVICE_PORT="80"

echo "=========================================="
echo "Kubernetes iptables 流量诊断"
echo "=========================================="

echo -e "n[1] 检查 Service 是否存在"
kubectl get svc -A --field-selector spec.clusterIP=$SERVICE_IP

echo -e "n[2] 检查后端 Pod 分布"
kubectl get endpoints $SERVICE_IP -o yaml 2>/dev/null || 
    kubectl get svc -o jsonpath='{.spec.clusterIP}' | xargs -I{} kubectl get pods -A -o wide | grep {}

echo -e "n[3] 查看 FORWARD 链规则"
iptables -L FORWARD -n -v --line-numbers | head -20

echo -e "n[4] 查看 nat PREROUTING 规则"
iptables -t nat -L PREROUTING -n -v --line-numbers | head -20

echo -e "n[5] 测试连通性"
curl -v --connect-timeout 3 http://${SERVICE_IP}:${SERVICE_PORT} 2>&1 | head -10

echo -e "n[6] 检查 conntrack"
conntrack -L | grep $SERVICE_IP | head -5

echo -e "n=========================================="

常见场景解决方案

场景 1:禁止访问特定 Service

# 推荐:FORWARD 链
iptables -I FORWARD -d 10.96.100.100 -j DROP

# 备选:NetworkPolicy
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-to-service
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except: [10.96.100.100/32]
EOF

场景 2:禁止特定 Pod 访问 Service

# 使用 NetworkPolicy(推荐)
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-pod-to-service
spec:
  podSelector:
    matchLabels:
      app: restricted-app
  policyTypes: [Egress]
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except: [10.96.100.100/32]
EOF

场景 3:仅允许特定 IP 访问 Service

# FORWARD 链白名单
iptables -F FORWARD
iptables -P FORWARD DROP
iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT
iptables -A FORWARD -d 10.96.100.100 -j DROP

规则持久化

CentOS/RHEL

yum install iptables-services
iptables-save > /etc/sysconfig/iptables
systemctl restart iptables

Ubuntu/Debian

apt install iptables-persistent
netfilter-persistent save

Kubernetes 节点特殊处理

由于 kube-proxy 会动态修改 iptables,持久化时需要:

# 方法 1:使用 DaemonSet 注入规则
# 方法 2:在 kube-proxy 启动后执行(推荐)
systemctl restart kube-proxy
sleep 5
iptables-save > /etc/sysconfig/iptables

总结

问题 原因 解决方案
INPUT 链拦截失败 跨节点流量走 FORWARD 在 FORWARD 链添加规则
规则被 kube-proxy 覆盖 规则顺序问题 使用 -I 插入到链头
DNAT 后匹配不到 目标 IP 已改变 在 PREROUTING 链拦截
规则重启失效 未持久化 使用 iptables-persistent

最佳实践建议

  1. 优先使用 NetworkPolicy:Kubernetes 原生方式,声明式管理,自动适应 Pod 变化
  2. 节点级控制用 FORWARD 链:覆盖所有流量路径,简单可靠
  3. 调试先用 LOG 规则:确认流量路径后再添加 DROP
  4. 注意 kube-proxy 规则顺序:自定义规则放在 kube-proxy 规则之前

提示:在生产环境修改 iptables 规则时,建议:
– 先通过 SSH 测试新规则
– 设置定时恢复脚本(如 5 分钟后自动恢复)
– 使用 iptables-save 备份当前规则集

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容