本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
为容器网络接口(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 下的网络策略子项目的创建。显而易见,作为一个社区,我们需要一个关于如何在 Kubernetes 上进行 Pod 网络安全的可靠方案,并且这个方案需要一个社区来支持,以便促进企业安全模式在 K8s 中的文化采纳。
在这篇文章中,我们将讨论
- 我们为什么为网络策略创建一个子项目
- 我们如何修改 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 网络策略 API 的一个子集,而另一些则实现了一个超集。例如,某些插件不支持以命名端口为目标的能力;其他插件不适用于某些 IP 地址类型,并且相似策略类型的语义也存在差异。
- 一些 CNI 插件与其他 CNI 插件结合以实现网络策略(canal),一些 CNI 可能会混合实现(multus),并且一些云服务将路由与网络策略实现分开处理。
尽管这种复杂性在一定程度上是为了支持不同的环境所必需的,但最终用户发现他们需要遵循多步骤过程来实施网络策略以保护他们的应用程序
- 确认他们的网络插件支持网络策略(有些不支持,例如 Flannel)
- 确认其集群的网络插件支持他们感兴趣的特定网络策略功能(再次,命名端口或端口范围的示例在这里浮现在脑海中)
- 确认其应用程序的网络策略定义正在做正确的事情
- 找出供应商策略实现的细微差别,并检查该实现是否具有 CNI 中立实现(这有时足以满足用户需求)
上游 Kubernetes 中的 NetworkPolicy 项目旨在提供一个社区,人们可以在其中学习和贡献 Kubernetes NetworkPolicy API 及其周围生态系统。
第一步:一个直观易懂的网络策略验证框架
Kubernetes 端到端套件一直都有网络策略测试,但这些测试并未在 CI 中运行,而且它们的实现方式未能提供关于策略在集群中如何工作的全面、易于理解的信息。这是因为最初的测试没有提供任何集群连接的可视化摘要。因此,我们最初的目标是让验证 CNI 对网络策略的支持变得容易,方法是让端到端测试(管理员或用户通常用于诊断集群一致性)易于解释。
为了解决确认 CNI 支持大多数用户关心的基本策略功能的问题,我们在 Kubernetes e2e 框架中构建了一个新的 NetworkPolicy 验证工具,该工具允许对策略及其对集群中一组标准 Pod 的影响进行可视化检查。例如,以下是测试输出。我们在 OVN Kubernetes 中发现了一个 bug。这个 bug 现已解决。使用这个工具,该 bug 非常容易表征,即某些策略导致状态修改,后来导致流量被错误地阻塞(即使所有 NetworkPolicy 已从集群中删除)。
这是相关测试的网络策略
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 都可以访问其他 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 Network Policy 作业。此作业持续针对 GCE 并使用 Calico 作为网络策略提供商运行整个网络策略测试套件。
作为子项目的一部分,我们的职责是帮助确保当这些测试失败时,我们能够有效地对其进行分类。
Cyclonus:网络策略一致性的下一步
在我们完成验证工作的同时,社区明确表示,总的来说,我们需要解决测试所有可能的网络策略实现的整体问题。例如,最近编写了一个 KEP,其中引入了网络策略微版本化的概念,以适应 Dan Winship 在 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 支持,例如 Calico)。
以下示例说明了我们如何使用 Cyclonus 从网络策略角度帮助改进 CNI 实现
好消息是,Antrea 和 Calico 已经合并了所有发现问题的修复,其他 CNI 提供商也在 SIG Network 和 Network Policy 子项目的支持下努力解决问题。
您有兴趣验证集群上的网络策略功能吗?(如果您关心安全或提供多租户 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
标志),因为旧镜像将不包含新的网络策略测试。
网络策略 API 和用户体验的改进
除了清理实现网络策略的 CNI 插件的验证工作外,子项目贡献者还花了一些时间改进 Kubernetes 网络策略 API,以满足一些常见请求的功能。经过数月的审议,我们最终确定了几个核心改进领域
端口范围策略:我们现在允许您为策略指定一个端口*范围*。这使得对 FTP 或虚拟化等场景感兴趣的用户能够启用高级策略。网络策略的端口范围选项将在 Kubernetes 1.21 中可用。请阅读以端口范围为目标以获取更多信息。
命名空间作为名称策略:允许 Kubernetes >= 1.21 的用户在构建网络策略对象时使用名称来指定命名空间。这是与 API 机器方面的 Jordan Liggitt 和 Tim Hockin 协作完成的。这一更改使我们能够在不实际更改 API 的情况下改进网络策略用户体验!有关更多详细信息,您可以阅读命名空间页面中的自动标签。简单来说,对于 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 的网络策略一致性结果,但我们无法独自维护网络策略测试数据中的所有细微之处。目前,我们使用 github actions 和 Kind 在 CI 中进行测试。
未来展望
我们还在为网络策略的未来进行一些改进,包括
- 完全限定域名策略:Google Cloud 团队创建了一个 FQDN 策略原型(我们对此感到非常兴奋)。该工具使用网络策略 API 来针对 L7 URL 强制执行策略,方法是查找它们的 IP 并在发出请求时主动阻止它们。
- 集群管理策略:我们正在努力为未来实现*管理*或*集群范围*的网络策略。这些正在逐步提交给 NetworkPolicy 子项目。您可以在集群范围的网络策略中阅读相关内容。
网络策略子项目于美国东部时间每周一下午 4 点举行会议。有关详细信息,请查看 SIG Network 社区存储库。我们很乐意与您交流、共同开发,并尽可能帮助您在集群中采用 K8s 网络策略。
关于用户反馈的快速说明
我们从用户那里获得了大量关于网络策略的创意和反馈。许多人对网络策略有有趣的看法,但我们发现,作为一个子项目,很少有人真正有兴趣全面实现这些想法。
几乎所有对 NetworkPolicy API 的更改都涉及数周或数月的讨论,以涵盖不同的情况并确保没有引入 CVE。因此,长期所有权是我们在改进 NetworkPolicy 用户体验方面最大的障碍。
我们鼓励任何人向我们提供反馈,但我们目前最紧迫的问题是寻找*长期所有者来帮助我们推动变革*。
这不需要大量的技术知识,而只需要长期致力于帮助我们保持组织、处理文书工作并迭代 K8s 功能流程的许多阶段。如果您想帮助我们并参与其中,请在 SIG Network 邮件列表或 k8s.io Slack 频道中的 SIG Network 房间联系我们!
任何人都可以尽一份力,让网络策略变得更好!