Kubernetes 文档 / 概念 / 服务、负载均衡和联网 / 网络策略

Kubernetes 文档 / 概念 / 服务、负载均衡和联网 / 网络策略

此文档从 Kubernetes 官网摘录
中文地址
英文地址
如果你希望针对 TCP、UDP 和 SCTP 协议在 IP 地址或端口层面控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。
Pod 可以与之通信的实体是通过如下三个标识符的组合来辩识的:

  1. 其他被允许的 Pod(例外:Pod 无法阻塞对自身的访问)
  2. 被允许的名字空间
  3. IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)

在定义基于 Pod 或名字空间的 NetworkPolicy 时, 你会使用选择算符来设定哪些流量可以进入或离开与该算符匹配的 Pod。

另外,当创建基于 IP 的 NetworkPolicy 时,我们基于 IP 组块(CIDR 范围)来定义策略。

前置条件

网络策略通过网络插件来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。 创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。

Pod 隔离的两种类型

Pod 有两种隔离:出口的隔离和入口的隔离。

默认情况下,一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Egress”,则该 Pod 是出口隔离的, 我们称这样的策略适用于该 Pod 的出口。

默认情况下,一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Ingress”,则该 Pod 被隔离入口, 我们称这种策略适用于该 Pod 的入口。

网络策略是相加的,所以不会产生冲突。如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。

要允许从源 Pod 到目的 Pod 的某个连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许此连接。 如果任何一方不允许此连接,则连接将会失败。

NetworkPolicy 资源

vim networkpolicy.yaml

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
说明:
除非选择支持网络策略的网络解决方案,否则将上述示例发送到 API 服务器没有任何效果。

选择器 to 和 from 的行为

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

podSelector:此选择器将在与 NetworkPolicy 相同的名字空间中选择特定的 Pod,应将其允许作为入站流量来源或出站流量目的地。

namespaceSelector:此选择器将选择特定的名字空间,应将所有 Pod 用作其入站流量来源或出站流量目的地。

namespaceSelector 和 podSelector:一个指定 namespaceSelector 和 podSelector 的 to/from 条目选择特定名字空间中的特定 Pod。 注意使用正确的 YAML 语法;下面的策略:

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

此策略在 from 数组中仅包含一个元素,只允许来自标有 role=client 的 Pod 且该 Pod 所在的名字空间中标有 user=alice 的连接。但是这项策略:

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

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

默认策略

默认情况下,如果名字空间中不存在任何策略,则所有进出该名字空间中 Pod 的流量都被允许。 以下示例使你可以更改该名字空间中的默认行为。

默认拒绝所有入站流量

你可以通过创建选择所有 Pod 但不允许任何进入这些 Pod 的入站流量的 NetworkPolicy 来为名字空间创建 “default” 隔离策略。
network-policy-default-deny-ingress.yaml

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

允许所有入站流量

如果你想允许一个名字空间中所有 Pod 的所有入站连接,你可以创建一个明确允许的策略。

network-policy-allow-all-ingress.yaml

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

默认拒绝所有出站流量

network-policy-default-deny-egress.yaml

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

允许所有出站流量

network-policy-allow-all-egress.yaml

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

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

network-policy-default-deny-all.yaml

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

网络流量过滤

NetworkPolicy 是为第 4 层连接 (TCP、UDP 和可选的 SCTP)所定义的。对于所有其他协议,这种网络流量过滤的行为可能因网络插件而异。
说明:
你必须使用支持 SCTP 协议 NetworkPolicy 的 CNI 插件。

当 deny all 网络策略被定义时,此策略只能保证拒绝 TCP、UDP 和 SCTP 连接。 对于 ARP 或 ICMP 这类其他协议,这种网络流量过滤行为是未定义的。 相同的情况也适用于 allow 规则:当特定 Pod 被允许作为入口源或出口目的地时, 对于(例如)ICMP 数据包会发生什么是未定义的。 ICMP 这类协议可能被某些网络插件所允许,而被另一些网络插件所拒绝。

针对某个端口范围

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

这一目的可以通过使用 endPort 字段来实现,如下例所示:
networkpolicy-multiport-egress.yaml

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"]
说明:
你不可以在 NetworkPolicy 中直接指定名字空间的名称。 你必须使用带有 matchLabels 或 matchExpressions 的 namespaceSelector 来根据标签选择名字空间。

基于名字指向某名字空间

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

如果 NetworkPolicy 无法在某些对象字段中指向某名字空间, 你可以使用标准的标签方式来指向特定名字空间。

Pod 生命周期

说明:
以下内容适用于使用了合规网络插件和 NetworkPolicy 合规实现的集群。

当新的 NetworkPolicy 对象被创建时,网络插件可能需要一些时间来处理这个新对象。 如果受到 NetworkPolicy 影响的 Pod 在网络插件完成 NetworkPolicy 处理之前就被创建了, 那么该 Pod 可能会最初处于无保护状态,而在 NetworkPolicy 处理完成后被应用隔离规则。

一旦 NetworkPolicy 被网络插件处理,

  1. 所有受给定 NetworkPolicy 影响的新建 Pod 都将在启动前被隔离。 NetworkPolicy 的实现必须确保过滤规则在整个 Pod 生命周期内是有效的, 这个生命周期要从该 Pod 的任何容器启动的第一刻算起。 因为 NetworkPolicy 在 Pod 层面被应用,所以 NetworkPolicy 同样适用于 Init 容器、边车容器和常规容器。
  2. Allow 规则最终将在隔离规则之后被应用(或者可能同时被应用)。 在最糟的情况下,如果隔离规则已被应用,但 allow 规则尚未被应用, 那么新建的 Pod 在初始启动时可能根本没有网络连接。

用户所创建的每个 NetworkPolicy 最终都会被网络插件处理,但无法使用 Kubernetes API 来获知确切的处理时间。

因此,若 Pod 启动时使用非预期的网络连接,它必须保持稳定。 如果你需要确保 Pod 在启动之前能够访问特定的目标,可以使用 Init 容器在 kubelet 启动应用容器之前等待这些目的地变得可达。

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

NetworkPolicy 和 hostNetwork Pod

针对 hostNetwork Pod 的 NetworkPolicy 行为是未定义的,但应限制为以下两种可能:

网络插件可以从所有其他流量中辨别出 hostNetwork Pod 流量 (包括能够从同一节点上的不同 hostNetwork Pod 中辨别流量), 网络插件还可以像处理 Pod 网络流量一样,对 hostNetwork Pod 应用 NetworkPolicy。
网络插件无法正确辨别 hostNetwork Pod 流量,因此在匹配 podSelector 和 namespaceSelector 时会忽略 hostNetwork Pod。流向/来自 hostNetwork Pod 的流量的处理方式与流向/来自节点 IP 的所有其他流量一样。(这是最常见的实现方式。)

通过网络策略(至少目前还)无法完成的工作

到 Kubernetes 1.30 为止,NetworkPolicy API 还不支持以下功能, 不过你可能可以使用操作系统组件(如 SELinux、OpenVSwitch、IPTables 等等) 或者第七层技术(Ingress 控制器、服务网格实现)或准入控制器来实现一些替代方案。 如果你对 Kubernetes 中的网络安全性还不太了解,了解使用 NetworkPolicy API 还无法实现下面的用户场景是很值得的。

  • 强制集群内部流量经过某公用网关(这种场景最好通过服务网格或其他代理来实现);
  • 与 TLS 相关的场景(考虑使用服务网格或者 Ingress 控制器);
  • 特定于节点的策略(你可以使用 CIDR 来表达这一需求不过你无法使用节点在 Kubernetes 中的其他标识信息来辩识目标节点);
  • 基于名字来选择服务(不过,你可以使用 标签 来选择目标 Pod 或名字空间,这也通常是一种可靠的替代方案);
  • 创建或管理由第三方来实际完成的“策略请求”;
  • 实现适用于所有名字空间或 Pod 的默认策略(某些第三方 Kubernetes 发行版本或项目可以做到这点);
  • 高级的策略查询或者可达性相关工具;
  • 生成网络安全事件日志的能力(例如,被阻塞或接收的连接请求);
  • 显式地拒绝策略的能力(目前,NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略);
  • 禁止本地回路或指向宿主的网络流量(Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求)。

NetworkPolicy 对现有连接的影响

当应用到现有连接的 NetworkPolicy 集合发生变化时,这可能是由于 NetworkPolicy 发生变化或者在现有连接过程中(主体和对等方)策略所选择的名字空间或 Pod 的相关标签发生变化, 此时是否对该现有连接生效是由实现定义的。例如:某个策略的创建导致之前允许的连接被拒绝, 底层网络插件的实现负责定义这个新策略是否会关闭现有的连接。 建议不要以可能影响现有连接的方式修改策略、Pod 或名字空间。

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值