Kubernetes 网络策略实战——NetworkPolicy 微隔离,限制 Pod 间通信
Kubernetes 网络策略实战——NetworkPolicy 微隔离,限制 Pod 间通信
适读人群:关注 K8s 网络安全的工程师 | 阅读时长:约 14 分钟 | 核心价值:彻底搞懂 NetworkPolicy 的配置方式,实现 Pod 级别的网络微隔离
K8s 默认的网络模型是"所有 Pod 之间都可以互通"。
这听起来很方便,但从安全角度看是个隐患。如果你的集群里跑了十几个不同团队的服务,某个前端服务被攻击者拿下,默认情况下攻击者可以直接从这个 Pod 内部访问数据库、Redis、其他微服务……
NetworkPolicy 就是用来解决这个问题的。
NetworkPolicy 的工作原理
NetworkPolicy 通过 label selector 来选择目标 Pod,然后定义这些 Pod 的入流量(ingress)和出流量(egress)规则。
重要前提:NetworkPolicy 依赖网络插件的支持。Calico、Cilium、Weave、Canal 支持 NetworkPolicy,但 Flannel 默认不支持(需要结合 Canal 使用)。
# 检查网络插件是否支持 NetworkPolicy
kubectl get pods -n kube-system | grep -E "calico|cilium|weave|canal"从零开始:命名空间默认拒绝所有流量
最佳实践是"默认拒绝,显式允许"。先为命名空间设置默认拒绝策略:
# 默认拒绝所有入流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 空选择器 = 选中命名空间所有 Pod
policyTypes:
- Ingress # 只限制入流量(更常用)
---
# 默认拒绝所有出流量(更严格,可选)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress设置了默认拒绝之后,所有 Pod 的流量都被隔断。接下来,针对每个服务的合法通信显式开放。
典型场景配置
场景一:只允许前端 Pod 访问 API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
# 目标:api 服务的 Pod
podSelector:
matchLabels:
app: api-service
policyTypes:
- Ingress
ingress:
# 只允许 frontend Pod 进来
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080场景二:数据库只允许特定应用访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: protect-database
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
# 只允许 user-service 和 order-service 访问
- from:
- podSelector:
matchLabels:
app: user-service
- podSelector:
matchLabels:
app: order-service
ports:
- protocol: TCP
port: 5432注意这里 from 里面用了两个 - podSelector,这是 OR 关系(允许 user-service 或 order-service)。
如果要用 AND 关系(既要满足 pod label 又要满足 namespace label),需要这样写:
ingress:
- from:
# AND 关系:同时满足 namespace 和 pod 两个条件
- namespaceSelector:
matchLabels:
name: production
podSelector: # 注意:没有 "-",是同一个元素的两个字段
matchLabels:
app: user-service这个 AND/OR 的写法是 NetworkPolicy 里最容易搞混的地方,下面我在踩坑里详细说。
允许特定命名空间访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring
namespace: production
spec:
podSelector: {} # 所有 Pod
policyTypes:
- Ingress
ingress:
# 允许 monitoring 命名空间的 Prometheus 来 scrape
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 9090 # metrics 端口控制出流量:限制 Pod 只能访问特定目标
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-api-egress
namespace: production
spec:
podSelector:
matchLabels:
app: api-service
policyTypes:
- Egress
egress:
# 允许访问数据库
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# 允许访问 Redis
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
# 允许 DNS 解析(非常重要!否则 DNS 会断)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 允许访问外部 HTTPS(如第三方 API)
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8 # 排除集群内网
- 172.16.0.0/12
- 192.168.0.0/16
ports:
- protocol: TCP
port: 443踩坑实录一:AND/OR 语义搞混,规则不生效
现象:想实现"只允许 production 命名空间的 frontend Pod 访问",但配置完后,其他命名空间的 Pod 也能访问。
原因:配置写成了 OR 逻辑,实际应该是 AND:
# 错误写法(OR):满足 namespace 条件 OR 满足 pod 条件,都能访问
ingress:
- from:
- namespaceSelector:
matchLabels:
name: production
- podSelector: # 注意有"-",是列表的不同元素,OR 关系
matchLabels:
app: frontend
# 正确写法(AND):必须同时满足 namespace 和 pod 两个条件
ingress:
- from:
- namespaceSelector:
matchLabels:
name: production
podSelector: # 注意没有"-",是同一元素的字段,AND 关系
matchLabels:
app: frontend一个 - 的区别,逻辑完全不同。这是 NetworkPolicy 里最经典的坑,几乎每个第一次配的人都会踩。
踩坑实录二:配了 egress 限制后,DNS 解析失败
现象:给某个 Pod 配了出流量限制,限制后 Pod 无法解析域名,访问任何服务都报 Name or service not known。
原因:DNS 解析需要访问 kube-dns(在 kube-system 命名空间),配了 egress 默认拒绝后,DNS 请求也被拦截了。
解法:在 egress 规则里显式允许 DNS:
egress:
# 放在第一条,确保 DNS 可用
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP这条规则几乎是"配了 egress 限制"的必带规则。我养成了一个习惯:只要配 egress,第一条先写 DNS 规则。
踩坑实录三:NetworkPolicy 配了但没生效,原来是网络插件不支持
现象:配了严格的 NetworkPolicy,但跑了验证脚本发现 Pod 间还是可以随意互通,NetworkPolicy 完全没起作用。
原因:集群用的是 Flannel,Flannel 默认不支持 NetworkPolicy。
验证方法:
# 测试 NetworkPolicy 是否生效
kubectl run test-pod --image=busybox --rm -it --restart=Never -n production -- \
wget -qO- --timeout=2 http://database-service:5432 2>&1
# 如果应该被拒绝但却成功了,说明 NetworkPolicy 没生效解法:
- 如果用 Flannel,需要换成 Canal(Flannel + Calico NetworkPolicy)
- 推荐直接用 Calico 或 Cilium,原生支持且功能更强
迁移网络插件是高风险操作,需要规划维护窗口。
验证 NetworkPolicy 配置的脚本
#!/bin/bash
# test-network-policy.sh
# 测试两个 Pod 之间的连通性
SOURCE_NS=${1}
SOURCE_POD=${2}
TARGET_NS=${3}
TARGET_SVC=${4}
TARGET_PORT=${5}
echo "=== 测试从 ${SOURCE_NS}/${SOURCE_POD} 到 ${TARGET_NS}/${TARGET_SVC}:${TARGET_PORT} ==="
# 在源 Pod 里尝试访问目标
kubectl exec -n ${SOURCE_NS} ${SOURCE_POD} -- \
timeout 3 bash -c "echo >/dev/tcp/${TARGET_SVC}.${TARGET_NS}/${TARGET_PORT}" 2>/dev/null \
&& echo "连通:可以访问" \
|| echo "不通:被 NetworkPolicy 阻断(或服务不存在)"NetworkPolicy 是 K8s 里很容易被忽略的安全功能,但对于有安全合规要求的场景,它是必须配置的。建议从"默认拒绝 + 显式开放"的思路出发,逐个服务梳理通信关系,然后按图索骥配置规则。
