网络策略

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

如果你想在 IP 地址或端口级别控制 TCP、UDP 和 SCTP 协议的流量,那么你可以考虑对集群中的特定应用程序使用 Kubernetes 网络策略。网络策略是一个以应用程序为中心的结构,它允许你指定 Pod 如何通过网络与各种网络“实体”(我们在这里使用“实体”一词是为了避免过度使用更常见的术语,如“端点”和“服务”,它们具有特定的 Kubernetes 内涵)进行通信。网络策略适用于一端或两端都有 Pod 的连接,与其他连接无关。

Pod 可以与之通信的实体通过以下三个标识符的组合来标识

  1. 允许的其他 Pod(例外:Pod 不能阻止对其自身的访问)
  2. 允许的命名空间
  3. IP 块(例外:无论 Pod 或节点的 IP 地址如何,始终允许进出 Pod 所在节点的流量)

在定义基于 Pod 或命名空间的网络策略时,可以使用 选择器 来指定允许进出匹配选择器的 Pod 的流量。

同时,在创建基于 IP 的网络策略时,我们根据 IP 块(CIDR 范围)定义策略。

先决条件

网络策略由 网络插件 实现。要使用网络策略,你必须使用支持网络策略的网络解决方案。在没有实现它的控制器的网络策略资源的情况下创建网络策略资源将不起作用。

两种 Pod 隔离

Pod 有两种隔离方式:出口隔离和入口隔离。它们涉及可以建立哪些连接。这里的“隔离”不是绝对的,而是指“适用某些限制”。另一种选择是“$direction 非隔离”,这意味着在指定方向上不适用任何限制。两种隔离(或不隔离)是独立声明的,并且都与从一个 Pod 到另一个 Pod 的连接相关。

默认情况下,Pod 的出口是非隔离的;允许所有出站连接。如果存在同时选择 Pod 并且其 policyTypes 中包含“Egress”的任何网络策略,则 Pod 的出口将被隔离;我们说这样的策略适用于 Pod 的出口。当 Pod 的出口被隔离时,唯一允许从 Pod 建立的连接是某些适用于 Pod 出口的网络策略的 egress 列表所允许的连接。对这些允许连接的回复流量也将被隐式允许。这些 egress 列表的效果是累加的。

默认情况下,Pod 的入口是非隔离的;允许所有入站连接。如果存在同时选择 Pod 并且其 policyTypes 中包含“Ingress”的任何网络策略,则 Pod 的入口将被隔离;我们说这样的策略适用于 Pod 的入口。当 Pod 的入口被隔离时,唯一允许进入 Pod 的连接是来自 Pod 节点的连接以及某些适用于 Pod 入口的网络策略的 ingress 列表所允许的连接。对这些允许连接的回复流量也将被隐式允许。这些 ingress 列表的效果是累加的。

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

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

网络策略资源

有关资源的完整定义,请参阅 网络策略 参考。

网络策略示例可能如下所示

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 配置一样,网络策略需要 apiVersionkindmetadata 字段。有关使用配置文件的常规信息,请参阅 配置 Pod 以使用 ConfigMap对象管理

spec:网络策略 spec 包含在给定命名空间中定义特定网络策略所需的所有信息。

podSelector:每个网络策略都包含一个 podSelector,它选择策略适用的 Pod 分组。示例策略选择标签为“role=db”的 Pod。空的 podSelector 选择命名空间中的所有 Pod。

policyTypes:每个网络策略都包含一个 policyTypes 列表,其中可能包含 IngressEgress 或两者兼而有之。policyTypes 字段指示给定策略是否适用于选定 Pod 的入口流量、选定 Pod 的出口流量或两者兼而有之。如果在网络策略上未指定 policyTypes,则默认情况下将始终设置 Ingress,如果网络策略有任何出口规则,则将设置 Egress

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

egress:每个网络策略都可以包含允许的 egress 规则列表。每条规则都允许与 toports 部分匹配的流量。示例策略包含一条规则,该规则匹配到 10.0.0.0/24 中任何目标的 TCP 端口 5978 上的流量。

因此,示例网络策略

  1. 隔离 default 命名空间中 role=db Pod 的入口和出口流量(如果它们尚未被隔离)

  2. (入口规则)允许从以下位置连接到 default 命名空间中标签为 role=db 的所有 Pod 的 TCP 端口 6379

    • default 命名空间中标签为 role=frontend 的任何 Pod
    • 标签为 project=myproject 的命名空间中的任何 Pod
    • 范围为 172.17.0.0172.17.0.255172.17.2.0172.17.255.255 的 IP 地址(即,172.17.0.0/16 的所有地址,但 172.17.1.0/24 除外)
  3. (出口规则)允许从 default 命名空间中标签为 role=db 的任何 Pod 连接到 CIDR 10.0.0.0/24 的 TCP 端口 5978

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

tofrom 选择器的行为

ingress from 部分或 egress to 部分中可以指定四种选择器

podSelector:这将选择与网络策略位于同一命名空间中的特定 Pod,这些 Pod 应被允许作为入口源或出口目标。

namespaceSelector:这将选择应允许所有 Pod 作为入口源或出口目标的特定命名空间。

namespaceSelector podSelector:同时指定 namespaceSelectorpodSelector 的单个 to/from 条目选择特定命名空间中的特定 Pod。请务必使用正确的 YAML 语法。例如

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

此策略包含一个 from 元素,允许来自标签为 user=alice 的命名空间中标签为 role=client 的 Pod 的连接。但以下策略不同

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

它在 from 数组中包含两个元素,并允许来自本地命名空间中带有标签 role=client 的 Pod 的连接,来自任何命名空间中带有标签 user=alice 的任何 Pod 的连接。

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

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

集群入口和出口机制通常需要重写数据包的源 IP 或目标 IP。在这种情况下,未定义是在 NetworkPolicy 处理之前还是之后发生这种情况,并且对于网络插件、云提供商、Service 实现等的组合,行为可能会有所不同。

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

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

默认策略

默认情况下,如果命名空间中不存在任何策略,则允许所有入口和出口流量进出该命名空间中的 Pod。以下示例允许您更改该命名空间中的默认行为。

默认拒绝所有入口流量

您可以通过创建一个选择所有 Pod 但不允许任何入口流量进入这些 Pod 的 NetworkPolicy 来为命名空间创建“默认”入口隔离策略。

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

这确保了即使未被任何其他 NetworkPolicy 选择的 Pod 仍将被隔离以进行入口。此策略不会影响从任何 Pod 出口的隔离。

允许所有入口流量

如果要允许所有传入连接到命名空间中的所有 Pod,则可以创建一个明确允许该连接的策略。

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

有了此策略,任何其他策略都不能导致拒绝任何传入连接到这些 Pod。此策略对从任何 Pod 出口的隔离没有影响。

默认拒绝所有出口流量

您可以通过创建一个选择所有 Pod 但不允许任何出口流量从这些 Pod 流出的 NetworkPolicy 来为命名空间创建“默认”出口隔离策略。

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

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

允许所有出口流量

如果要允许来自命名空间中所有 Pod 的所有连接,则可以创建一个策略,该策略明确允许来自该命名空间中 Pod 的所有传出连接。

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

有了此策略,任何其他策略都不能导致拒绝来自这些 Pod 的任何传出连接。此策略对进入任何 Pod 的隔离没有影响。

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

您可以通过在该命名空间中创建以下 NetworkPolicy 来为命名空间创建“默认”策略,该策略阻止所有入口和出口流量。

---
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 等协议,而其他插件可能会拒绝。

定位端口范围

功能状态: Kubernetes v1.25 [稳定]

编写 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 上带有标签 role=db 的任何 Pod 通过 TCP 与 10.0.0.0/24 范围内的任何 IP 通信,前提是目标端口在 32000 到 32768 之间。

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

  • endPort 字段必须等于或大于 port 字段。
  • 仅当还定义了 port 时,才能定义 endPort
  • 两个端口都必须是数字。

按标签定位多个命名空间

在这种情况下,您的 Egress NetworkPolicy 使用其标签名称定位多个命名空间。为此,您需要标记目标命名空间。例如

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"]

按名称定位命名空间

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

虽然 NetworkPolicy 无法使用某些对象字段按名称定位命名空间,但您可以使用标准化标签来定位特定命名空间。

Pod 生命周期

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

网络插件处理 NetworkPolicy 后,

  1. 受给定 NetworkPolicy 影响的所有新创建的 Pod 将在启动之前被隔离。NetworkPolicy 的实现必须确保过滤在整个 Pod 生命周期中都有效,即使从该 Pod 中任何容器启动的第一刻起也是如此。因为它们应用于 Pod 级别,所以 NetworkPolicies 同样适用于 init 容器、sidecar 容器和常规容器。

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

每个创建的 NetworkPolicy 最终都将由网络插件处理,但是无法从 Kubernetes API 准确判断何时发生这种情况。

因此,Pod 必须能够适应以与预期不同的网络连接启动。如果您需要确保 Pod 在启动之前可以到达某些目标,则可以使用 init 容器 等待这些目标在 kubelet 启动应用程序容器之前可达。

每个 NetworkPolicy 最终都将应用于所有选定的 Pod。因为网络插件可能以分布式方式实现 NetworkPolicy,所以在首次创建 Pod 时,或者在 Pod 或策略更改时,Pod 可能会看到网络策略的视图略有不一致。例如,应该能够到达节点 1 上的 Pod A 和节点 2 上的 Pod B 的新创建的 Pod 可能会发现它可以立即到达 Pod A,但要等到几秒钟后才能到达 Pod B。

NetworkPolicy 和 hostNetwork Pod

hostNetwork Pod 的 NetworkPolicy 行为未定义,但应限于两种可能性

  • 网络插件可以将 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.30,NetworkPolicy API 中尚不存在以下功能,但您也许可以使用操作系统组件(例如 SELinux、OpenVSwitch、IPTables 等)或第 7 层技术(Ingress 控制器、服务网格实现)或准入控制器来实现解决方法。如果您不熟悉 Kubernetes 中的网络安全,则值得注意的是,以下用户故事(尚)无法使用 NetworkPolicy API 实现。

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

NetworkPolicy 对现有连接的影响

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

下一步

  • 有关更多示例,请参阅 声明网络策略 演练。
  • 有关 NetworkPolicy 资源启用的常见场景,请参阅更多方案
上次修改时间:2024 年 4 月 1 日下午 3:05 PST:为 NetworkPolicy 添加 API 参考的指示牌 (f1c4eb8457)