保护集群元数据


概览

GKE 使用实例元数据配置节点虚拟机,但其中某些元数据可能比较敏感,应该受到保护,以免受集群上运行的工作负载的影响。

准备工作

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

使用以下任一方法设定默认的 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

配置节点服务帐号

由于每个节点的服务帐号凭据将继续向工作负载公开,因此应确保已对服务帐号配置了所需的最小权限。然后,将此服务帐号附加到节点,这样攻击者就无法通过使用 Compute Engine API 直接访问节点实例来绕开 GKE 的元数据保护。

请勿使用具有 compute.instances.get 权限、Compute Instance Admin 角色或其他类似权限的服务帐号,因为潜在攻击者可以通过它们使用 Compute Engine API 获取实例元数据。最佳做法是使用服务帐号权限(而非访问权限范围)限制节点虚拟机的权限。如需了解详情,请参阅 Compute Engine 的服务帐号文档。

如果您没有节点服务帐号,可以使用以下命令创建一个:

export NODE_SA_NAME=gke-node-sa
gcloud iam service-accounts create $NODE_SA_NAME \
  --display-name "Node Service Account"
export NODE_SA_EMAIL=$(gcloud iam service-accounts list --format='value(email)' \
  --filter='displayName:Node Service Account')

如需为服务帐号配置必要的角色和权限,请运行以下命令。PROJECT 是您的项目 ID

export PROJECT=$(gcloud config get-value project)

gcloud projects add-iam-policy-binding $PROJECT \
  --member serviceAccount:$NODE_SA_EMAIL \
  --role roles/monitoring.metricWriter
gcloud projects add-iam-policy-binding $PROJECT \
  --member serviceAccount:$NODE_SA_EMAIL \
  --role roles/monitoring.viewer
gcloud projects add-iam-policy-binding $PROJECT \
  --member serviceAccount:$NODE_SA_EMAIL \
  --role roles/logging.logWriter

此外,如果您的集群从 Container Registry 中拉取私有映像,请添加 storage.objectViewer 角色:

gcloud projects add-iam-policy-binding $PROJECT \
  --member serviceAccount:$NODE_SA_EMAIL \
  --role roles/storage.objectViewer

停用旧版元数据 API 并从其转换到新版

Compute Engine v1beta1 和 v0.1 元数据服务器端点已启用,并将按计划关停。请务必确保将所有请求更新为使用 v1 端点。

Compute Engine 实例元数据服务器提供旧版 v0.1 和 v1beta1 端点,这些端点不会强制执行元数据查询标头。这是 v1 API 中的一项功能,可能会让潜在的攻击者难以检索实例元数据,例如服务器端请求伪造 (SSRF)。除非特别要求,否则我们建议停用这些旧版 API。

以下部分介绍了如何执行以下操作:

  • 确定正在使用已弃用端点的节点。如果有任何节点使用已弃用的端点,您需要迁移这些节点。

  • 创建新的集群或节点池,并为这些已确定的节点停用旧版元数据服务器端点。

识别使用旧版元数据服务器端点的节点

您可以使用 check-legacy-endpoint-access 工具,确定哪个 Kubernetes Engine 节点正在使用旧版元数据服务器端点。在集群中应用此工具时,此工具会每隔 5 分钟记录您的节点向 v0.1 和 v1beta1 端点发出的所有请求。 此工具还可用于识别、调试和验证 Kubernetes Engine 中旧版端点的使用情况。

要设置 check-legacy-endpoint-access 工具,请完成以下步骤:

  1. 在每个集群中运行以下命令:

    kubectl apply -f \
    https://raw.githubusercontent.com/GoogleCloudPlatform\
    /k8s-node-tools/master/check-legacy-endpoint-access/check-legacy-endpoint-access.yaml
  2. 查询已经收集到的包含旧版端点使用信息的日志。如需查询日志,请在每个集群中运行以下命令:

    kubectl -n kube-system logs -l \
    app=check-legacy-endpoint-access | grep "access count"

您还可以查看 Stackdriver Logging 中收集到的日志。

  1. 转到 Cloud Console 中的 Cloud Logging > 日志(日志查看器)页面:

    转到“日志查看器”页面

  2. 应用以下过滤条件:

    resource.type="container"
    resource.labels.namespace_id="kube-system"
    logName:"/check-legacy-endpoint-access"

    转到过滤结果视图

  3. 确定节点后,您需要确定正在使用这些端点的进程。有关确定这些进程的说明,请参阅确定进程

  4. 迁移这些进程迁移以使用 v1 元数据服务器端点。有关迁移 Compute Engine 节点以及端点差异的说明,请参阅迁移至 v1 元数据服务器端点

  5. 移除 check-legacy-endpoint-access daemonset:

    kubectl delete daemonset check-legacy-endpoint-access -n kube-system

创建新节点池并停用旧版元数据 API

创建服务帐号后,您可以使用 gcloud 命令行工具创建新的节点池(或在新集群中创建默认节点池),并停用旧版元数据 API。

如需创建新的节点池并停用旧版元数据 API,请使用 --metadata disable-legacy-endpoints=true 标志。例如:

gcloud container node-pools create pool-name \
  --service-account=$NODE_SA_EMAIL \
  --metadata disable-legacy-endpoints=true

也可以使用相同标志创建新集群,并在默认节点池中停用旧版元数据 API。例如:

gcloud container clusters create cluster-name \
  --service-account=$NODE_SA_EMAIL \
  --metadata disable-legacy-endpoints=true

更新现有集群以停用旧版元数据 API

创建新节点池并停用旧版元数据 API 后,您可以按照节点池迁移指南更新现有集群以使用它。

验证是否已停用旧版元数据 API

停用旧版实例元数据 API 后,对 /0.1//v1beta1/ 元数据服务器端点的请求将返回 403 Forbidden

如需验证旧版元数据 API 已停用,您可以在 Pod 中运行 curl 命令:

root@pod-name# curl -H 'Metadata-Flavor: Google' \
'http://metadata.google.internal/computeMetadata/v1/instance/attributes/disable-legacy-endpoints'
true
root@pod-name# curl 'http://metadata.google.internal/computeMetadata/v1beta1/instance/id'
... Error 403 (Forbidden) ... Legacy metadata endpoint accessed: /computeMetadata/v1beta1/instance/id Legacy metadata endpoints are disabled. Please use the /v1/ endpoint. ...

元数据隐藏

GKE 的元数据隐藏可以保护一些潜在的敏感系统元数据免受集群上运行的用户工作负载的影响。

在 Kubernetes 1.9.3 及更高版本中,您可以启用元数据隐藏,以防止用户 Pod 访问集群节点的某些虚拟机元数据,例如 Kubelet 凭据和虚拟机实例信息。具体而言,元数据隐藏可为 kube-env(包含 Kubelet 凭据)和虚拟机的实例身份令牌提供访问保护。

元数据隐藏使用防火墙阻隔从用户 Pod(未在 HostNetwork 上运行的 Pod)到集群元数据服务器的流量,仅允许安全查询。防火墙阻止用户 Pod 使用 Kubelet 凭据进行提升权限攻击,或使用虚拟机身份进行实例升级攻击。

限制

  • 元数据隐藏只能为 kube-env 和节点的实例身份令牌提供访问保护。
  • 元数据隐藏不限制对节点服务帐号的访问。
  • 元数据隐藏不限制对其他相关实例元数据的访问。
  • 元数据隐藏不限制对其他旧版元数据 API 的访问。

创建带元数据隐藏的新集群或节点池

创建服务帐号后,您可以使用 gcloud 命令行工具创建新的集群,并启用元数据隐藏。

如需创建集群并启用元数据隐藏,请在 shell 或终端窗口中运行以下命令:

gcloud beta container clusters create cluster-name \
  --workload-metadata-from-node=SECURE \
  --service-account=$NODE_SA_EMAIL \
  --metadata disable-legacy-endpoints=true \
  [additional parameters and flags omitted]

其中:

  • cluster-name 是要创建的集群的名称。
  • --workload-metadata-from-node 设为 SECURE;将标志设置为 EXPOSEDUNSPECIFIED 会停用元数据隐藏。

验证集群工作负载中已隐藏身份令牌元数据

元数据隐藏后,应该无法通过节点的实例身份令牌请求签名。如需验证请求会将元数据隐藏明确通知用户,您可以在 Pod 中运行 curl 命令:

root@pod-name# curl -H "Metadata-Flavor: Google" \
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://www.example.com'
This metadata endpoint is concealed.