本文发布已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已不再正确。

定义容器网络接口 (CNI) 提供商的网络策略一致性

特别感谢 Tim Hockin 和 Bowie Du (Google)、Dan Winship 和 Antonio Ojea (Red Hat)、Casey Davenport 和 Shaun Crampton (Tigera) 以及 Abhishek Raut 和 Antonin Bas (VMware) 对这项工作的支持,并与我们一起解决不同容器网络接口 (CNI) 中的问题。

2020 年 4 月围绕“节点本地”网络策略的一次简短对话,激发了从 SIG Network 创建 NetworkPolicy 子项目的想法。很明显,作为一个社区,我们需要一个坚实的故事,讲述如何在 Kubernetes 上进行 Pod 网络安全,并且这个故事需要一个围绕它的社区,以便在 K8s 中推广企业安全模式的文化采用。

在这篇文章中,我们将讨论

  • 为什么我们创建了一个网络策略 (Network Policies) 的子项目
  • 我们如何更改 Kubernetes 端到端 (e2e) 框架以可视化你的 CNI 提供商的网络策略实现
  • 我们基于这些原则构建的综合网络策略一致性验证工具 Cyclonus 的初步结果
  • 子项目贡献者对网络策略用户体验的改进

为什么我们创建了一个网络策略的子项目

到 2020 年 4 月,许多 CNI 开始出现,并且许多供应商以微妙的方式实现这些 CNI。用户开始对如何为不同场景实现策略感到困惑,并要求新功能。很明显,我们需要开始统一我们在 Kubernetes 中思考网络策略的方式,以避免 API 分裂和不必要的复杂性。

例如

  • 为了对用户环境保持灵活性,Calico 作为 CNI 提供商可以运行在 IPIP 或 VXLAN 模式下,或不带封装开销。Antrea 和 Cilium 等 CNI 也提供类似的配置选项。
  • 一些 CNI 插件提供 iptables 来实现网络策略,以及其他选项,而其他 CNI 则使用完全不同的技术栈(例如,Antrea 项目使用 Open vSwitch 规则)。
  • 一些 CNI 插件只实现了 Kubernetes NetworkPolicy API 的一个子集,而另一些则实现了超集。例如,某些插件不支持按名称端口进行目标定位;其他插件不支持某些 IP 地址类型,并且类似策略类型的语义也存在分歧。
  • 一些 CNI 插件与其他 CNI 插件结合以实现网络策略 (canal),一些 CNI 可能会混合实现 (multus),并且一些云环境将路由与网络策略实现分开处理。

尽管这种复杂性在一定程度上对于支持不同环境是必要的,但最终用户发现他们需要遵循多步骤流程来实施网络策略以保护其应用

  • 确认其网络插件支持网络策略(有些不支持,例如 Flannel)
  • 确认其集群的网络插件支持他们感兴趣的特定网络策略功能(再次强调,这里想到按名称端口或端口范围的例子)
  • 确认其应用的网络策略定义是否正确执行
  • 找出供应商策略实现的细微差别,并检查该实现是否具有 CNI 中立实现(这有时对用户来说足够了)

上游 Kubernetes 中的 NetworkPolicy 项目旨在提供一个社区,让人们可以在这里学习 Kubernetes NetworkPolicy API 和相关生态系统,并为其做出贡献。

第一步:一个直观易用且易于理解的网络策略验证框架

Kubernetes 端到端 (e2e) 测试套件一直包含 NetworkPolicy 测试,但这些测试并未在 CI 中运行,并且它们的实现方式未能提供关于策略如何在集群中工作的全面、易于理解的信息。这是因为最初的测试没有提供任何形式的集群连接性可视化摘要。因此,我们最初着手通过使端到端测试(管理员或用户经常使用它们来诊断集群一致性)易于解释来轻松确认 CNI 对 NetworkPolicy 的支持。

为了解决确认 CNI 支持大多数用户关注的策略基本功能的问题,我们在 Kubernetes e2e 框架中构建了一个新的 NetworkPolicy 验证工具,该工具允许对策略及其对集群中一组标准 Pod 的影响进行可视化检查。例如,请看以下测试输出。我们在OVN Kubernetes 中发现了一个 Bug。此 Bug 现已解决。使用此工具,该 Bug 非常容易描述,其中某些策略导致了状态修改,后来导致流量被错误地阻止(即使在集群中删除了所有网络策略之后)。

这是该测试所使用的网络策略

metadata:
  creationTimestamp: null
  name: allow-ingress-port-80
spec:
  ingress:
  - ports:
    - port: serve-80-tcp
  podSelector: {}

这是预期的连接结果。测试设置包括 9 个 Pod(3 个命名空间:x、y 和 z;每个命名空间中有 3 个 Pod:a、b 和 c);每个 Pod 在相同的端口和协议上运行一个服务器,在没有网络策略的情况下可以通过 HTTP 调用访问。通过使用agnhost 网络工具在其他 Pod 预期提供服务的端口和协议上发出 HTTP 调用来验证连接性。测试场景首先运行连接性检查,以确保每个 Pod 都可以相互访问,共有 81 个 (= 9 x 9) 数据点。这称为“对照”。然后根据测试场景应用扰动:创建、更新和删除策略;从 Pod 和命名空间中添加和删除标签等。每次更改后,都会重新收集连接矩阵,并与预期的连接性进行比较。

这些结果以简单的矩阵形式提供了连接性的可视化指示。最左侧列向下是“源”Pod,即发出请求的 Pod;最上方行横向是“目标”Pod,即接收请求的 Pod。. 表示连接被允许;X 表示连接被阻止。例如

Nov  4 16:58:43.449: INFO: expected:

-   x/a x/b x/c y/a y/b y/c z/a z/b z/c
x/a .   .   .   .   .   .   .   .   .
x/b .   .   .   .   .   .   .   .   .
x/c .   .   .   .   .   .   .   .   .
y/a .   .   .   .   .   .   .   .   .
y/b .   .   .   .   .   .   .   .   .
y/c .   .   .   .   .   .   .   .   .
z/a .   .   .   .   .   .   .   .   .
z/b .   .   .   .   .   .   .   .   .
z/c .   .   .   .   .   .   .   .   .

以下是 OVN Kubernetes Bug 情况下的观察到的连接结果。请注意前三行如何表明来自命名空间 x 的所有请求,无论 Pod 和目标如何,都被阻止了。由于这些实验结果与预期结果不符,将报告失败。请注意特定的失败模式如何清晰地揭示问题的性质——由于来自特定命名空间的请求全部失败,我们有了一个清晰的线索来开始调查。

Nov  4 16:58:43.449: INFO: observed:

-   x/a x/b x/c y/a y/b y/c z/a z/b z/c
x/a X   X   X   X   X   X   X   X   X
x/b X   X   X   X   X   X   X   X   X
x/c X   X   X   X   X   X   X   X   X
y/a .   .   .   .   .   .   .   .   .
y/b .   .   .   .   .   .   .   .   .
y/c .   .   .   .   .   .   .   .   .
z/a .   .   .   .   .   .   .   .   .
z/b .   .   .   .   .   .   .   .   .
z/c .   .   .   .   .   .   .   .   .

这是我们在网络策略小组中最早的成果之一,我们能够识别并与 OVN Kubernetes 小组合作修复出口策略处理中的一个 Bug。

然而,尽管此工具使验证大约 30 个常见场景变得容易,但它并不能验证所有网络策略场景 - 因为可能创建的排列组合数量巨大(从技术上讲,考虑到可以创建无限数量的命名空间/Pod/端口/协议变体,我们可以说这个数字是无限的)。

这些测试就位后,我们与上游 SIG Network 和 SIG Testing 社区(感谢 Antonio Ojea 和 Ben Elder)合作,建立了一个 testgrid 网络策略作业。此作业持续针对将 Calico 作为网络策略提供商的 GCE 运行整个网络策略测试套件。

作为子项目的一部分,我们的职责是帮助确保当这些测试失败时,我们可以有效地对其进行分类和处理。

Cyclonus:迈向网络策略一致性的下一步

大约在我们完成验证工作的同时,社区明确表示,总的来说,我们需要解决测试所有可能的网络策略实现这一整体问题。例如,Dan Winship 最近撰写了一个 KEP,引入了微版本控制的概念到网络策略,以适应在 API 层面描述这一点

为了响应全面评估所有供应商网络策略实现的这一日益明显的需求,Matt Fenwick 决定再次通过创建 Cyclonus 来改进我们的网络策略验证方法。

Cyclonus 是一个全面的网络策略模糊测试工具,它通过定义与端到端测试中展示的类似的真值表/策略组合,针对数百种不同的网络策略场景来验证 CNI 提供商,同时还提供了策略“类别”的分层表示。到目前为止,我们在几乎所有测试过的 CNI 中都发现了一些有趣的细微差别和问题,甚至贡献了一些修复。

要执行 Cyclonus 验证运行,你创建一个类似于以下的 Job Manifest

apiVersion: batch/v1
kind: Job
metadata:
  name: cyclonus
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - command:
            - ./cyclonus
            - generate
            - --perturbation-wait-seconds=15
            - --server-protocol=tcp,udp
          name: cyclonus
          imagePullPolicy: IfNotPresent
          image: mfenwick100/cyclonus:latest
      serviceAccount: cyclonus

Cyclonus 输出所有将要运行的测试用例报告

test cases to run by tag:
- target: 6
- peer-ipblock: 4
- udp: 16
- delete-pod: 1
- conflict: 16
- multi-port/protocol: 14
- ingress: 51
- all-pods: 14
- egress: 51
- all-namespaces: 10
- sctp: 10
- port: 56
- miscellaneous: 22
- direction: 100
- multi-peer: 0
- any-port-protocol: 2
- set-namespace-labels: 1
- upstream-e2e: 0
- allow-all: 6
- namespaces-by-label: 6
- deny-all: 10
- pathological: 6
- action: 6
- rule: 30
- policy-namespace: 4
- example: 0
- tcp: 16
- target-namespace: 3
- named-port: 24
- update-policy: 1
- any-peer: 2
- target-pod-selector: 3
- IP-block-with-except: 2
- pods-by-label: 6
- numbered-port: 28
- protocol: 42
- peer-pods: 20
- create-policy: 2
- policy-stack: 0
- any-port: 14
- delete-namespace: 1
- delete-policy: 1
- create-pod: 1
- IP-block-no-except: 2
- create-namespace: 1
- set-pod-labels: 1
testing 112 cases

请注意,Cyclonus 根据正在创建的策略类型标记其测试,因为策略本身是自动生成的,因此没有有意义的名称可供识别。

对于每个测试,Cyclonus 会输出一个真值表,这再次类似于 E2E 测试,并伴随正在验证的策略

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  creationTimestamp: null
  name: base
  namespace: x
spec:
  egress:
  - ports:
    - port: 81
    to:
    - namespaceSelector:
        matchExpressions:
        - key: ns
          operator: In
          values:
          - "y"
          - z
      podSelector:
        matchExpressions:
        - key: pod
          operator: In
          values:
          - a
          - b
  - ports:
    - port: 53
      protocol: UDP
  ingress:
  - from:
    - namespaceSelector:
        matchExpressions:
        - key: ns
          operator: In
          values:
          - x
          - "y"
      podSelector:
        matchExpressions:
        - key: pod
          operator: In
          values:
          - b
          - c
    ports:
    - port: 80
      protocol: TCP
  podSelector:
    matchLabels:
      pod: a
  policyTypes:
  - Ingress
  - Egress

0 wrong, 0 ignored, 81 correct
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| TCP/80 | X/A | X/B | X/C | Y/A | Y/B | Y/C | Z/A | Z/B | Z/C |
| TCP/81 |     |     |     |     |     |     |     |     |     |
| UDP/80 |     |     |     |     |     |     |     |     |     |
| UDP/81 |     |     |     |     |     |     |     |     |     |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| x/a    | X   | X   | X   | X   | X   | X   | X   | X   | X   |
|        | X   | X   | X   | .   | .   | X   | .   | .   | X   |
|        | X   | X   | X   | X   | X   | X   | X   | X   | X   |
|        | X   | X   | X   | X   | X   | X   | X   | X   | X   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| x/b    | .   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| x/c    | .   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| y/a    | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| y/b    | .   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| y/c    | .   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| z/a    | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| z/b    | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| z/c    | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
|        | X   | .   | .   | .   | .   | .   | .   | .   | .   |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+

Cyclonus 和 e2e 测试都使用相同的策略来验证网络策略 - 通过 TCP 或 UDP 探测 Pod,支持 SCTP 的 CNI 也可使用 SCTP(例如 Calico)。

作为我们如何使用 Cyclonus 从网络策略的角度帮助改进 CNI 实现的例子,你可以看看以下问题

好消息是 Antrea 和 Calico 已经合并了所有发现问题的修复,其他 CNI 提供商也在努力解决,并得到了 SIG Network 和网络策略子项目的支持。

你是否有兴趣验证你的集群上的网络策略功能?(如果你关心安全或提供多租户 SaaS,你应该会)如果是,你可以运行上游的端到端测试,或 Cyclonus,或两者都运行。

  • 如果你刚开始接触网络策略,并且只想简单验证大多数 CNI 应该正确实现的“常见”网络策略场景,以一种快速诊断的方式,那么你最好只运行 e2e 测试。
  • 如果你对 CNI 提供商的网络策略实现深感好奇,并想验证它:使用 Cyclonus。
  • 如果你想测试数百种策略,并评估你的 CNI 插件的全面功能,深入发现潜在的安全漏洞:使用 Cyclonus,并考虑运行端到端集群测试。
  • 如果你想参与上游的网络策略工作:使用 Cyclonus,并至少阅读哪些 e2e 测试是相关的概述。

从哪里开始进行网络策略测试?

  • Cyclonus 很容易在您的集群上运行,请查看 github 上的说明,并确定您的特定 CNI 配置是否完全符合数百种不同的 Kubernetes 网络策略 API 结构。
  • 或者,您可以使用诸如 sonobuoy 之类的工具运行 Kubernetes 中现有的 E2E 测试,并使用 --ginkgo.focus=NetworkPolicy 标志。请确保您使用适用于 K8s 1.21 或更高版本的 K8s 一致性镜像(例如,使用 --kube-conformance-image-version v1.21.0 标志),因为较旧的镜像不包含新的网络策略测试。

NetworkPolicy API 和用户体验的改进

除了整理实现 NetworkPolicies 的 CNI 插件的验证流程外,子项目贡献者还花了一些时间改进 Kubernetes NetworkPolicy API 的一些常见请求功能。经过数月的讨论,我们最终确定了几个核心改进领域

  • 端口范围策略:我们现在允许您为策略指定一个端口范围。这使得对 FTP 或虚拟化等场景感兴趣的用户能够启用高级策略。网络策略的端口范围选项将在 Kubernetes 1.21 中可用。请在定位端口范围中阅读更多内容。

  • 命名空间名称策略:允许 Kubernetes >= 1.21 中的用户在构建 Network Policy 对象时使用名称定位命名空间。这是与 API Machinery 方面的 Jordan Liggitt 和 Tim Hockin 合作完成的。这一更改使我们能够在不实际更改 API 的情况下改进 Network Policy 用户体验!有关更多详细信息,您可以在关于命名空间的页面中阅读 自动标签。简而言之(TL,DR;):对于 Kubernetes 1.21 及更高版本,默认情况下会为所有命名空间添加以下标签

    kubernetes.io/metadata.name: <name-of-namespace>
    

这意味着即使您无法编辑此命名空间的标签,也可以针对此命名空间编写命名空间策略。例如,此策略无需运行诸如 kubectl edit namespace 之类的命令,就能“正常工作”。实际上,由于 API 服务器默认值的魔力,即使您完全无法编辑或查看此命名空间的数据,它也能工作。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  # Allow inbound traffic to Pods labelled role=db, in the namespace 'default'
  # provided that the source is a Pod in the namespace 'my-namespace'
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: my-namespace

结果

在我们的测试中,我们发现

  • Antrea 和 Calico 在支持 Cyclonus 的所有场景方面做得很好,我们做了一些非常小的调整。
  • Cilium 也符合大多数策略,除了已知未完全支持的功能(例如,与 Cilium 处理 pod CIDR 策略的方式有关)。

如果您是 CNI 提供商,并且有兴趣帮助我们更好地策划大量的网络策略测试,请与我们联系!我们正在继续在此处整理来自 Cyclonus 的网络策略一致性结果,但我们自己无法维护网络策略测试数据中的所有微妙之处。目前,我们在 CI 中使用 github actions 和 Kind 进行测试。

未来展望

我们还在为 Network Policies 的未来努力进行一些改进,包括

  • 完全限定域名策略 (Fully qualified Domain policies):Google Cloud 团队创建了一个FQDN 策略原型(我们对此感到非常兴奋)。该工具使用 Network Policy API 对 L7 URL 强制执行策略,通过查找其 IP 并在发出请求时主动阻止它们。
  • 集群管理策略 (Cluster Administrative policies):我们正在努力为未来启用管理集群范围的 Network Policies。这些正在向 NetworkPolicy 子项目迭代地提交。您可以在集群范围的网络策略中阅读相关内容。

Network Policy 子项目每周一美国东部时间下午 4 点开会。有关详细信息,请查看 SIG Network 社区仓库。我们很乐意与您交流,一起攻克难题,并尽可能帮助您在集群中采用 K8s Network Policies。

关于用户反馈的简要说明

我们收到了很多用户关于 Network Policies 的想法和反馈。很多人对 Network Policies 有有趣的见解,但我们发现作为一个子项目,很少有人真正有兴趣将这些想法完全实现。

几乎所有对 NetworkPolicy API 的更改都涉及数周或数月的讨论,以涵盖不同的情况并确保不会引入 CVE。因此,长期拥有者是我们随着时间推移改进 NetworkPolicy 用户体验的最大障碍。

  • 我们已在此处记录了许多关于 Network Policy 对话的历史。
  • 我们还对用户进行了民意调查,了解他们希望在 Network Policy API 中看到什么,结果在此处

我们鼓励任何人向我们提供反馈,但我们目前最紧迫的问题是寻找长期拥有者来帮助我们推动变革

这不需要太多的技术知识,而只需要一个长期的承诺,帮助我们保持井井有条,处理文档工作,并迭代完成 K8s 功能开发的许多阶段。如果您想帮助我们并参与进来,请在 SIG Network 邮件列表或 k8s.io slack 频道的 SIG Network 房间与我们联系!

任何人都可以贡献一份力量,帮助改进 NetworkPolicies!