这篇文章已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。
为容器网络接口 (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 中发展企业安全模式的文化采用。
在这篇文章中,我们将讨论
- 为什么我们为 网络策略 创建了一个子项目
- 我们如何更改 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 端到端套件一直都有网络策略测试,但这些测试并未在 CI 中运行,并且它们的实现方式没有提供关于策略在集群中如何工作的整体、易于消费的信息。这是因为原始测试没有提供任何关于集群中连通性的可视化摘要。因此,我们最初的目标是使最终用户测试(通常由管理员或用户用于诊断集群一致性)易于解释,从而更容易确认 CNI 对网络策略的支持。
为了解决确认 CNI 支持大多数用户关心的策略基本功能的问题,我们在 Kubernetes e2e 框架中构建了一个新的网络策略验证工具,该工具允许对策略及其对集群中一组标准 Pod 的影响进行可视化检查。例如,请看以下测试输出。我们在 OVN Kubernetes 中发现了一个错误。这个错误现在已经解决了。使用这个工具,这个错误很容易被描述,其中某些策略导致状态修改,后来导致流量被错误地阻止(即使在所有网络策略都从集群中删除之后)。
这是所讨论的测试的网络策略
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 错误的情况下观察到的连接结果。请注意,前三行表明来自命名空间 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 小组合作修复出口策略处理中的错误。
然而,即使这个工具使得验证大约 30 个常见场景变得容易,它也无法验证 所有 网络策略场景 - 因为人们可能会创建大量的可能排列(从技术上讲,我们可以说这个数字是无限的,因为可以创建无限数量的可能命名空间/Pod/端口/协议变化)。
一旦这些测试开始运行,我们便与上游 SIG Network 和 SIG Testing 社区(感谢 Antonio Ojea 和 Ben Elder)合作,建立了一个 testgrid 网络策略作业。此作业针对 GCE(以 Calico 作为网络策略提供程序) 持续运行整个网络策略测试套件。
作为子项目的一部分,我们的职责是帮助确保在这些测试中断时,我们可以有效地对其进行分类。
Cyclonus:迈向网络策略一致性的下一步
在我们完成验证工作的同时,社区也明确表示,一般来说,我们需要解决测试所有可能的网络策略实现的总体问题。例如,Dan Winship 最近编写了一个 KEP,其中引入了网络策略微版本控制的概念,以适应 在 API 级别描述它。
为了响应这种越来越明显的需要来全面评估来自所有供应商的网络策略实现,Matt Fenwick 决定通过创建 Cyclonus 来再次改进我们的网络策略验证方法。
Cyclonus 是一种综合性的网络策略模糊测试工具,它通过定义与端到端测试中演示的类似真值表/策略组合,同时还提供策略“类别”的分层表示,来验证 CNI 提供商是否符合数百种不同的网络策略场景。到目前为止,我们几乎在测试过的每个 CNI 中都发现了一些有趣的细微差别和问题,甚至还贡献了一些修复程序。
要执行 Cyclonus 验证运行,您需要创建一个类似于
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) 也提供 SCTP 支持。
以下是一些我们如何使用 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
标志),因为较旧的镜像中不包含新的网络策略测试。
网络策略 API 和用户体验的改进
除了清理实现网络策略的 CNI 插件的验证流程外,子项目的贡献者还花了一些时间改进 Kubernetes 网络策略 API,以满足一些常见的需求。经过数月的讨论,我们最终确定了几个需要改进的核心领域
端口范围策略:我们现在允许您为策略指定一个端口范围。这允许对 FTP 或虚拟化等场景感兴趣的用户启用高级策略。网络策略的端口范围选项将在 Kubernetes 1.21 中可用。请在定位端口范围中阅读更多信息。
命名空间作为名称策略:允许 Kubernetes >= 1.21 中的用户在构建网络策略对象时使用名称来定位命名空间。这是在 API Machinery 方面与 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 并在发出请求时主动阻止它们。
- 集群管理策略:我们正在努力为未来启用管理或集群范围的网络策略。这些正在迭代地提交给网络策略子项目。您可以在集群范围网络策略中阅读相关信息。
网络策略子项目在东部时间下午 4 点的周一开会。有关详细信息,请查看 SIG Network 社区存储库。我们很乐意与您交流、开发新功能,并尽可能帮助您为您的集群采用 K8s 网络策略。
关于用户反馈的简要说明
我们从用户那里收到了很多关于网络策略的想法和反馈。很多人对网络策略有有趣的想法,但我们发现,作为一个子项目,很少有人对全面实施这些想法非常感兴趣。
对网络策略 API 的几乎每个更改都包括数周或数月的讨论,以涵盖不同的情况并确保不会引入 CVE。因此,从长远来看,长期所有权是我们改进网络策略用户体验的最大障碍。
我们鼓励任何人向我们提供反馈,但我们目前最紧迫的问题是找到长期所有者来帮助我们推动变革。
这不需要大量的技术知识,而只需要长期致力于帮助我们保持井然有序、处理文书工作并迭代 K8s 功能过程的许多阶段。如果您想帮助我们并参与其中,请在 SIG Network 邮件列表中或 k8s.io slack 频道中的 SIG Network 房间中联系我们!
任何人都可以出一份力,帮助使网络策略变得更好!