在专用节点池中隔离工作负载


本页面介绍如何通过指示 Google Kubernetes Engine (GKE) 在独立于特权 GKE 管理工作负载的专用节点池上安排工作负载,降低集群中的提权攻击风险。本页面适用于未启用节点自动预配功能的 Standard 集群。如需在 Autopilot 集群和启用了节点自动预配功能的 Standard 集群中隔离工作负载,请参阅在 GKE 中配置工作负载隔离

概览

GKE 集群使用特权 GKE 管理的工作负载来启用特定集群功能和特性,例如指标收集。这些工作负载具有在集群中正确运行的特殊权限。

部署到节点的工作负载可能会被恶意实体破解。将这些工作负载与特权 GKE 管理工作负载一起运行意味着攻破遭破解容器的攻击者可以使用节点上的特权工作负载的凭据来提升集群中的权限。

防止容器故障

您的主要防御措施应该是您的应用。GKE 具有多个功能,可用于强化集群和 Pod。在大多数情况下,我们强烈建议使用 GKE Sandbox 隔离您的工作负载。GKE Sandbox 基于 gVisor 开源项目,并在用户空间中实现 Linux 内核 API。每个 Pod 都在专用内核上运行,它会沙盒化应用以防止访问主机内核中的特权系统调用。GKE Sandbox 中运行的工作负载会自动调度到单独的节点上,与其他工作负载隔离。

您还应该遵循强化集群的安全性中的建议。

避免提升权限攻击

如果您无法使用 GKE Sandbox,并且除了其他安全强化措施之外,您还希望获得额外的隔离层,则可以使用节点污点节点亲和性在专用节点池上调度您的工作负载。节点污点会告知 GKE 避免在这些节点上调度没有相应容忍设置的工作负载(例如 GKE 管理的工作负载)。您自己的工作负载上的节点亲和性会告知 GKE 在专用节点上调度您的 Pod。

节点隔离的限制

  • 攻击者仍然可以从遭破解的节点发起拒绝服务 (DoS) 攻击。
  • 遭破解的节点仍然可以读取许多资源,包括集群中的所有 Pod 和命名空间。
  • 遭破解的节点可以访问在该节点上运行的每个 Pod 使用的 Secret 和凭据。
  • 使用单独的节点池隔离工作负载可能会影响费用效率、自动扩缩和资源利用率。
  • 遭破解的节点仍然可以绕过出站网络政策。
  • 某些由 GKE 管理的工作负载必须在集群的每个节点上运行,并配置为容忍所有污点。
  • 如果您部署的 DaemonSet 具有提升的权限并且可以容忍任何污点,这些 Pod 可能是从遭破解节点提升权限的途径。

节点隔离的工作原理

如需为工作负载实现节点隔离,您必须执行以下操作:

  1. 为工作负载添加节点池污点和标签。
  2. 使用相应的容忍和节点亲和性规则更新工作负载。

本指南假设您从集群中的一个节点池开始。使用节点亲和性以及节点污点并非强制性要求,但我们建议您使用,因为您可以更好地控制调度。

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行 gcloud components update 以获取最新版本。
  • 为您想要用于专用节点池的节点污点和节点标签选择特定名称。在此示例中,我们使用 workloadType=untrusted

为工作负载添加节点池污点和标签

为您的工作负载创建新的节点池,并应用节点污点和节点标签。在节点池级层应用污点或标签时,所有新节点(例如由自动扩缩功能创建的节点)都将自动获取指定的污点和标签。

您还可以向现有节点池添加节点污点和节点标签。如果使用 NoExecute 效果,则 GKE 会逐出在这些节点上运行且不容忍新污点的任何 Pod。

如需向新节点池添加污点和标签,请运行以下命令:

gcloud container node-pools create POOL_NAME \
    --cluster CLUSTER_NAME \
    --node-taints TAINT_KEY=TAINT_VALUE:TAINT_EFFECT \
    --node-labels LABEL_KEY=LABEL_VALUE

替换以下内容:

  • POOL_NAME:工作负载的新节点池的名称。
  • CLUSTER_NAME:GKE 集群的名称。
  • TAINT_KEY=TAINT_VALUE:与调度 TAINT_EFFECT 关联的键值对。例如 workloadType=untrusted
  • TAINT_EFFECT:以下效果值之一:NoSchedulePreferNoScheduleNoExecuteNoExecute 提供比 NoSchedule 更好的逐出保证。
  • LABEL_KEY=LABEL_VALUE:节点标签的键值对,与您在工作负载清单中指定的选择器对应。

为您的工作负载添加容忍和节点亲和性规则

污染专用节点池后,任何工作负载都无法在其上调度,除非它们具有与您添加的污染相对应的容忍度。将容忍度添加到您的工作负载规范中,让这些 Pod 在您的受污染节点池上调度。

如果您已为专用节点池添加标签,则还可以添加节点亲和性规则,以指示 GKE 仅在该节点池上调度工作负载。

以下示例为 workloadType=untrusted:NoExecute 污点添加容忍设置,并为 workloadType=untrusted 节点标签添加节点亲和性规则。

kind: Deployment
apiVersion: apps/v1
metadata:
  name: my-app
  namespace: default
  labels:
    app: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      tolerations:
      - key: TAINT_KEY
        operator: Equal
        value: TAINT_VALUE
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: LABEL_KEY
                operator: In
                values:
                - "LABEL_VALUE"
      containers:
      - name: sleep
        image: ubuntu
        command: ["/bin/sleep", "inf"]

替换以下内容:

  • TAINT_KEY:您应用于专用节点池的污点键。
  • TAINT_VALUE:您应用于专用节点池的污点值。
  • LABEL_KEY:您应用于专用节点池的节点标签键。
  • LABEL_VALUE:您应用于专用节点池的节点标签值。

使用 kubectl apply 更新 Deployment 时,GKE 会重新创建受影响的 Pod。节点亲和性规则强制 Pod 推送到您创建的专用节点池。容忍设置仅允许将这些 Pod 放置在节点上。

验证分离是否有效

要验证调度是否正常运行,请运行以下命令并检查您的工作负载是否在专用节点池上:

kubectl get pods -o=wide

建议和最佳实践

设置节点隔离后,我们建议您执行以下操作:

  • 通过添加 components.gke.io/gke-managed-components 污点,将特定节点池限制为仅由 GKE 管理的工作负载。添加此污点可防止您自己的 Pod 在这些节点上调度,从而改善隔离。
  • 创建新的节点池时,您可以通过向这些节点池添加自己的污点,阻止大多数由 GKE 管理的工作负载在这些节点上运行。
  • 每当您将新工作负载部署到集群时(例如安装第三方工具时),请审核 Pod 所需的权限。如果可能,请避免将使用提升权限的工作负载部署到共享节点。

后续步骤