租户隔离 - Amazon EKS

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

租户隔离

当我们想到多租户时,我们通常希望将用户或应用程序与在共享基础架构上运行的其他用户或应用程序隔离开来。

Kubernetes 是一个单租户协调器,即控制平面的单个实例由集群中的所有租户共享。但是,你可以使用各种各样的 Kubernetes 对象来创建多租户的外观。例如,可以实施命名空间和基于角色的访问控制 (RBAC),以便在逻辑上隔离租户彼此。同样,配额和限制范围可用于控制每个租户可以消耗的集群资源量。尽管如此,集群是唯一能提供强大安全边界的结构。这是因为设法获得对集群内主机的访问权限的攻击者可以检索安装在该主机上的所有 Secrets 和 Volumes。ConfigMaps他们还可以模仿 Kubelet,这样他们就可以操纵在集群内横向 and/or 移动的节点的属性。

以下各节将介绍如何实现租户隔离,同时降低使用像 Kubernetes 这样的单一租户协调器的风险。

软多租户

在软多租户中,您可以使用原生 Kubernetes 结构,例如命名空间、角色和角色绑定以及网络策略,在租户之间创建逻辑分离。例如,RBAC 可以阻止租户访问或操纵彼此的资源。配额和限制范围控制每个租户可以消耗的集群资源量,而网络策略可以帮助防止部署到不同命名空间的应用程序相互通信。

但是,这些控件都无法阻止来自不同租户的 Pod 共享节点。如果需要更强的隔离,你可以使用节点选择器、反亲和性规则、 and/or 污点和容忍度来强制将来自不同租户的 Pod 调度到单独的节点(通常称为单租户节点)。在租户众多的环境中,这可能会变得相当复杂,而且成本高得令人望而却步。

重要

使用命名空间实现的软多租户不允许您向租户提供经过筛选的命名空间列表,因为命名空间是一种全局范围的类型。如果租户能够查看特定的命名空间,则可以查看集群中的所有命名空间。

警告

借 soft-multi-tenancy助,默认情况下,租户仍可以查询 CoreDNS 以了解集群内运行的所有服务。攻击者可以通过 ..svc.cluster.local从集群中的任何 pod 运行 dig SRV 来利用此漏洞。如果您需要限制访问集群中运行的服务的 DNS 记录,请考虑使用适用于 CoreDNS 的防火墙或策略插件。有关更多信息,请参阅 p https://github.com/coredns/olicy# kubernetes-metadata-multi-tenancy-policy

Kiosk 是一个开源项目,可以帮助实现软多租户。它以一系列 CRDs 和控制器形式实现,提供以下功能:

  • 用于在共享 Kubernetes 集群中分离租户的@@ 账户和账户用户

  • 为账户用户@@ 配置自助命名空间

  • 账户限制可确保共享集群时的服务质量和公平性

  • 用于安全租户隔离和自助服务@@ 命名空间初始化的命名空间模板

Loft 是 Kiosk 维护者提供的商业产品 DevSpace,它增加了以下功能:

  • 多集群访问权限用于授予对不同集群中空间的访问权限

  • 睡眠模式可在空间处于非活动状态时缩小部署规模

  • 使用 OIDC 身份@@ 验证提供商进行单点登录,例如 GitHub

软多租户可以解决三个主要用例。

企业设置

第一种是在企业环境中,“租户” 是半信任的,因为他们是员工、承包商或以其他方式获得组织授权。每个租户通常会与行政部门(例如部门或团队)保持一致。

在这种类型的设置中,集群管理员通常负责创建命名空间和管理策略。他们还可以实现委托管理模式,让某些人监督命名空间,允许他们对非策略相关对象(如部署、服务、pod、作业等)执行 CRUD 操作。

在此设置中,容器运行时提供的隔离可能是可以接受的,或者可能需要通过其他控制来增强 pod 安全性。如果需要更严格的隔离,则可能还需要限制不同命名空间中的服务之间的通信。

Kubernetes 即服务

相比之下,软多租户可以在你想要提供 Kubernetes 即服务 (KaaS) 的环境中使用。使用 KaaS,您的应用程序与一组控制器一起托管在共享集群中CRDs ,并提供一组 PaaS 服务。租户直接与 Kubernetes API 服务器交互,并允许租户对非策略对象执行 CRUD 操作。还有一个自助服务要素,即可以允许租户创建和管理自己的命名空间。在这种类型的环境中,假定租户正在运行不受信任的代码。

要隔离此类环境中的租户,您可能需要实施严格的网络策略和容器沙箱。沙盒是你在 Firecracker 等微型虚拟机或用户空间内核中运行 pod 容器的地方。现在,你可以使用 EKS Fargate 创建沙盒吊舱。

软件即服务 (SaaS)

软多租户的最终用例是( Software-as-a-ServiceSaaS)设置。在此环境中,每个租户都与集群中运行的应用程序的特定实例相关联。每个实例通常都有自己的数据,并使用单独的访问控制,这些控制通常独立于 Kubernetes RBAC。

与其他用例不同,SaaS 设置中的租户不直接与 Kubernetes API 交互。相反,SaaS 应用程序负责与 Kubernetes API 接口,以创建支持每个租户所需的对象。

Kubernetes 构造

在每种情况下,都使用以下结构将租户彼此隔离开来:

命名空间

命名空间是实现软多租户的基础。它们允许您将集群划分为逻辑分区。实现多租户所需的配额、网络策略、服务帐号和其他对象限定在命名空间内。

网络策略

默认情况下,允许 Kubernetes 集群中的所有 Pod 相互通信。使用网络策略可以改变这种行为。

网络策略使用标签或 IP 地址范围限制 Pod 之间的通信。在需要在租户之间进行严格网络隔离的多租户环境中,我们建议从一个默认规则开始,即拒绝 Pod 之间的通信,另一个允许所有 Pod 查询 DNS 服务器以获取名称解析的规则。有了这些,你就可以开始添加更宽松的规则了,允许在命名空间内进行通信。这可以根据需要进一步完善。

注意

在 AWS 上运行 Kubernetes 时,Amazon VPC CNI 现在支持 Kubernetes 网络策略,以创建可以隔离敏感工作负载并保护它们免受未经授权的访问的策略。这意味着您可以在 Amazon EKS 集群中使用网络策略 API 的所有功能。这种精细控制级别使您能够实现最小权限原则,从而确保只有经过授权的 Pod 才能相互通信。

重要

网络策略是必要的,但还不够。网络策略的实施需要诸如 Calico 或 Cilium 之类的策略引擎。

基于角色的访问控制 (RBAC)

角色和角色绑定是 Kubernetes 对象,用于在 Kubernetes 中强制执行基于角色的访问控制 (RBAC)。角色包含可以对集群中的对象执行的操作列表。角色绑定指定角色适用的个人或群组。在企业和 KaaS 设置中,RBAC 可用于允许选定的群组或个人管理对象。

限额

配额用于定义集群中托管的工作负载的限制。使用配额,您可以指定 Pod 可以消耗的最大 CPU 和内存量,也可以限制可以在集群或命名空间中分配的资源数量。限制范围允许您声明每个限制的最小值、最大值和默认值。

在共享集群中过度使用资源通常是有益的,因为这样可以最大限度地利用资源。但是,不受限制地访问集群会导致资源短缺,从而导致性能下降和应用程序可用性丧失。如果 Pod 的请求设置得太低并且实际资源利用率超过节点的容量,则该节点将开始承受 CPU 或内存压力。发生这种情况时,Pod 可能会被重新启动 and/or 并逐出节点。

为了防止这种情况发生,您应该计划对多租户环境中的命名空间施加配额,以强制租户在集群上调度 pod 时指定请求和限制。它还可以通过限制 Pod 可以消耗的资源量来缓解潜在的拒绝服务。

您还可以使用配额来分配集群的资源,使其与租户的支出保持一致。这在 KaaS 场景中特别有用。

Pod 优先级和抢占权

当你想让 Pod 相对于其他 Pod 更重要时,Pod 优先级和抢占会很有用。例如,使用容器优先级,您可以将客户 A 的 Pod 配置为比客户 B 更高的优先级运行。当可用容量不足时,调度器会将优先级较低的 pod 从客户 B 中驱逐出来,以容纳客户 A 的高优先级 Pod。这在 SaaS 环境中特别方便,在这种环境中,愿意支付溢价的客户会获得更高的优先级。

重要

Pod 优先级可能会对其他优先级较低的 Pod 产生不良影响。例如,尽管受害的 pod 会优雅地终止,但不能保证,这可能会使依赖法定数量的 Pod 优先级较低的应用程序崩溃,但请参见抢占限制。 PodDisruptionBudget

缓解控制措施

作为多租户环境的管理员,你最关心的是防止攻击者访问底层主机。应考虑采取以下控制措施来降低这种风险:

容器的沙盒化执行环境

沙箱是一种技术,通过这种技术,每个容器都可以在自己的独立虚拟机中运行。执行吊舱沙箱的技术包括 Firecracker 和 Weave 的 Fire kube。

有关努力使 Firecracker 成为 EKS 支持的运行时的其他信息,请参阅 1238496944684597248.html。https://threadreaderapp.com/thread/

开放策略代理 (OPA) 和看门人

Gatekeeper 是一个 Kubernetes 准入控制器,用于强制执行使用 OPA 创建的策略。借助 OPA,您可以创建一个策略,在单独的实例上运行租户的 Pod,或者以比其他租户更高的优先级运行 Pod。可以在该项目的 GitHub存储库中找到常用 OPA 策略的集合。

还有一个适用于 CoreDNS 的实验性 OPA 插件,允许你使用 OPA 来处理 CoreDNS 返回 filter/control 的记录。

Kyverno

Kyverno 是一个 Kubernetes 原生策略引擎,它可以使用策略作为 Kubernetes 资源来验证、变异和生成配置。Kyverno 使用 Kustomize 风格的叠加层进行验证,支持 JSON 补丁和用于突变的战略合并补丁,并且可以基于灵活的触发器跨命名空间克隆资源。

您可以使用 Kyverno 来隔离命名空间、强制执行 pod 安全和其他最佳实践,并生成网络策略等默认配置。该项目的 GitHub存储库中包含几个示例。Kyverno 网站上的政策库中包含了许多其他内容。

将租户工作负载隔离到特定节点

将租户工作负载限制在特定节点上运行可用于增强软多租户模型中的隔离。使用这种方法,租户特定的工作负载只能在为相应租户配置的节点上运行。为了实现这种隔离,使用原生 Kubernetes 属性(节点亲和性以及污点和容忍)来定位特定节点进行 Pod 调度,并防止来自其他租户的 Pod 被调度到租户特定的节点上。

第 1 部分-节点亲和力

Kubernetes 节点亲和力用于根据节点标签定位节点进行调度。使用节点亲和性规则,Pod 会被吸引到与选择器术语匹配的特定节点。在以下 pod 规范中,requiredDuringSchedulingIgnoredDuringExecution节点亲和性应用于相应的 pod。结果是 pod 将瞄准标有以下键/值的节点:。node-restriction.kubernetes.io/tenant: tenants-x

... spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-restriction.kubernetes.io/tenant operator: In values: - tenants-x ...

有了这种节点亲和性,在调度期间需要标签,但在执行期间不需要;如果底层节点的标签发生变化,则不会仅仅因为标签更改而驱逐这些 Pod。但是,未来的日程安排可能会受到影响。

警告

的标签前缀在 Kubernetes 中node-restriction.kubernetes.io/具有特殊含义。 NodeRestriction为 EKS 集群启用,可kubelet防止使用带有此前缀的adding/removing/updating标签。攻击者无法使用 a kubelet’s credentials to update the node object or modify the system setup to pass these labels into `kubelet s kubelet 不允许修改这些标签。如果将此前缀用于所有 Pod 到节点的调度,则可以防止攻击者可能想要通过修改节点标签将一组不同的工作负载吸引到节点的情况。

我们可以使用节点选择器来代替节点亲和性。但是,节点亲和性更具表现力,允许在 Pod 调度期间考虑更多条件。有关差异和更高级的调度选择的更多信息,请参阅这篇关于高级 Kubernetes pod 到节点调度的CNCF博客文章。

第 2 部分-污点和容忍

将 pod 吸引到节点只是这种由三部分组成的方法的第一部分。为了使这种方法起作用,我们必须阻止 Pod 调度到未获授权的节点上。为了击退不需要的或未经授权的 pod,Kubernetes 使用节点污点。污点用于在节点上设置阻止 Pod 被调度的条件。下面的污点使用键值对。tenant: tenants-x

... taints: - key: tenant value: tenants-x effect: NoSchedule ...

鉴于上述节点taint,只有容忍污点的 Pod 才允许在该节点上调度。为了允许将已授权的 pod 调度到节点上,相应的 pod 规格必须包含toleration对污点的 a,如下所示。

... tolerations: - effect: NoSchedule key: tenant operator: Equal value: tenants-x ...

具有上述内容的 Pod toleration 不会停止在节点上调度,至少不会因为这种特定的污点而停止。在某些情况下(例如节点资源压力),Kubernetes 还使用污点来暂时停止 Pod 的调度。通过节点亲和性以及污点和容忍度,我们可以有效地将所需的 Pod 吸引到特定节点并击退不需要的 Pod。

重要

某些 Kubernetes 容器需要在所有节点上运行。这些 pod 的例子是容器网络接口 (CNI)kube-proxy 守护程序集启动的 pod。为此,这些 pod 的规格包含非常宽松的容忍度,可以容忍不同的污点。应注意不要改变这些容忍度。更改这些容忍度可能会导致集群操作不正确。此外,可以使用 OPA/GatekeeperKyverno 等策略管理工具来编写验证策略,防止未经授权的 pod 使用这些允许容忍度。

第 3 部分-基于策略的节点选择管理

有多种工具可用于帮助管理 Pod 规范的节点亲和性和容忍度,包括在 CICD 管道中强制执行规则。但是,还应在 Kubernetes 集群级别强制执行隔离。为此,可以使用策略管理工具根据请求有效负载对入站 Kubernetes API 服务器请求进行更,以应用上述相应的节点关联性规则和容忍度。

例如,可以为发往 tenants-x 命名空间的 Pod 加上正确的节点亲和力和容忍度,以允许在 tenants-x 节点上进行调度。利用使用 Kubernetes 变更准入 Webhook 配置的策略管理工具,可以使用策略来改变入站 Pod 的规范。这些突变添加了所需的元素以允许所需的调度。添加节点亲和性的示例 OPA/Gatekeeper 策略如下所示。

apiVersion: mutations.gatekeeper.sh/v1alpha1 kind: Assign metadata: name: mutator-add-nodeaffinity-pod annotations: aws-eks-best-practices/description: >- Adds Node affinity - https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity spec: applyTo: - groups: [""] kinds: ["Pod"] versions: ["v1"] match: namespaces: ["tenants-x"] location: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms" parameters: assign: value: - matchExpressions: - key: "tenant" operator: In values: - "tenants-x"

上述策略应用于 Kubernetes API 服务器请求,即将 pod 应用于 tenants-x 命名空间。该策略添加了requiredDuringSchedulingIgnoredDuringExecution节点亲和性规则,这样 Pod 就会被带tenant: tenants-x标签的节点所吸引。

第二个策略(如下所示)使用与目标命名空间和组、种类和版本相同的匹配标准,将容忍度添加到相同的 pod 规格中。

apiVersion: mutations.gatekeeper.sh/v1alpha1 kind: Assign metadata: name: mutator-add-toleration-pod annotations: aws-eks-best-practices/description: >- Adds toleration - https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ spec: applyTo: - groups: [""] kinds: ["Pod"] versions: ["v1"] match: namespaces: ["tenants-x"] location: "spec.tolerations" parameters: assign: value: - key: "tenant" operator: "Equal" value: "tenants-x" effect: "NoSchedule"

上述策略特定于 pod;这是由于策略location元素中变异元素的路径造成的。可以编写其他策略来处理创建 pod 的资源,例如 Deployment 和 Job 资源。列出的政策和其他示例可以在本指南的配套GitHub项目中看到。

这两个突变的结果是,Pod 被所需的节点所吸引,同时不会被特定的节点污点所排斥。为了验证这一点,我们可以看到两次kubectl调用的输出片段,以获取标有标签的节点tenant=tenants-x,然后在tenants-x命名空间中获取 pod。

kubectl get nodes -l tenant=tenants-x NAME ip-10-0-11-255... ip-10-0-28-81... ip-10-0-43-107... kubectl -n tenants-x get pods -owide NAME READY STATUS RESTARTS AGE IP NODE tenant-test-deploy-58b895ff87-2q7xw 1/1 Running 0 13s 10.0.42.143 ip-10-0-43-107... tenant-test-deploy-58b895ff87-9b6hg 1/1 Running 0 13s 10.0.18.145 ip-10-0-28-81... tenant-test-deploy-58b895ff87-nxvw5 1/1 Running 0 13s 10.0.30.117 ip-10-0-28-81... tenant-test-deploy-58b895ff87-vw796 1/1 Running 0 13s 10.0.3.113 ip-10-0-11-255... tenant-test-pod 1/1 Running 0 13s 10.0.35.83 ip-10-0-43-107...

从上面的输出中我们可以看出,所有的 pod 都被调度到标有标注的节点上tenant=tenants-x。简而言之,Pod 只能在所需的节点上运行,而其他 pod(没有所需的亲和力和容忍度)则不会。租户工作负载被有效隔离。

变异的 pod 规范示例如下所示。

apiVersion: v1 kind: Pod metadata: name: tenant-test-pod namespace: tenants-x spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: tenant operator: In values: - tenants-x ... tolerations: - effect: NoSchedule key: tenant operator: Equal value: tenants-x ...
重要

集成到 Kubernetes API 服务器请求流的策略管理工具,使用变更和验证准入 Webhook,旨在在指定的时间范围内响应 API 服务器的请求。这通常是 3 秒或更短。如果 webhook 调用未能在配置的时间内返回响应,则入站 API 服务器请求的变异 and/or 验证可能会发生,也可能不会发生。此行为取决于准入 webhook 配置设置为 “失效打开” 还是 “失效关闭”。

在上面的示例中,我们使用了为 OPA/Gatekeeper 编写的策略。但是,还有其他策略管理工具可以处理我们的节点选择用例。例如,此 Kyverno 策略可用于处理节点亲和性突变。

注意

如果操作正常,变更策略将对入站 API 服务器请求负载进行所需的更改。但是,还应包括验证策略,以验证在允许更改持续存在之前,是否发生了所需的更改。在使用这些策略进行 tenant-to-node隔离时,这一点尤其重要。另外一个好主意是加入审计策略,以便定期检查集群中是否有不需要的配置。

参考信息

艰难的多租户

硬多租户可以通过为每个租户配置单独的集群来实现。虽然这在租户之间提供了非常牢固的隔离,但它也有一些缺点。

首先,当你有很多租户时,这种方法很快就会变得昂贵。您不仅需要为每个集群支付控制平面成本,而且无法在集群之间共享计算资源。这最终会导致碎片化,其中一部分集群未得到充分利用,而其他集群则被过度利用。

其次,您可能需要购买或构建特殊工具来管理所有这些集群。随着时间的推移,管理成百上千个集群可能会变得过于繁琐。

最后,与创建命名空间相比,为每个租户创建集群会很慢。但是,在高度监管的行业或需要严格隔离的SaaS环境中,可能需要采取硬租赁方法。

未来的方向

Kubernetes 社区已经认识到软多租户当前的缺点以及硬多租户面临的挑战。多租户特别兴趣小组 (SIG) 正试图通过多个孵化项目来解决这些缺点,包括分层命名空间控制器 (HNC) 和虚拟集群。

HNC 提案 (KEP) 描述了一种在命名空间之间创建父子关系的方法,该方法具有 [policy] 对象继承,并允许租户管理员创建子命名空间。

虚拟集群提案描述了一种机制,用于为集群中的每个租户(也称为 “Kubernetes 上的 Kubernetes on Kubernetes”)创建单独的控制平面服务实例,包括 API 服务器、控制器管理器和调度器。

多租户基准测试提案提供了使用命名空间进行隔离和分段的共享集群的指南,以及用于验证是否符合指南的命令行工具 kubectl-mtb

多集群管理工具和资源