跳到主要内容

NetworkPolicy 应用实践

NetworkPolicy vs CiliumNetworkPolicy

Kubernetes 原生的 NetworkPolicy 和 Cilium 的 CiliumNetworkPolicy 都用于定义 Pod 间的网络访问控制策略,但 CiliumNetworkPolicy 在功能上更加强大和灵活。虽然 Cilium 也兼容 NetworkPolicy,但既然安装了 Cilium 就建议使用功能更强大的 CiliumNetworkPolicy。

核心功能对比

特性NetworkPolicyCiliumNetworkPolicy
作用域命名空间级别命名空间级别
基本流量控制✅ 支持 ingress/egress✅ 支持 ingress/egress
Pod 选择器✅ 基于 label 选择✅ 支持更复杂的表达式
L3/L4 规则✅ IP/端口控制✅ IP/端口控制
L7 协议感知❌ 不支持✅ 支持 HTTP/gRPC 等(Kafka 已于 1.20 移除)
FQDN 支持❌ 不支持✅ 支持域名匹配
显式拒绝规则❌ 只能隐式拒绝✅ 支持 egressDeny/ingressDeny
实体选择器❌ 不支持✅ 支持 toEntities/fromEntities
DNS 感知❌ 不支持✅ 支持 DNS 规则
服务选择器❌ 不支持✅ 支持 toServices

主要差异说明

1. L7 协议感知

  • NetworkPolicy 只能控制到 L3/L4 层(IP 地址和端口)。
  • CiliumNetworkPolicy 可以深入到 L7 层,控制 HTTP 方法、路径、header,gRPC 方法等。

2. FQDN 域名支持

  • NetworkPolicy 只能使用 IP 或 CIDR,无法直接控制域名访问。
  • CiliumNetworkPolicy 支持 toFQDNs,可以直接使用域名和通配符模式。

3. 显式拒绝规则

  • NetworkPolicy 采用白名单模式,未匹配的流量默认拒绝,但无法显式拒绝特定流量。
  • CiliumNetworkPolicy 支持 egressDeny/ingressDeny,可以在允许大部分流量的同时显式拒绝特定目标。

4. 实体选择器

  • NetworkPolicy 需要通过 CIDR 或选择器间接指定目标。
  • CiliumNetworkPolicy 提供 toEntities/fromEntities,可以直接选择 kube-apiserverhostremote-nodeworld 等预定义实体。

5. 选择器灵活性

  • NetworkPolicy 使用标准的 podSelectornamespaceSelector
  • CiliumNetworkPolicy 的 endpointSelector 支持更复杂的表达式。

兼容性

  • Cilium 完全兼容 Kubernetes 原生 NetworkPolicy。
  • 可以在同一集群中混用两种策略类型。

CiliumClusterwideNetworkPolicy

CiliumNetworkPolicy 和 CiliumClusterwideNetworkPolicy 的核心区别在于作用域和管理方式,它们的策略语法完全相同。

核心差异

特性CiliumNetworkPolicyCiliumClusterwideNetworkPolicy
作用域命名空间级别集群级别
资源类型命名空间资源集群资源(无命名空间)
管理权限命名空间管理员集群管理员
选择器默认范围同命名空间 Pod集群所有 Pod
跨命名空间选择需要显式指定天然支持
策略优先级普通优先级普通优先级(与 CCNP 可加性合并,无优先级差异)
节点防火墙❌ 不支持✅ 支持(通过 nodeSelector 选中节点)
使用场景应用级策略集群基线策略

主要差异说明

1. 作用域和资源位置

  • CiliumNetworkPolicy 必须创建在特定命名空间中,通过 metadata.namespace 指定。
  • CiliumClusterwideNetworkPolicy 是集群级资源,没有命名空间概念。

2. 选择器行为

  • CiliumNetworkPolicy 的 endpointSelector 默认只选择同命名空间的 Pod。
  • CiliumClusterwideNetworkPolicy 的 endpointSelector 可以选择集群中任意命名空间的 Pod。

3. 跨命名空间访问控制

  • CiliumNetworkPolicy 控制跨命名空间访问时,需要在 toEndpoints/fromEndpoints 中显式指定命名空间标签。
  • CiliumClusterwideNetworkPolicy 可以直接通过命名空间标签统一管理多个命名空间的策略。

4. 管理权限和职责分离

  • CiliumNetworkPolicy 可以由命名空间管理员(有该命名空间权限的用户)管理。
  • CiliumClusterwideNetworkPolicy 需要集群管理员权限,适合平台团队管理。

5. 策略合并和优先级

  • 当同一个 Pod 同时被两种策略选中时,规则会合并生效。
  • 拒绝规则(egressDeny/ingressDeny)优先于允许规则。
  • 通常使用 CiliumClusterwideNetworkPolicy 设置安全基线,用 CiliumNetworkPolicy 添加应用特定规则。

6. 配置节点防火墙

  • CiliumClusterwideNetworkPolicy 支持将网络策略应用到节点上,用于设置节点维度防火墙。
  • 这种策略只能由 CiliumClusterwideNetworkPolicy 来配置,CiliumNetworkPolicy 不支持。

典型使用场景

CiliumNetworkPolicy 适用于:

  • 微服务之间的访问控制。
  • 应用特定的网络隔离需求。
  • 开发团队自主管理的网络策略。
  • 命名空间内的细粒度控制。

CiliumClusterwideNetworkPolicy 适用于:

  • 集群默认拒绝策略(default deny)。
  • 统一管理多个基础设施命名空间的网络策略。
  • 全局安全基线和合规要求。
  • 跨命名空间的统一访问控制。
  • 限制对敏感资源(如 kube-apiserver)的访问。
  • 配置节点防火墙。

最佳实践

分层管理策略:

  1. 使用 CiliumClusterwideNetworkPolicy 设置集群安全基线(如默认拒绝、DNS 访问、基础设施互通)。
  2. 使用 CiliumNetworkPolicy 实现应用特定的网络策略(如服务间调用、外部 API 访问)。

权限分离:

  • 平台团队管理 CiliumClusterwideNetworkPolicy,确保集群整体安全。
  • 应用团队管理 CiliumNetworkPolicy,满足业务需求。

命名规范:

  • 集群策略使用描述性前缀,如 default-deny-allglobal-infrastructure
  • 命名空间策略使用应用相关名称,如 frontend-to-backendallow-external-api

模式兼容性

下面用法实践中的部分能力依赖 cilium 的 L7 DNS Proxy(具体包括 toFQDNstoPorts.rules.dns 这两类规则),cilium 通过 BPF + iptables TPROXY 把被策略选中的 Pod 发出的 DNS 查询重定向到 cilium-agent 内置的 DNS 代理。

本系列教程提供的 3 种推荐部署方案——Native Routing (VPC-CNI)Overlay (VPC-CNI)Overlay (GR)——都完整支持 toFQDNsrules.dns,下文所有示例可直接套用。

GR Native Routing 不可用

第 4 种组合 GR + Native Routing 因兼容性问题已不再作为推荐方案(除 L7/DNS 不支持外,跨节点 Pod-to-Pod 流量也不通),详见 为什么不提供 GR Native Routing 部署方案?。GR 集群希望使用 cilium,请改用 Overlay 模式

用法实践

安全基线:默认拒绝

集群默认拒绝 egress 流量(dns 解析除外,kube-system 命名空间中的 pod 除外),严格控制集群 Pod 的网络访问权限:

备注

通常 ingress 流量不设置全局的默认拒绝,可针对敏感业务单独设置 ingress 策略(如 A 只允许被 B 访问)。

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: default-deny
spec:
description: "Block all the traffic (except DNS) by default"
egress:
- toEndpoints: # Allow all Pods to resolve DNS via coredns
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
endpointSelector:
matchExpressions: # Do not restrict egress traffic for Pods in kube-system
- key: io.kubernetes.pod.namespace
operator: NotIn
values:
- kube-system

统一管控基础设施的网络策略

集群中可能会部署许多基础设施相关应用,分散在多个命名空间,我们可以用 CiliumClusterwideNetworkPolicy 和命名空间标签来统一设置这些命名空间的网络策略(假设这些命名空间都打上了 role=infrastructure 这个 label):

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: default-infrastructure
spec:
endpointSelector: # Select all Pods in infrastructure namespaces
matchLabels:
io.cilium.k8s.namespace.labels.role: infrastructure
egress: # Configure egress policy
- toEndpoints: # Allow access to all Pods in infrastructure namespaces
- matchLabels:
io.cilium.k8s.namespace.labels.role: infrastructure
- toEndpoints: # Allow DNS resolution via coredns
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
- toFQDNs: # Allow calling Tencent Cloud related APIs
- matchPattern: '**.tencent.com'
- matchPattern: '**.tencentcloudapi.com'
- matchPattern: '**.tencentyun.com'
- toCIDR: # Allow access to platform services on Tencent Cloud
- 169.254.0.0/16 # 169.254.0.0/16 is a reserved CIDR on Tencent Cloud used by some platform services, such as the VIP of the TKE cluster apiserver, COS storage, image registry, etc. Some TKE built-in components also call interfaces provided by this CIDR (e.g., ipamd) and use hostAlias, bypassing DNS resolution. Using toFQDNs to allow egress traffic will not work (toFQDNs relies on requests going through DNS resolution).
- toEntities: # Allow access to apiserver
- kube-apiserver
- toEntities: # Allow access to port 10250 on all nodes for metric collection
- host
- remote-node
toPorts:
- ports:
- port: "10250"
protocol: TCP

配置节点防火墙

apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
name: default-host-firewall
spec:
nodeSelector: {} # 选中所有节点
ingress:
- fromEntities:
- cluster # 不限制集群内的流量
- toPorts:
- ports: # 允许 ssh 访问
- port: "22"
protocol: TCP
- icmps: # 允许 ping 请求
- fields:
- type: EchoRequest
family: IPv4
ingress 三条规则之间是 OR 关系

同一策略内多条 ingress 规则是并集(OR)生效:上面的 cluster 实体、SSH 22/TCP、ICMP Echo 三条规则中,后两条对任意来源(含集群外)放行——SSH 和 ping 实际是全网可达的。如果希望 SSH 仅限集群内访问,需把端口限制合并进 fromEntities: cluster 那条规则(用 fromEntities + toPorts 组合),而不是并列多条规则。

多租户隔离

很多时候集群是多租户共享,如果每个租户使用一个命名空间,平台可以使用 CiliumNetworkPolicy 限制租户的业务 Pod 只能在同名空间下互访:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-same-namespace
namespace: tenant-001
spec:
endpointSelector: {}
ingress:
- fromEndpoints:
- {}
egress:
- toEndpoints:
- {}

如果是每个租户的业务 Pod 分布有多个命名空间,但有相同的命名空间标签来标识租户,这可以使用 CiliumClusterwideNetworkPolicy 来实现:

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: tenant-001
spec:
endpointSelector: # Select Pods in all namespaces of tenant 001
matchLabels:
io.cilium.k8s.namespace.labels.tenant-id: "001"
egress: # Only allow tenant 001 Pods to access their own business Pods
- toEndpoints:
- matchLabels:
io.cilium.k8s.namespace.labels.tenant-id: "001"
ingress: # Only allow tenant 001 Pods to be accessed by their own business Pods
- fromEndpoints:
- matchLabels:
io.cilium.k8s.namespace.labels.tenant-id: "001"

限制 apiserver 的访问

如需严格限制 apiserver 的访问,避免集群被攻击或减少不必要的控制面压力,可先配置一个全局的默认拒绝规则(参考前面的 安全基线:默认拒绝 示例),然后再按需配置允许哪些 Pod 访问。

允许 test 命名空间下所有 pod 访问 apiserver:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: default-allow-apiserver
namespace: test
spec:
endpointSelector: {}
egress:
- toEntities:
- kube-apiserver

允许 test 命名空间下的 A 服务访问 apiserver:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: from-a-to-apiserver
namespace: test
spec:
endpointSelector:
matchLabels:
app: a
egress:
- toEntities:
- kube-apiserver

限制业务的入流量:保护敏感服务

限制 A 只能被 B 访问,且只能访问 80/TCP 端口

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: from-b-to-a
spec:
endpointSelector:
matchLabels:
app: a
ingress:
- fromEndpoints:
- matchLabels:
app: b
toPorts:
- ports:
- port: "80"
protocol: TCP

限制 A 只能被 B 访问,且只能访问部分接口

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: from-b-to-a-api
spec:
description: "Allow HTTP API from b to a"
endpointSelector:
matchLabels:
app: a
ingress:
- fromEndpoints:
- matchLabels:
app: b
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET" # Allow GET /public
path: "/public"
- method: "PUT" # Allow PUT /avatar, requires X-My-Header: true header
path: "/avatar$"
headers:
- 'X-My-Header: true'

限制 A 只能被集群外部访问

如果 A 对外提供服务, CLB 直连 Pod(参考 使用 LoadBalancer 直连 Pod 模式 Service),处理来自公网的请求,不允许其它流量(如来自集群内 Pod 或节点),可配置如下策略:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: from-outside-to-a
spec:
endpointSelector:
matchLabels:
app: a
ingress:
- fromEntities:
- world

如果 CLB 不是直连的 Pod,而是经过了 NodePort 转发,跨节点会做 SNAT,这时可以写成这样:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: from-outside-to-a
spec:
endpointSelector:
matchLabels:
app: a
ingress:
- fromEntities:
- world
- remote-node # 允许来自 NodePort 跨节点转发的流量

限制业务的出流量

A 只能访问 B

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: egress-a-to-b
spec:
endpointSelector:
matchLabels:
app: a
egress:
- toEndpoints:
- matchLabels:
app: b

A 只能访问同名空间下的 Pod

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-all-from-a
spec:
endpointSelector:
matchLabels:
app: a
egress:
- toEndpoints:
- {}

A 只能访问指定网段下的服务

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-a-to-cidr
spec:
endpointSelector:
matchLabels:
app: a
egress:
- toCIDR:
- 192.0.2.0/24
toPorts:
- ports:
- port: "80"
protocol: TCP

A 只能访问指定端口范围的服务

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-a-ports
spec:
endpointSelector:
matchLabels:
app: a
egress:
- toPorts:
- ports: # 只能发送目标端口在 80-444 的 TCP 流量
- port: "80"
endPort: 444
protocol: TCP

A 只能访问指定域名的服务

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: from-a-to-domains
spec:
endpointSelector:
matchLabels:
app: a
egress:
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
- toFQDNs:
- matchName: 'imroc.cc'
- matchPattern: '**.imroc.cc'
- matchPattern: '**.myqcloud.com'
- matchPattern: '**.tencent.com'
- matchPattern: '**.tencentcloudapi.com'
- matchPattern: '**.tencentyun.com'

显式禁止:A 不能访问 B

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: deny-a-to-b
spec:
endpointSelector:
matchLabels:
app: a
egressDeny:
- toEndpoints: # 显式禁止 A 访问 B
- matchLabels:
app: b
egress:
- toEntities: # 允许 A 的其它流量
- all

参考资料