利用 GKE Sandbox 强化工作负载隔离


本页面介绍如何在 Pod 中的容器执行未知代码或不可信代码,或者在需要与节点的额外隔离时,使用 GKE Sandbox 保护节点上的主机内核。

启用 GKE Sandbox

您可以在新集群或现有集群中启用 GKE Sandbox。

准备工作

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

使用以下任一方法设定默认的 gcloud 设置:

  • 使用 gcloud init(如果您想要在系统引导下完成默认设置)。
  • 使用 gcloud config(如果您想单独设置项目 ID、区域和地区)。

使用 gcloud init

如果您收到 One of [--zone, --region] must be supplied: Please specify location 错误,请完成本部分。

  1. 运行 gcloud init 并按照说明操作:

    gcloud init

    如果您要在远程服务器上使用 SSH,请使用 --console-only 标志来防止命令启动浏览器:

    gcloud init --console-only
  2. 按照说明授权 gcloud 使用您的 Google Cloud 帐号。
  3. 创建新配置或选择现有配置。
  4. 选择 Google Cloud 项目。
  5. 为可用区级集群选择默认 Compute Engine 可用区,或为区域级集群或 Autopilot 集群选择区域。

使用 gcloud config

  • 设置默认项目 ID
    gcloud config set project PROJECT_ID
  • 如果您使用的是可用区级集群,请设置默认计算可用区
    gcloud config set compute/zone COMPUTE_ZONE
  • 如果您使用的是 Autopilot 集群或区域级集群,请设置默认计算区域
    gcloud config set compute/region COMPUTE_REGION
  • gcloud 更新到最新版本:
    gcloud components update
  • GKE Sandbox 要求集群控制层面和节点使用 GKE 1.13.5-gke.15 或更高版本。
  • 确保 gcloud 命令为 243.0.0 或更高版本。

在新集群上

如需启用 GKE Sandbox,您需要配置节点池。默认节点池(即创建集群时在集群中创建的第一个节点池)无法使用 GKE Sandbox。如需在集群创建期间启用 GKE Sandbox,您必须在创建集群时添加第二个节点池。

控制台

要查看集群,请访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

  1. 转到 Cloud Console 中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击 创建

  3. 可选但建议执行的操作:在导航窗格中的集群下,点击功能并启用 Cloud Operations for GKE,以便系统记录 gVisor 消息。

  4. 点击 添加节点池

  5. 在导航窗格中的节点池下,展开新的节点池,然后点击节点

  6. 为节点池配置以下设置:

    1. 映像类型下拉列表中,选择包含 Containerd 的 Container-Optimized OS (cos_containerd)
    2. 机器配置下,选择系列机器类型

  7. 在导航窗格中您要配置的节点池的名称下,点击安全,然后选中启用 gVisor 沙盒复选框。

  8. 根据需要继续配置集群和节点池。

  9. 点击创建

gcloud

无法为默认节点池启用 GKE Sandbox,而且无法在使用 gcloud 命令创建新集群时创建其他节点池。但是,您可按照通常的方式创建集群。建议通过添加标志 --enable-stackdriver-kubernetes 来启用 Stackdriver Logging 和 Stackdriver Monitoring - 此操作可选。系统会记录 gVisor 消息。

然后使用 gcloud container node-pools create 命令,并将 --sandbox 标志设置为 type=gvisor。将方括号中的值替换为您自己的值。

gcloud container node-pools create node-pool-name \
  --cluster=cluster-name \
  --node-version=node-version \
  --machine-type=machine-type \
  --image-type=cos_containerd \
  --sandbox type=gvisor \

1.18.4-gke.1300 之前,gvisor RuntimeClass 在节点创建期间进行实例化。在任何工作负载调度到节点上之前,使用以下命令检查是否存在 gvisor RuntimeClass:

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

如果您运行的是 1.17.9-gke.1500 之前的版本或 1.18.6-gke.600 之前的 1.18 版本,则还必须等待 gvisor.config.common-webhooks.networking.gke.io 进行实例化。如需进行检查,请使用以下命令:

kubectl get mutatingwebhookconfiguration gvisor.config.common-webhooks.networking.gke.io
NAME                                              CREATED AT
gvisor.config.common-webhooks.networking.gke.io   2020-04-06T17:07:17Z

在现有集群上

您可以通过添加新节点池并为其启用该功能,在现有集群上启用 GKE Sandbox。

控制台

如需创建已启用 GKE Sandbox 的新节点池,请执行以下操作:

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击要修改的集群的名称。

  3. 点击 添加节点池

  4. 根据需要配置节点池详情页面。

  5. 在导航窗格中,点击节点并配置以下设置:

    1. 映像类型下拉列表中,选择包含 Containerd 的 Container-Optimized OS (cos_containerd)
    2. 机器配置下,选择系列机器类型

  6. 在导航窗格中,点击安全,然后选中启用 gVisor 沙盒复选框。

  7. 点击创建

gcloud

如需创建已启用 GKE Sandbox 的新节点池,请使用如下命令:

gcloud container node-pools create node-pool-name \
  --cluster=cluster-name \
  --machine-type=machine-type \
  --image-type=cos_containerd \
  --sandbox type=gvisor

1.18.4-gke.1300 之前,gvisor RuntimeClass 在节点创建期间进行实例化。在任何工作负载调度到节点上之前,使用以下命令检查是否存在 gvisor RuntimeClass:

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

如果您运行的是 1.17.9-gke.1500 之前的版本或 1.18.6-gke.600 之前的 1.18 版本,则还必须等待 gvisor.config.common-webhooks.networking.gke.io 进行实例化。如需进行检查,请使用以下命令:

kubectl get mutatingwebhookconfiguration gvisor.config.common-webhooks.networking.gke.io
NAME                                              CREATED AT
gvisor.config.common-webhooks.networking.gke.io   2020-04-06T17:07:17Z

可选:启用 Cloud Operations for GKE

建议在集群上启用 Cloud Operations for GKE,以便系统记录 gVisor 消息,此操作可选。对于新集群,Cloud Operations for GKE 默认处于启用状态。

您可以使用 Google Cloud Console 在现有集群上启用这些功能。

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击要修改的集群的名称。

  3. 功能下的 Cloud Operations for GKE 字段中,点击 修改 Cloud Operations for GKE

  4. 选中启用 Cloud Operations for GKE复选框。

  5. 从下拉列表中,选择系统与工作负载日志记录和监控

  6. 点击保存更改

使用 GKE Sandbox

在沙盒中运行应用

如需强制 Deployment 在启用了 GKE Sandbox 的节点上运行,请将其 spec.template.spec.runtimeClassName 设置为 gvisor,如以下 Deploymen 清单所示:

# httpd.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      runtimeClassName: gvisor
      containers:
      - name: httpd
        image: httpd

如需创建 Deployment,请使用 kubectl create 命令:

kubectl create -f httpd.yaml

Pod 已部署到节点池中启用了 GKE Sandbox 的节点。如需验证部署,请使用以下命令查找部署了 Pod 的节点:

kubectl get pods

输出类似于以下内容:

NAME                    READY   STATUS    RESTARTS   AGE
httpd-db5899bc9-dk7lk   1/1     Running   0          24s

在输出结果中找到 Pod 的名称,然后运行以下命令检查对应的 RuntimeClass 值:

kubectl get pods pod-name -o jsonpath='{.spec.runtimeClassName}'

输出为:

gvisor

或者,您可以列出每个 Pod 的 RuntimeClass,然后查找 RuntimeClass 设置为 gvisor 的 Pod:

kubectl get pods -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.runtimeClassName}\n{end}'

输出如下:

pod-name: gvisor

使用这种方法来验证 Pod 是否在沙盒中运行是非常可靠的做法,因为它不依赖于沙盒本身内的任何数据。从沙盒内报告的任何内容都可能存在缺陷或是恶意的,因此不可信。

运行常规 Pod 和沙盒化的 Pod

在节点池上启用 GKE Sandbox 后,您无需使用沙盒,即可通过使用节点污点和容忍设置,在这些节点上运行受信任的应用。这些 Pod 称为“常规 Pod”,以将它们与沙盒化的 Pod 区分开来。

与沙盒化的 Pod 一样,常规 Pod 也无法访问其他 Google Cloud 服务或集群元数据。此防护措施是节点配置的一部分。如果您的常规 Pod 或沙盒化的 Pod 需要访问 Google Cloud 服务,请使用 Workload Identity

GKE Sandbox 会向可运行沙盒化 Pod 的节点添加以下标签和污点:

labels:
  sandbox.gke.io/runtime: gvisor
taints:
- effect: NoSchedule
  key: sandbox.gke.io/runtime
  value: gvisor

除了 Pod 清单中的任何节点亲和性和容忍设置外,GKE Sandbox 还会对 RuntimeClass 设置为 gvisor 的所有 Pod 应用以下节点亲和性和容忍设置:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: sandbox.gke.io/runtime
          operator: In
          values:
          - gvisor
tolerations:
  - effect: NoSchedule
    key: sandbox.gke.io/runtime
    operator: Equal
    value: gvisor

如需在启用了 GKE 沙盒的节点上安排定期 Pod,请在 Pod 清单中手动应用上述节点亲和性和容忍度。

  • 如果您的 Pod 能够在启用了 GKE Sandbox 的节点上运行,请添加容忍设置。
  • 如果您的 Pod 必须在启用了 GKE Sandbox 的节点上运行,请同时添加节点亲和性和容忍设置。

例如,以下清单会修改在沙盒中运行应用时使用的清单,以便通过移除 runtimeClass 并添加上述污点和容忍设置,在具有沙盒化 Pod 的节点上运行它。

# httpd-no-sandbox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-no-sandbox
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: sandbox.gke.io/runtime
                operator: In
                values:
                - gvisor
      tolerations:
        - effect: NoSchedule
          key: sandbox.gke.io/runtime
          operator: Equal
          value: gvisor

首先,验证 Deployment 未在沙盒中运行:

kubectl get pods -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.runtimeClassName}\n{end}'

输出类似于以下内容:

httpd-db5899bc9-dk7lk: gvisor
httpd-no-sandbox-5bf87996c6-cfmmd:

之前创建的 httpd Deployment 正在沙盒中运行,因为其 runtimeClass 为 gvisorhttpd-no-sandbox Deployment 没有对应的 runtimeClass 值,因此未在沙盒中运行。

接下来,通过运行以下命令,验证未沙盒化的 Deployment 正在具有 GKE Sandbox 的节点上运行:

kubectl get pod -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.nodeName}\n{end}'

在值 nodeName 中嵌入节点池的名称。验证 Pod 是否在节点池中启用了 GKE Sandbox 的节点上运行。

验证元数据保护

如需验证元数据免受可运行沙盒化 Pod 的节点影响的断言,您可以运行以下测试:

  1. 使用 kubectl apply -f 从以下清单创建沙盒化 Deployment。它使用 fedora 映像,其中包含 curl 命令。Pod 会运行 /bin/sleep 命令,以确保 Deployment 运行 10000 秒。

    # sandbox-metadata-test.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: fedora
      labels:
        app: fedora
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: fedora
      template:
        metadata:
          labels:
            app: fedora
        spec:
          runtimeClassName: gvisor
          containers:
          - name: fedora
            image: fedora
            command: ["/bin/sleep","10000"]
    
  2. 使用 kubectl get pods 获取 Pod 的名称,然后使用 kubectl exec 以交互方式连接到 Pod。

    kubectl exec -it pod-name /bin/sh
    

    您在 /bin/sh 会话中连接到 Pod 中运行的容器。

  3. 在交互式会话中,尝试访问返回集群元数据的网址:

    curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env" -H "Metadata-Flavor: Google"
    

    由于丢弃数据包时不显示提示,因此命令会挂起并最终超时。

  4. Ctrl+C 终止 curl 命令,然后键入 exit 取消与 Pod 的连接。

  5. 从 YAML 清单中移除 RuntimeClass 行,然后使用 kubectl apply -f filename 重新部署 Pod。在没有 GKE Sandbox 的节点上终止沙盒化的 Pod,然后重新创建它。

  6. 获取新的 Pod 名称,使用 kubectl exec 连接到它,然后再次运行 curl 命令。这次会返回结果。此示例输出被截断。

    ALLOCATE_NODE_CIDRS: "true"
    API_SERVER_TEST_LOG_LEVEL: --v=3
    AUTOSCALER_ENV_VARS: kube_reserved=cpu=60m,memory=960Mi,ephemeral-storage=41Gi;...
    ...
    

    键入 exit 取消与 Pod 的连接。

  7. 移除该 Deployment:

    kubectl delete deployment fedora
    

停用 GKE Sandbox

目前无法更新节点池以停用 GKE Sandbox。 如需在现有节点池上停用 GKE Sandbox,您可以执行以下任一操作:

后续步骤