使用 Init Container + iptables 强制隔离 Pod 网络(Flannel 环境)
背景
Flannel 是最常用的 Kubernetes CNI 插件之一,但其本身不支持 NetworkPolicy。在 Flannel 环境下,要实现 Pod 级别的网络隔离,可以采用在 Pod 启动前通过 Init Container 设置 iptables 规则的方法。这种方法也可以用于对 NetworkPolicy 进行额外加固。
原理
Kubernetes 中每个 Pod 内的所有容器(包括 Init Container)共享同一个网络命名空间(由 pause 容器创建)。Init Container 在主容器启动之前运行,因此可以在 Init Container 中执行 iptables 命令,为主容器预设网络访问控制规则。这样主容器启动后,其网络流量就会受到这些规则的约束。
实施步骤
1. 确定关键信息
- Pod CIDR :集群中 Pod 的网段,用于放行同网段流量。例如
10.244.0.0/16。 - DNS 服务 IP :CoreDNS 的 ClusterIP,通常是
kube-system命名空间下kube-dns服务的 ClusterIP,例如10.96.0.10。 - Kubernetes API Server IP(可选):如果 Pod 需要访问 API Server,需要放行。
2. 设计 iptables 规则
目标:默认丢弃所有出站和入站流量,仅放行:
- 回环接口(lo)的流量
- DNS 查询(UDP/TCP 53 到 DNS 服务 IP)
- 同网段 Pod 之间的流量(源或目标为 Pod CIDR)
- 可能需要的其他白名单(如访问外部特定服务)
3. 编写 Init Container
使用带有 iptables 工具的基础镜像,例如 busybox 或 alpine(需要安装 iptables)。推荐使用 docker.io/library/busybox:latest,因为 busybox 自带 iptables(较新版本)。或者使用 ghcr.io/linuxserver/iptables 等专用镜像。
Init Container 脚本示例:
yaml
initContainers:
- name: iptables-init
image: busybox:1.36
securityContext:
capabilities:
add:
- NET_ADMIN
command:
- sh
- -c
- |
set -ex
# 设置默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
# 放行回环接口
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# 放行 DNS(假设 DNS IP 为 10.96.0.10)
iptables -A OUTPUT -p udp --dport 53 -d 10.96.0.10 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -d 10.96.0.10 -j ACCEPT
# 允许 DNS 响应进入(状态跟踪)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 放行同网段 Pod 流量
iptables -A INPUT -s 10.244.0.0/16 -j ACCEPT
iptables -A OUTPUT -d 10.244.0.0/16 -j ACCEPT
# 可选:放行访问 API Server(如果 Pod 需要)
# iptables -A OUTPUT -d <api-server-ip> -p tcp --dport 6443 -j ACCEPT
# 完成,记录日志
echo "iptables rules applied"
4. 完整 Pod 示例
yaml
apiVersion: v1
kind: Pod
metadata:
name: isolated-pod
namespace: your-namespace
spec:
initContainers:
- name: iptables-init
image: busybox:1.36
securityContext:
capabilities:
add:
- NET_ADMIN
command:
- sh
- -c
- |
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -d 10.96.0.10 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -d 10.96.0.10 -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -s 10.244.0.0/16 -j ACCEPT
iptables -A OUTPUT -d 10.244.0.0/16 -j ACCEPT
containers:
- name: app
image: nginx:alpine
ports:
- containerPort: 80
5. 应用与验证
- 创建 Pod,查看 Init Container 日志确认规则已应用。
- 进入主容器,测试网络连通性:
- 访问同命名空间其他 Pod 的 IP,应该成功。
- 访问外部网站(如
curl https://www.google.com),应该失败(因为出站被 DROP)。 - 解析域名(
nslookup kubernetes.default),应该成功(DNS 被放行)。 - 访问 API Server,根据是否放行而定。
6. 注意事项与限制
- 权限要求 :Init Container 需要
NET_ADMINcapability,否则无法修改 iptables。 - 规则持久性:iptables 规则只在当前网络命名空间有效,Pod 重启后需要重新执行 Init Container。
- Pod 级别隔离:此方法只能对单个 Pod 设置规则,若要应用于命名空间内所有 Pod,需要为每个 Pod 模板添加 Init Container,或者使用 Admission Webhook(如 Kyverno、OPA)自动注入。
- 性能影响:每个 Pod 启动时多一个 Init Container,略有开销,但可以接受。
- 维护复杂度:与 NetworkPolicy 相比,手动管理 iptables 规则更容易出错,且不易审计。
- 推荐场景:Flannel 环境下需要临时加固,或者作为额外防护层,但长期建议迁移到支持 NetworkPolicy 的 CNI。
总结
本文介绍了两种实现 Kubernetes 命名空间/Pod 网络隔离的方法:使用 NetworkPolicy(标准做法,适用于 Calico/Cilium)和使用 Init Container + iptables(适用于 Flannel 或特殊加固需求)。两者各有优劣,可根据集群实际情况选择。