摘要:使用 NetworkPolicy 限制 Pod 入站和出站流量,理解 Pod 与命名空间选择器、默认拒绝、规则叠加、DNS 放行及网络插件的能力边界。
这一节开始学习 Kubernetes 网络安全。
核心问题是:如何限制 Pod 之间的通信,只允许经过授权的应用访问指定服务?
例如,一个典型的三层应用:
- 前端 Pod:对外提供 Web 服务。
- 后端 Pod:提供业务 API。
- 数据库 Pod:保存业务数据。
我们希望实现:
- 只有前端可以访问后端。
- 只有后端可以访问数据库。
- 其他 Pod 不能随意访问数据库。
- 必要的 DNS 查询仍然可以正常工作。
这就是 NetworkPolicy 的主要用途。
一、为什么需要 NetworkPolicy?
假设集群中有三个应用:
flowchart TB
N0["Frontend Pod<br/>前端应用 · 8080"]
N1["Backend Pod<br/>业务 API · 8080"]
N2["Database Pod<br/>数据库 · 5432"]
N3["其他 Pod<br/>没有授权时,不应直接访问数据库。"]
N0 -->|"允许访问"| N1
N1 -->|"允许访问"| N2
N3 --x|"禁止访问"| N2
在许多 Kubernetes 网络插件的默认配置下,同一集群中的 Pod 可以直接通过 Pod IP 通信。Service 并不是强制经过的安全网关。
这意味着,即使你只给数据库创建了 ClusterIP Service,其他具有网络连通性的 Pod 仍可能直接访问数据库 Pod IP。
因此,Service 负责提供服务入口,NetworkPolicy 负责声明网络访问规则,两者解决的问题不同。
二、NetworkPolicy 的工作原理
NetworkPolicy 是 Kubernetes 的网络策略 API,使用标签选择器指定受保护的 Pod,并定义允许哪些入站或出站流量。
它有三个核心概念:
- PodSelector:选择哪些 Pod 应用这条策略。
- Ingress:控制进入所选 Pod 的流量。
- Egress:控制从所选 Pod 发出的流量。
例如,下面这条策略选择所有 app=database 的 Pod,并禁止它们接收未被其他策略允许的入站流量。
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: database-deny-ingress namespace: defaultspec: podSelector: matchLabels: app: database policyTypes: - Ingress ingress: []这条策略的含义是:
- 保护
defaultNamespace 中带有app=database标签的 Pod。 - 不允许任何入站连接,除非有其他适用策略也允许该流量。
- 不会限制这些 Pod 的出站流量。
注意,NetworkPolicy 只是策略声明,真正执行规则需要支持 NetworkPolicy 的网络插件。常见的 Calico 和 Cilium 都支持这类策略,但具体能力和行为应以实际部署为准。
三、入站策略:只允许前端访问后端
我们先实现一个常见的生产需求:
只有 app=frontend 的 Pod 可以访问 app=backend 的 Pod 的 TCP 8080 端口。
假设 Pod 已经带有以下标签:
# 前端 Pod 标签app: frontend
# 后端 Pod 标签app: backend创建策略:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: backend-ingress namespace: defaultspec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080逐字段理解
| 配置 | 含义 |
|---|---|
podSelector | 选择被保护的后端 Pod |
policyTypes: Ingress | 声明该策略控制入站流量 |
from | 定义允许的来源 |
podSelector | 允许来源必须具有 app=frontend 标签 |
ports | 只允许 TCP 8080 端口 |
应用配置:
kubectl apply -f backend-ingress.yaml
kubectl describe networkpolicy backend-ingresskubectl get networkpolicy假设前端和后端位于同一个 Namespace,这条策略会产生以下效果:
流量与结果:
flowchart LR
N0["前端 Pod"]
N1["后端 TCP 8080"]
N2["其他普通 Pod"]
N3["后端 TCP 9090"]
N0 -->|"允许"| N1
N2 --x|"默认拒绝"| N1
N0 --x|"默认拒绝"| N3
后端 Pod 的出站流量:不受这条策略限制。
这里的“默认拒绝”有一个前提:后端 Pod 已经因为这条策略而受到入站隔离,而且没有其他策略允许相应流量。
重要:同一个 Pod 可以同时匹配多条 NetworkPolicy。对标准 NetworkPolicy 而言,适用策略的允许规则通常是累加的,而不是后创建的规则覆盖先创建的规则。
因此,如果另一条策略允许所有 Pod 访问后端 8080,那么上面这条策略并不能单独阻止其他 Pod 的访问。
四、跨 Namespace 如何限制访问?
实际集群往往有多个 Namespace,例如:
flowchart TB
N0["frontend-ns"]
N1["frontend Pod"]
N2["backend-ns"]
N3["backend Pod"]
N4["database-ns"]
N5["database Pod"]
N0 --> N1
N2 --> N3
N4 --> N5
如果你希望只允许 frontend-ns 中的前端访问 backend-ns 中的后端,就需要同时匹配来源 Namespace 和 Pod 标签。
例如,在 backend-ns 中创建:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: backend-cross-namespace namespace: backend-nsspec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: frontend-ns podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080这里有一个非常重要的 YAML 细节。
namespaceSelector 和 podSelector 写在同一个 from 条目中,表示两个条件必须同时满足:
- 来源 Pod 所在的 Namespace 是
frontend-ns。 - 来源 Pod 具有
app=frontend标签。
如果把它们拆成两个独立的 from 条目,语义就变成满足其中任意一个条件即可,可能会放宽访问范围。
五、出站策略:控制 Pod 能访问什么
前面控制的是谁可以访问后端,现在控制一个 Pod 可以主动访问哪些目标。
例如,只允许后端访问数据库的 TCP 5432 端口:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: backend-egress namespace: defaultspec: podSelector: matchLabels: app: backend policyTypes: - Egress egress: - to: - podSelector: matchLabels: app: database ports: - protocol: TCP port: 5432这条策略的含义:
- 选择
app=backend的 Pod。 - 将它们的出站流量纳入策略隔离。
- 允许访问同一 Namespace 中
app=database的 Pod 的 TCP 5432 端口。 - 其他出站流量默认不允许,除非有其他适用策略允许。
但是,这条策略还有一个实际问题:DNS 可能会被阻断。
如果后端应用使用 database.default.svc.cluster.local 访问数据库,就需要确保它能够访问集群 DNS 服务。
为什么 DNS 会失效?
启用出站隔离后,Pod 的 DNS 查询也属于出站流量。
如果没有允许访问 DNS 的规则,应用可能出现:
Could not resolve hostTemporary failure in name resolution这时即使数据库本身正常运行,应用仍可能因为无法解析 Service 名称而连接失败。
你可以根据实际 CoreDNS Service 的标签、Namespace 和端口,增加 DNS 出站规则。常见示例是:
egress: - to: - podSelector: matchLabels: k8s-app: kube-dns namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - protocol: UDP port: 53 - protocol: TCP port: 53 - to: - podSelector: matchLabels: app: database ports: - protocol: TCP port: 5432这是一个需要结合现有策略合并使用的 egress 规则示例,不是完整的 NetworkPolicy 资源。
还需要注意:
- CoreDNS 的实际标签可能不同,需要通过
kubectl get pods -n kube-system --show-labels检查。 - 部分集群的 DNS 请求可能经过 NodeLocal DNSCache,流量路径和策略匹配需要结合具体部署分析。
- UDP 53 和 TCP 53 都应该考虑,尤其是 DNS 响应较大或需要 TCP 回退的情况。
- 如果应用需要访问外部 HTTPS API,还要明确添加相应的出站允许规则。
生产环境建议先盘点应用依赖,再逐步启用出站隔离,避免直接使用全拒绝策略导致业务中断。
六、默认拒绝策略:构建真正的网络隔离
如果你想让某个 Namespace 中的所有 Pod 默认拒绝入站流量,可以创建:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-deny-ingress namespace: defaultspec: podSelector: {} policyTypes: - Ingress ingress: []podSelector: {} 表示选择该 Namespace 中的所有 Pod。
如果还要默认拒绝所有出站流量,可以使用:
policyTypes: - Ingress - Egressingress: []egress: []这是一种常见的“默认拒绝,再逐项放行”的安全设计。
但请特别注意:如果直接对整个 Namespace 应用默认拒绝入站和出站策略,DNS、外部 API、监控探针以及服务之间的通信都可能中断。应该先规划允许规则,再逐步实施。
七、NetworkPolicy 与 Service 有什么关系?
这是一个容易混淆的地方。
| 组件 | 解决的问题 |
|---|---|
| Service | 提供稳定的服务访问入口 |
| EndpointSlice | 维护服务关联的网络端点 |
| CNI | 提供 Pod 网络连接及相关网络能力 |
| NetworkPolicy | 声明允许和拒绝哪些 Pod 网络流量 |
| Ingress / Gateway | 管理外部 HTTP/HTTPS 流量路由 |
例如,后端有一个 ClusterIP Service:
backend.default.svc.cluster.local:8080前端通过 Service 访问后端时,仍然需要满足适用的网络策略。即使其他 Pod 绕过 Service,直接访问后端 Pod IP,也不应因此绕过正确实施的 NetworkPolicy。
标准 NetworkPolicy 主要描述 L3/L4 访问控制,并不直接提供基于 HTTP URL、请求头或用户身份的细粒度授权。如果需要这些能力,通常还需要网关、服务网格或应用层身份认证与授权机制。
八、本节总结
本节需要掌握的核心知识:
- NetworkPolicy 使用标签选择器定义流量访问规则。
Ingress控制进入目标 Pod 的流量,Egress控制目标 Pod 发出的流量。- 默认拒绝策略可以建立网络隔离基础,但需要显式规划必要的放行规则。
- 多条标准 NetworkPolicy 的允许规则通常是累加关系。
- 网络策略是否生效,取决于底层网络插件的支持和实际配置。
- DNS、监控、探针及外部依赖都需要纳入策略设计。