NetworkPolicy 详解:Kubernetes 的网络访问控制机制

This article is extracted from the chat log with AI. Please identify it with caution.

说明:本文由 Codex 根据作者提供的主题、素材与公开资料辅助生成,属于 AI 生成/整理内容,非作者原创。请读者自行甄别并交叉验证。

NetworkPolicy 是什么#

NetworkPolicy 是 Kubernetes 里用于控制 Pod 网络访问的资源对象。

它主要工作在三层和四层:

IP 地址
端口
协议:TCP / UDP / SCTP

它回答的问题是:

哪些来源可以访问这些 Pod?
这些 Pod 可以访问哪些目标?
允许访问哪些端口?

一个常见理解是:

Service 负责“怎么找到后端”;
NetworkPolicy 负责“找到以后能不能访问”。

需要特别注意:NetworkPolicy 本身只是 Kubernetes API 对象。真正执行规则的是网络插件,例如 Calico、Cilium、Antrea、kube-router 等。集群如果没有安装支持 NetworkPolicy 的 CNI,创建策略对象也不会产生实际隔离效果。

默认行为#

Kubernetes 默认不是零信任网络。

如果一个 Pod 没有被任何 NetworkPolicy 选中,那么它默认是:

Ingress:允许所有入站连接
Egress:允许所有出站连接

也就是说,默认情况下 Pod 之间通常可以互相访问。

当一个 Pod 被某条策略选中,并且该策略声明了 Ingress,这个 Pod 才会进入入站隔离状态。

当一个 Pod 被某条策略选中,并且该策略声明了 Egress,这个 Pod 才会进入出站隔离状态。

可以把它理解成:

没有策略命中:默认放行
有策略命中:只放行策略允许的流量

两个方向:Ingress 和 Egress#

Ingress 是进入 Pod 的流量。

Client Pod -> Server Pod
              ^^^^^^^^^^
              Ingress

Egress 是从 Pod 发出去的流量。

Client Pod -> Server Pod
^^^^^^^^^^
Egress

一次 Pod 到 Pod 的访问要成功,需要同时满足两个方向:

源 Pod 的 egress 策略允许出去;
目标 Pod 的 ingress 策略允许进来。

如果任意一边不允许,连接就不会成功。

策略是叠加的,不是覆盖的#

多条 NetworkPolicy 同时选中一个 Pod 时,规则是叠加关系。

它们不会互相覆盖,也没有先后顺序。

可以理解成:

最终允许集合 = 所有命中策略允许流量的并集

所以 Kubernetes 原生 NetworkPolicy 没有显式 deny 规则。

如果想拒绝某类流量,通常做法是:

先 default deny;
再逐步 allow 需要的来源、目标和端口。

基本结构#

一个 NetworkPolicy 的基本结构如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

含义是:

选中 default namespace 下 app=api 的 Pod;
限制这些 Pod 的入站流量;
只允许 app=frontend 的 Pod 访问 TCP 8080。

几个关键字段:

字段含义
metadata.namespace策略所在 namespace
spec.podSelector这条策略选中哪些 Pod
policyTypes策略作用方向:IngressEgress
ingress[].from允许哪些来源进入
egress[].to允许访问哪些目标
ports允许哪些协议和端口

podSelector: {} 表示选中当前 namespace 下所有 Pod。

default deny#

安全基线里最常见的是 default deny。

拒绝所有入站#

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: app
spec:
  podSelector: {}
  policyTypes:
    - Ingress

这表示:app namespace 里的所有 Pod 默认不允许任何入站连接,除非其他策略显式放行。

拒绝所有出站#

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: app
spec:
  podSelector: {}
  policyTypes:
    - Egress

这表示:app namespace 里的所有 Pod 默认不允许出站连接。

如果启用了 egress default deny,通常要记得放行 DNS,否则应用可能连服务名都解析不了。

放行 DNS#

常见 DNS 放行示例:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: app
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

这里有一个细节:

- namespaceSelector: ...
  podSelector: ...

写在同一个列表项里,表示同时满足 namespace 和 pod 条件。

如果写成两个列表项:

- namespaceSelector: ...
- podSelector: ...

含义就变成二选一:满足 namespace 条件的所有 Pod,或者当前 namespace 里满足 pod 条件的 Pod。

这是很多 NetworkPolicy 写错的地方。

namespaceSelector#

NetworkPolicy 不能直接通过 namespace 名称字段选择 namespace,但 Kubernetes 会给 namespace 自动加上标准标签:

kubernetes.io/metadata.name=<namespace-name>

所以可以这样允许某个 namespace 访问:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-gateway
  namespace: app
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: gateway
      ports:
        - protocol: TCP
          port: 8080

这表示:允许 gateway namespace 中的 Pod 访问 app namespace 下 app=api 的 Pod 的 8080 端口。

ipBlock#

ipBlock 用于匹配 CIDR。

例如允许访问外部网段:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-external-api
  namespace: app
spec:
  podSelector:
    matchLabels:
      app: worker
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24
            except:
              - 203.0.113.10/32
      ports:
        - protocol: TCP
          port: 443

这表示:app=worker 的 Pod 可以访问 203.0.113.0/24 的 443 端口,但排除 203.0.113.10

需要注意的是,ipBlock 适合外部地址或明确的 CIDR,不适合直接拿来描述动态变化的 Pod 集合。Pod 之间访问优先使用 podSelectornamespaceSelector

常见使用场景#

多租户 namespace 隔离#

每个团队一个 namespace,默认拒绝跨 namespace 访问,只开放公共入口。

team-a namespace
team-b namespace
shared-gateway namespace

策略思路:

  • 每个业务 namespace 默认 deny ingress。
  • 只允许网关、监控、必要的共享服务访问。
  • 业务间调用显式开白名单。

数据库只允许应用访问#

数据库 Pod 不应该被整个集群随便访问。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-postgres
  namespace: app
spec:
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 5432

控制出站访问#

一些环境要求应用只能访问固定外部服务。

可以用 egress 策略把出站限制在:

  • DNS。
  • 内部 Service。
  • 指定外部 CIDR 和端口。

这类场景要小心依赖方是否使用动态 IP。如果目标是域名,Kubernetes 原生 NetworkPolicy 不能直接按 FQDN 匹配,通常要依赖 CNI 扩展能力或通过固定出口代理收敛。

排查命令#

查看策略:

kubectl get networkpolicy -A
kubectl describe networkpolicy -n app
kubectl get networkpolicy -n app -o yaml

查看 Pod 标签:

kubectl get pod -n app --show-labels
kubectl get ns --show-labels

临时起一个测试 Pod:

kubectl run -n app -it --rm netshoot --image=nicolaka/netshoot -- bash

测试访问:

curl -v http://api.app.svc.cluster.local:8080
nc -vz postgres.app.svc.cluster.local 5432
nslookup kubernetes.default.svc.cluster.local

排查顺序:

1. CNI 是否支持 NetworkPolicy
2. 策略是否在正确 namespace
3. podSelector 是否真的选中了目标 Pod
4. policyTypes 是否包含方向
5. ingress/egress 是否同时满足
6. namespaceSelector 和 podSelector 是否写在了正确层级
7. DNS egress 是否被误拦截
8. 是否存在 CNI 自己的扩展策略影响结果

常见误区#

创建策略就一定生效#

不一定。必须有支持 NetworkPolicy 的 CNI 插件执行规则。

NetworkPolicy 是显式 deny 规则#

不是。Kubernetes 原生 NetworkPolicy 是 allow-list 模型,多条策略之间是加法并集。

只写 ingress 就能限制 Pod 访问外部#

不能。限制出站要写 Egress

Service 被允许就等于后端 Pod 被允许#

NetworkPolicy 最终还是作用在 Pod 连接上。访问 Service 时,流量最终会到后端 Pod,仍要看目标 Pod 的 ingress 策略和源 Pod 的 egress 策略。

可以按域名写策略#

Kubernetes 原生 NetworkPolicy 不支持按 FQDN 写规则。按域名控制通常依赖 CNI 扩展或出口代理。

小结#

NetworkPolicy 的关键点可以压缩成几句话:

默认全通;
被策略选中后,进入指定方向的隔离状态;
策略只做 allow,不做显式 deny;
多条策略叠加取并集;
Pod 到 Pod 成功访问需要源 egress 和目标 ingress 都允许;
实际执行依赖支持 NetworkPolicy 的 CNI。

理解这几个规则,再看 YAML 就不会被 podSelectornamespaceSelectorpolicyTypes 绕进去。

参考资料#

本文共 2511 字,创建于 Jun 25, 2026

相关标签: Kubernetes, DevOps, ByAI