网络策略

如果你想控制 IP 地址或端口级别(OSI 第三层或第四层)的流量,NetworkPolicies 允许你指定集群内部以及 Pod 和外部世界之间的流量规则。你的集群必须使用支持 NetworkPolicy 强制实施的网络插件。

如果你想控制 TCP、UDP 和 SCTP 协议在 IP 地址或端口级别的流量,那么你可能需要考虑在集群中对特定应用使用 Kubernetes NetworkPolicy。NetworkPolicy 是一种以应用为中心的构造,允许你指定一个 Pod 如何通过网络与各种网络“实体”(这里使用“实体”一词是为了避免与更常见的术语如“端点”和“服务”重叠,这些术语在 Kubernetes 中有特定的含义)通信。NetworkPolicy 适用于连接到一端或两端都是 Pod 的连接,与其它连接无关。

Pod 可以与之通信的实体通过以下三种标识符的组合进行识别:

  1. 允许的其他 Pod (例外:一个 Pod 不能阻止访问它自己)
  2. 允许的 Namespace
  3. IP 地址块(例外:无论 Pod 或节点的 IP 地址如何,进出 Pod 运行节点的流量总是被允许的)

在定义基于 Pod 或 Namespace 的 NetworkPolicy 时,你可以使用 选择器 来指定匹配该选择器的 Pods 所允许的进出流量。

同时,创建基于 IP 的 NetworkPolicy 时,我们基于 IP 地址块 (CIDR 范围) 定义策略。

先决条件

网络策略由网络插件来实现。要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。创建一个没有实现 NetworkPolicy 的控制器,将没有任何效果。

Pod 隔离的两种类型

Pod 的隔离有两种类型:出站(egress)隔离和入站(ingress)隔离。它们涉及哪些连接可以被建立。这里的“隔离”不是绝对的,而是意味着“一些限制适用”。另一种情况,“未隔离(non-isolated)”对于某个方向意味着在该指定方向上没有任何限制。这两种隔离类型(或不隔离)是独立声明的,并且都与从一个 Pod 到另一个 Pod 的连接有关。

默认情况下,Pod 在出站方向上未隔离;所有出站连接都允许。如果任何 NetworkPolicy 同时选择了 Pod 并在其 policyTypes 中包含 “Egress”,则该 Pod 在出站方向上是隔离的;我们称此类策略对该 Pod 的出站方向适用。当一个 Pod 在出站方向上被隔离时,允许从该 Pod 发起的唯一连接是那些被适用于该 Pod 出站方向的某个 NetworkPolicy 的 egress 列表所允许的。这些被允许连接的回复流量也会被隐式允许。这些 egress 列表的效果是累加的。

默认情况下,Pod 在入站方向上未隔离;所有入站连接都允许。如果任何 NetworkPolicy 同时选择了 Pod 并在其 policyTypes 中包含 “Ingress”,则该 Pod 在入站方向上是隔离的;我们称此类策略对该 Pod 的入站方向适用。当一个 Pod 在入站方向上被隔离时,允许进入该 Pod 的唯一连接是来自 Pod 所在节点的连接以及那些被适用于该 Pod 入站方向的某个 NetworkPolicy 的 ingress 列表所允许的连接。这些被允许连接的回复流量也会被隐式允许。这些 ingress 列表的效果是累加的。

网络策略不会冲突;它们是叠加(additive)的。如果任何策略或策略集适用于给定 Pod 的给定方向,则该方向上从该 Pod 允许的连接是适用策略允许的所有连接的并集。因此,评估顺序不影响策略结果。

要允许从源 Pod 到目标 Pod 的连接,源 Pod 上的出站策略和目标 Pod 上的入站策略都需要允许该连接。如果任何一方不允许该连接,它将不会发生。

NetworkPolicy 资源

有关资源的完整定义,请参阅 NetworkPolicy 参考。

一个 NetworkPolicy 示例可能如下所示:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

必需字段:与其他所有 Kubernetes 配置一样,NetworkPolicy 需要 apiVersionkindmetadata 字段。有关使用配置文件的一般信息,请参见配置 Pod 以使用 ConfigMap对象管理

spec: NetworkPolicy 的 spec 包含定义给定 Namespace 中特定网络策略所需的所有信息。

podSelector: 每个 NetworkPolicy 都包含一个 podSelector,用于选择策略应用的 Pod 组。示例策略选择了带有标签 “role=db” 的 Pod。空的 podSelector 选择 Namespace 中的所有 Pod。

policyTypes: 每个 NetworkPolicy 都包含一个 policyTypes 列表,其中可以包含 IngressEgress 或两者。policyTypes 字段指示给定策略是否适用于所选 Pod 的入站流量、所选 Pod 的出站流量或两者。如果 NetworkPolicy 上未指定 policyTypes,则默认情况下始终设置 Ingress,并且如果 NetworkPolicy 有任何出站规则,则会设置 Egress

ingress: 每个 NetworkPolicy 可以包含一个允许的 ingress 规则列表。每个规则允许匹配 fromports 部分的流量。示例策略包含一个规则,匹配单个端口上的流量,来自三个来源之一:第一个通过 ipBlock 指定,第二个通过 namespaceSelector 指定,第三个通过 podSelector 指定。

egress: 每个 NetworkPolicy 可以包含一个允许的 egress 规则列表。每个规则允许匹配 toports 部分的流量。示例策略包含一个规则,匹配到 10.0.0.0/24 CIDR 范围内任意目标的单个端口上的流量。

因此,示例 NetworkPolicy:

  1. 隔离 default Namespace 中带有 role=db 标签的 Pod 的入站和出站流量(如果它们尚未隔离)

  2. (入站规则) 允许从以下来源连接到 default Namespace 中所有带有 role=db 标签的 Pod 的 TCP 端口 6379:

    • default Namespace 中带有 role=frontend 标签的任何 Pod
    • 带有 project=myproject 标签的 Namespace 中的任何 Pod
    • IP 地址范围在 172.17.0.0172.17.0.255172.17.2.0172.17.255.255 (即,除了 172.17.1.0/24 之外的所有 172.17.0.0/16
  3. (出站规则) 允许从 default Namespace 中任何带有 role=db 标签的 Pod 连接到 CIDR 10.0.0.0/24 的 TCP 端口 5978。

更多示例请参阅声明网络策略演练。

tofrom 选择器的行为

ingressfrom 部分或 egressto 部分中可以指定四种选择器:

podSelector: 这选择与 NetworkPolicy 位于同一 Namespace 中的特定 Pod,这些 Pod 应被允许作为入站源或出站目标。

namespaceSelector: 这选择特定的 Namespace,这些 Namespace 中的所有 Pod 都应被允许作为入站源或出站目标。

namespaceSelector podSelector: 一个同时指定了 namespaceSelectorpodSelectorto/from 条目,选择特定 Namespace 中的特定 Pod。请注意使用正确的 YAML 语法。例如:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

此策略包含一个 from 元素,允许从带有标签 role=client 且其 Namespace 带有标签 user=alice 的 Pods 发起的连接。但下面的策略不同:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

它在 from 数组中包含两个元素,允许从本地 Namespace 中带有标签 role=client 的 Pod 发起的连接,_或者_允许从任意 Namespace 中带有标签 user=alice 的任意 Pod 发起的连接。

如有疑问,请使用 kubectl describe 查看 Kubernetes 如何解释策略。

ipBlock: 这选择特定的 IP CIDR 范围,允许它们作为入站源或出站目标。这些应该是集群外部的 IP,因为 Pod IP 是短暂且不可预测的。

集群的入站和出站机制通常需要重写数据包的源或目标 IP。在这种情况下,无法确定NetworkPolicy处理是在此之前还是之后发生,并且其行为可能因网络插件、云提供商、Service实现等不同组合而异。

对于入站流量,这意味着在某些情况下,你可以根据实际原始源 IP 过滤入站数据包,而在其他情况下,NetworkPolicy 所作用的“源 IP”可能是 LoadBalancer 或 Pod 所在节点的 IP 等。

对于出站,这意味着从 Pod 到被重写为集群外部 IP 的 Service IP 的连接可能会也可能不会受到基于 ipBlock 的策略的约束。

默认策略

默认情况下,如果一个 Namespace 中不存在任何策略,则允许该 Namespace 中所有 Pod 的所有入站和出站流量。以下示例允许你更改该 Namespace 中的默认行为。

默认拒绝所有入站流量

你可以通过创建一个选择所有 Pod 但不允许任何入站流量到这些 Pod 的 NetworkPolicy 来为 Namespace 创建一个“默认”的入站隔离策略。

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

这确保即使没有被任何其他 NetworkPolicy 选中的 Pod 也仍然会被隔离以进行入站流量控制。此策略不影响任何 Pod 的出站隔离行为。

允许所有入站流量

如果你想允许所有入站连接到 Namespace 中的所有 Pod,你可以创建一个明确允许此行为的策略。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

启用此策略后,任何附加策略都无法导致对这些 Pod 的任何入站连接被拒绝。此策略对任何 Pod 的出站隔离没有影响。

默认拒绝所有出站流量

你可以通过创建一个选择所有 Pod 但不允许从这些 Pod 发出任何出站流量的 NetworkPolicy 来为 Namespace 创建一个“默认”的出站隔离策略。

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

这确保即使未被任何其他 NetworkPolicy 选中的 Pod 也不会被允许出站流量。此策略不改变任何 Pod 的入站隔离行为。

允许所有出站流量

如果你想允许 Namespace 中所有 Pod 的所有连接,你可以创建一个明确允许该 Namespace 中 Pod 的所有出站连接的策略。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

启用此策略后,任何附加策略都无法导致从这些 Pod 发出的任何出站连接被拒绝。此策略对任何 Pod 的入站隔离没有影响。

默认拒绝所有入站和出站流量

你可以在某个 Namespace 中创建以下 NetworkPolicy,以创建一个“默认”策略,阻止该 Namespace 中所有入站和出站流量。

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

这确保即使未被任何其他 NetworkPolicy 选中的 Pod 也不会被允许入站或出站流量。

网络流量过滤

NetworkPolicy 是为第 4 层连接(TCP、UDP 和可选的 SCTP)定义的。对于所有其他协议,行为可能因网络插件而异。

当定义了一个 deny all 的网络策略时,只保证拒绝 TCP、UDP 和 SCTP 连接。对于其他协议,如 ARP 或 ICMP,其行为是未定义的。允许规则也是如此:当某个特定 Pod 被允许作为入站源或出站目标时,对(例如)ICMP 数据包会发生什么情况是未定义的。一些网络插件可能允许 ICMP 等协议,而另一些则可能拒绝它们。

针对一个端口范围

FEATURE STATE: Kubernetes v1.25 [stable]

编写 NetworkPolicy 时,你可以针对一个端口范围,而不是单个端口。

通过使用 endPort 字段可以实现此功能,如下例所示:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 32000
          endPort: 32768

上面的规则允许 default Namespace 中带有标签 role=db 的任何 Pod 与范围为 10.0.0.0/24 内的任何 IP 通过 TCP 进行通信,前提是目标端口在 32000 到 32768 的范围内。

使用此字段时适用以下限制:

  • endPort 字段必须等于或大于 port 字段。
  • 只有定义了 port 字段才能定义 endPort 字段。
  • 两个端口都必须是数字。

按标签定位多个 Namespace

在这种情况下,你的 Egress NetworkPolicy 使用 Namespace 的标签名称来定位多个 Namespace。为了实现这一点,你需要为目标 Namespace 添加标签。例如:

kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend

将标签添加到 NetworkPolicy 文档的 namespaceSelector 下。例如:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-namespaces
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchExpressions:
        - key: namespace
          operator: In
          values: ["frontend", "backend"]

按名称定位 Namespace

Kubernetes 控制平面在所有 Namespace 上设置一个不可变的标签 kubernetes.io/metadata.name,标签的值就是 Namespace 的名称。

尽管 NetworkPolicy 不能通过某些对象字段按名称定位 Namespace,但你可以使用标准化标签来定位特定的 Namespace。

Pod 生命周期

当创建一个新的 NetworkPolicy 对象时,网络插件处理新对象可能需要一些时间。如果一个受 NetworkPolicy 影响的 Pod 在网络插件完成 NetworkPolicy 处理之前创建,该 Pod 可能会在未受保护的情况下启动,并且隔离规则将在 NetworkPolicy 处理完成后应用。

一旦网络插件处理了 NetworkPolicy,

  1. 所有受给定 NetworkPolicy 影响的新创建的 Pod 在启动前都会被隔离。NetworkPolicy 的实现必须确保过滤在整个 Pod 生命周期中都是有效的,即使是从 Pod 中任何容器启动的最早时刻开始。由于它们应用于 Pod 级别,NetworkPolicy 对 Init 容器、Sidecar 容器和普通容器同样适用。

  2. 允许规则最终会在隔离规则之后(或可能同时)应用。在最坏的情况下,如果隔离规则已经应用但允许规则尚未应用,新创建的 Pod 在启动时可能完全没有网络连接。

每个创建的 NetworkPolicy 最终都会由网络插件处理,但无法从 Kubernetes API 中确定具体何时发生。

因此,Pods 在启动时必须能够承受与预期不同的网络连接状况。如果你需要确保 Pod 在启动前能够到达某些目的地,可以使用 init container 来等待这些目的地可达,然后再由 Kubelet 启动应用容器。

每个 NetworkPolicy 最终都将应用于所有选定的 Pod。由于网络插件可能以分布式方式实现 NetworkPolicy,因此当 Pod 首次创建时,或者当 Pod 或策略发生变化时,Pod 可能会看到略微不一致的网络策略视图。例如,一个新创建的 Pod 本应能够同时访问 Node 1 上的 Pod A 和 Node 2 上的 Pod B,但它可能会发现可以立即访问 Pod A,但直到几秒钟后才能访问 Pod B。

NetworkPolicy 和 hostNetwork Pods

针对 hostNetwork Pod 的 NetworkPolicy 行为未定义,但应仅限于以下 2 种可能性:

  • 网络插件可以区分 hostNetwork Pod 的流量与所有其他流量(包括能够区分同一节点上不同 hostNetwork Pod 的流量),并将 NetworkPolicy 应用于 hostNetwork Pod,就像它对 pod-network Pod 所做的那样。
  • 网络插件无法正确区分 hostNetwork Pod 的流量,因此在匹配 podSelectornamespaceSelector 时会忽略 hostNetwork Pod。进出 hostNetwork Pod 的流量被视为与进出节点 IP 的所有其他流量相同。(这是最常见的实现。)

这适用于以下情况:

  1. 一个 hostNetwork Pod 被 spec.podSelector 选中。

      ...
      spec:
        podSelector:
          matchLabels:
            role: client
      ...
    
  2. 一个 hostNetwork Pod 在 ingressegress 规则中被 podSelectornamespaceSelector 选中。

      ...
      ingress:
        - from:
          - podSelector:
              matchLabels:
                role: client
      ...
    

同时,由于 hostNetwork Pod 具有与其所在节点相同的 IP 地址,它们的连接将被视为节点连接。例如,你可以使用 ipBlock 规则允许来自 hostNetwork Pod 的流量。

通过网络策略无法完成的事情(至少目前如此)

截至 Kubernetes 1.33 版本,NetworkPolicy API 中不存在以下功能,但你可能可以使用操作系统组件(如 SELinux、OpenVSwitch、IPTables 等)或第 7 层技术(Ingress 控制器、服务网格实现)或准入控制器来实现变通方法。如果你是 Kubernetes 网络安全新手,值得注意的是,以下用户故事目前无法使用 NetworkPolicy API 实现。

  • 强制内部集群流量通过公共网关(这可能最适合通过服务网格或其他代理实现)。
  • 任何与 TLS 相关的功能(为此请使用服务网格或 Ingress 控制器)。
  • 特定节点的策略(你可以使用 CIDR 表示法来指定这些,但不能专门根据其 Kubernetes 身份来定位节点)。
  • 按名称定位服务(但是,你可以按 标签 定位 Pod 或 Namespace,这通常是一个可行的变通方法)。
  • 创建或管理由第三方实现的“策略请求”。
  • 应用于所有 Namespace 或 Pod 的默认策略(有一些第三方 Kubernetes 发行版和项目可以做到这一点)。
  • 高级策略查询和可达性工具。
  • 记录网络安全事件的能力(例如,被阻止或接受的连接)。
  • 明确拒绝策略的能力(当前 NetworkPolicy 的模型是默认拒绝,只能添加允许规则)。
  • 阻止回环或传入主机流量的能力(Pods 目前无法阻止 localhost 访问,也无法阻止其驻留节点对自身的访问)。

NetworkPolicy 对现有连接的影响

当适用于现有连接的 NetworkPolicy 集合发生变化时——这可能由于 NetworkPolicy 发生变化,或者策略选择的 Namespace/Pod(主体和对等方)的相关标签在现有连接过程中发生变化——变化是否会对现有连接生效取决于具体实现。示例:创建了一个导致拒绝先前允许的连接的策略,底层网络插件实现负责定义新策略是否会关闭现有连接。建议不要以可能影响现有连接的方式修改策略/Pod/Namespace。

下一步

上次修改时间:2024 年 4 月 1 日下午 3:05 PST:指向 NetworkPolicy API 参考的指引 (f1c4eb8457)