减少安全突发事件


本文档介绍了针对您的 Google Kubernetes Engine (GKE) 集群和容器上的潜在安全突发事件的常见缓解措施和响应方式。

强化集群的安全性中的建议有助于提高 GKE 工作负载的安全性。但是,即使采取了保护工作负载的措施,安全突发事件仍然可能会发生。

检测突发事件

为了检测潜在的突发事件,我们建议您设置一个收集和监控工作负载日志的流程。然后根据从日志中检测到的异常事件设置提醒。如果检测到异常情况,系统会发出提醒,以通知您的安全团队。随后您的安全团队即可审核潜在的突发事件。

从日志生成提醒

您可以根据具体的指标或操作自定义提醒。例如,有关 GKE 节点上 CPU 使用率过高的提醒可能表示,这些节点已遭到入侵,并被用于挖矿。

您应该在聚合日志和指标的系统中生成提醒。例如,您可以将 GKE 审核日志记录与 Cloud Logging 中基于日志的提醒结合使用。

如需详细了解如何根据来自 GKE 的日志设置提醒,请参阅GKE 应用的安全控制和取证分析。 如需查看安全相关查询的示例,请参阅审核日志文档。

响应安全突发事件

收到突发事件提醒后,请采取相应措施。在可行情况下,应修复漏洞。如果您不知道漏洞的根本原因或者尚未好修复措施,请应用缓解措施。

您可以采取的缓解措施取决于突发事件的严重程度,以及您对于问题的确定程度。

本指南将介绍在 GKE 上运行的工作负载上检测到突发事件后可采取的措施。下面按照严重程度递增顺序列出了您可以采取的相应措施:

以下部分介绍了这些缓解措施。

准备工作

本主题中用到的方法使用以下信息:

  • 您认为已遭到入侵的 Pod 的名称,即 POD_NAME
  • 运行容器或 Pod 的主机虚拟机的名称,即 NODE_NAME

此外,在执行任何这些操作之前,请考虑攻击者如果知道自己被发现时是否会做出负面反应。攻击者可能会决定删除数据或彻底破坏工作负载。如果相应风险过高,请考虑采取更有力的缓解措施,例如在进一步开展调查之前删除工作负载。

创建虚拟机的磁盘快照

通过创建虚拟机磁盘的快照,您可以在重新部署或删除工作负载之后进行取证调查。即便磁盘挂接到在运行中的实例上,您也可以为其创建快照。

  1. 如需为永久性磁盘创建快照,请先查明挂接到虚拟机的磁盘。 运行以下命令并查看 source 字段:

    gcloud compute instances describe NODE_NAME --zone COMPUTE_ZONE \
        --format="flattened([disks])"
    
  2. 查找包含 disks[NUMBER].source 的行。输出内容类似如下:

    disks[0].source: https://www.googleapis.com/compute/v1/projects/PROJECT_NAME/zones/COMPUTE_ZONE/disks/DISK_NAME
    

    来源名称中最后一个斜杠之后的部分即为磁盘名称。例如,磁盘名称为 gke-cluster-pool-1-abcdeffff-zgt8

  3. 如需完成快照创建,请运行以下命令:

    gcloud compute disks snapshot DISK_NAME
    

如需了解详情,请参阅 Compute Engine 文档中的创建永久性磁盘快照

在工作负载保持运行的情况下检查虚拟机

此外,在采取措施之前,请考虑攻击者可能拥有的访问权限。如果您怀疑某个容器遭到入侵,并且担心会打草惊蛇,您可以连接到容器并对其执行检查。在采取更具破坏性的措施之前执行检查有助于快速调查情况。检查也是对工作负载的破坏性最小的方法,但它不能阻止突发事件发生。

另外,为了避免使用具有特权的凭据登录到机器,您可以设置实时取证(例如 GRR 快速响应)、节点上代理或网络过滤,从而对工作负载执行分析。如需详细了解推荐取证工具,请参阅 GKE 应用的安全控制和取证分析

在检查实际运行的虚拟机之前减少访问权限

通过封锁排空以及限制被入侵容器的托管虚拟机的网络访问,您可以部分地将遭到入侵的容器与集群的其余部分隔离开来。限制对虚拟机的访问可以降低风险,但不能阻止攻击者利用某个严重漏洞在您的环境中横向移动。

封锁节点并从中排空其他工作负载

对某个节点进行封锁和排空会将与遭遇入侵的容器共存的工作负载移动到您集群中的其他虚拟机。封锁和排空可以降低攻击者影响同一节点上其他工作负载的能力。但这些举措不一定能阻止攻击者检查工作负载的永久性状态(例如通过检查容器映像内容进行此项检查)。

  1. 使用 kubectl 封锁该节点,并确保其上没有安排其他任何 Pod。

    kubectl cordon NODE_NAME
    

    封锁节点之后,节点上的其他 Pod 会被排空。

  2. 为您要隔离的 Pod 添加标签:

    kubectl label pods POD_NAME quarantine=true
    

    POD_NAME 替换为要隔离的 Pod 的名称。

  3. 排空不带 quarantine 标签的 Pod 的节点:

    kubectl drain NODE_NAME --pod-selector='!quarantine'
    

限制对节点的网络访问

我们建议阻止内部和外部流量访问主机虚拟机。接下来,允许来自网络或 VPC 上的特定虚拟机到被隔离虚拟机的入站连接。

第一步是从拥有虚拟机的托管实例组中放弃该虚拟机。 放弃虚拟机会导致该节点无法被标记为运行状况不佳,也无法在您的调查完成之前自动修复(重新创建)。

如需放弃该虚拟机,请运行以下命令:

gcloud compute instance-groups managed abandon-instances INSTANCE_GROUP_NAME \
    --instances=NODE_NAME

用防火墙隔离虚拟机

在受影响的容器与同一网络中的其他工作负载之间创建防火墙,这种做法有助于防止攻击者在您执行进一步分析时转移到您环境中的其他部分。由于您已经排空了其他容器的虚拟机,因此此举只会影响被隔离的容器。

下文中有关使用防火墙隔离虚拟机的说明可防止以下操作:

  • 使用出站规则新建到集群中其他虚拟机的出站连接。
  • 使用入站规则建立到遭遇入侵的虚拟机的入站连接。

如需使用防火墙将虚拟机与其他实例隔离开来,请对托管您希望隔离的 Pod 的节点执行以下步骤:

  1. 为实例添加标记,以便应用新的防火墙规则。

    gcloud compute instances add-tags NODE_NAME \
        --zone COMPUTE_ZONE \
        --tags quarantine
    
  2. 创建防火墙规则以拒绝来自具有 quarantine 标记的实例的所有出站 TCP 流量:

    gcloud compute firewall-rules create quarantine-egress-deny \
        --network NETWORK_NAME \
        --action deny \
        --direction egress \
        --rules tcp \
        --destination-ranges 0.0.0.0/0 \
        --priority 0 \
        --target-tags quarantine
    
  3. 创建防火墙规则以拒绝发往具有 quarantine 标记的实例的所有入站 TCP 流量。对于此入站规则,将 priority 指定为 1,这让您可以使用允许来自指定虚拟机的 SSH 的其他规则替换该规则。

    gcloud compute firewall-rules create quarantine-ingress-deny \
        --network NETWORK_NAME \
        --action deny \
        --direction ingress \
        --rules tcp \
        --source-ranges 0.0.0.0/0 \
        --priority 1 \
        --target-tags quarantine
    

移除虚拟机的外部 IP 地址

移除虚拟机的外部 IP 地址会中断您的 VPC 之外的任何现有网络连接。

如需移除虚拟机的外部地址,请执行以下步骤:

  1. 查找并删除将外部 IP 地址与虚拟机相关联的访问配置。首先通过描述虚拟机找到访问配置:

    gcloud compute instances describe NODE_NAME \
        --zone COMPUTE_ZONE --format="flattened([networkInterfaces])"
    

    查找包含 namenatIP 的行。这两行代码如下所示:

    networkInterfaces[0].accessConfigs[0].name:              ACCESS_CONFIG_NAME
    networkInterfaces[0].accessConfigs[0].natIP:             EXTERNAL_IP_ADDRESS
    
  2. 找到与您要移除的外部 IP 地址匹配的 natIP 值。请注意访问配置的名称。

  3. 如需移除外部 IP 地址,请运行以下命令:

    gcloud compute instances delete-access-config NODE_NAME \
        --access-config-name "ACCESS_CONFIG_NAME"
    

通过中间虚拟机使用 SSH 连接到主机虚拟机

移除主机虚拟机的外部 IP 地址后,您无法再从 VPC 外部通过 SSH 连接到它。但可以通过相同网络中的其他虚拟机访问该虚拟机。在本节的其余部分中,我们将其他这些虚拟机称为中间虚拟机

前提条件

  • 可访问主机虚拟机子网的中间虚拟机。如果您还没有可用于此用途的虚拟机,请创建一个相应的虚拟机
  • 中间虚拟机的内部 IP 地址。
  • 来自中间虚拟机的 SSH 公钥。如需了解详情,请参阅管理 SSH 密钥

连接到主机虚拟机

  1. 将中间虚拟机的公钥添加到主机虚拟机。如需了解详情,请参阅 Compute Engine 文档中的添加和移除 SSH 密钥
  2. 为中间虚拟机添加标记。

    gcloud compute instances add-tags INTERMEDIATE_NODE_NAME \
      --zone COMPUTE_ZONE \
      --tags intermediate
    
  3. 添加入站流量允许规则,替换您先前添加的拒绝规则。如需添加规则,请运行以下命令。

    gcloud compute firewall-rules create quarantine-ingress-allow \
        --network NETWORK_NAME \
        --action allow \
        --direction ingress \
        --rules tcp:22 \
        --source-tags intermediate \
        --priority 0 \
        --target-tags quarantine
    

    此规则允许端口 22 (SSH) 上来自网络内带有 intermediate 标记的虚拟机的传入流量。该参数会使用设置为 0priority 替换拒绝规则。

  4. 使用内部 IP 地址连接到被隔离的虚拟机:

    ssh -i KEY_PATH USER@QUARANTINED_VM_INTERNAL_IP
    

    请替换以下内容:

    • KEY_PATH:SSH 私钥的路径。
    • USER:您的 Google Cloud 帐号的电子邮件地址。
    • QUARANTINED_VM_INTERNAL_IP:内部 IP 地址。

重新部署容器

通过重新部署容器,您可以从容器的全新副本开始,并删除已经遭到入侵的容器。

您通过删除托管容器的 Pod 来重新部署容器。如果 Pod 由更高级别的 Kubernetes 构造(例如,Deployment 或 DaemonSet)进行管理,删除 Pod 会安排新 Pod。此 Pod 会运行新的容器。

在以下情况下,重新部署是合理做法:

  • 您已经知道出现此漏洞的原因。
  • 您认为攻击者需要投入大量精力才能再次入侵您的容器。

如需在 Kubernetes 中重新部署容器,请删除包含该容器的 Pod:

kubectl delete pods POD_NAME --grace-period=10

如果已删除的 Pod 中的容器仍在继续运行,您可以删除工作负载

删除工作负载

删除诸如 Deployment 或 DaemonSet 之类的工作负载会导致其全部成员 Pod 被删除。这些 Pod 中的所有容器都会停止运行。在以下情况下,删除工作负载可能是合理做法:

  • 您想要阻止正在进行的攻击。
  • 您愿意将工作负载转为离线。
  • 立即阻止攻击比应用正常运行时间或取证分析更为重要。

若要删除工作负载,请使用 kubectl delete CONTROLLER_TYPE。 例如,若要删除一个 Deployment 工作负载,请运行以下命令:

kubectl delete deployments DEPLOYMENT

如果删除工作负载并未删除所有关联的 Pod 或容器,您可以使用容器运行时环境的 CLI 工具(通常为 docker)手动删除容器。如果您的节点运行 containerd,请使用 crictl

Docker

若要使用 Docker 容器运行时停止容器,您可以使用 docker stopdocker kill

docker stop 通过向根进程发送 SIGTERM 信号来停止容器,并且默认会等待 10 秒钟,让进程退出。如果进程在该时间段内未退出,则在此时间段结束后会发送 SIGKILL 信号。 您可以使用 --time 选项指定此宽限期。

docker stop --time TIME_IN_SECONDS CONTAINER

docker kill 是停止容器的最快方法。它会立即发送 SIGKILL 信号。

docker kill CONTAINER

您还可以通过 docker rm -f 在一条命令中停止并移除容器。

docker rm -f CONTAINER

containerd

如果您在 GKE 中使用 containerd 运行时,请通过 crictl 停止或移除容器。

如需在 containerd 中停止容器,请运行以下命令:

crictl stop CONTAINER

如需在 containerd 中移除容器,请运行以下命令:

crictl rm -f CONTAINER

删除主机虚拟机

如果您无法删除或移除容器,可以直接删除用于托管受影响容器的虚拟机。

如果 Pod 仍然可见,您可以使用以下命令找到主机虚拟机的名称:

kubectl get pods --all-namespaces \
  -o=custom-columns=POD_NAME:.metadata.name,INSTANCE_NAME:.spec.nodeName \
  --field-selector=metadata.name=POD_NAME

若要删除主机虚拟机,请运行以下 gcloud 命令:

gcloud compute instance-groups managed delete-instances INSTANCE_GROUP_NAME \
    --instances=NODE_NAME

从托管实例组中放弃实例可以将组减小相当于一个虚拟机的大小。您可以使用以下命令,手动将一个实例添加回该组:

gcloud compute instance-groups managed resize INSTANCE_GROUP_NAME \
    --size=SIZE

后续步骤