Como mitigar incidentes de segurança


Este documento descreve mitigações e respostas comuns a possíveis incidentes de segurança nos seus clusters e contêineres do Google Kubernetes Engine (GKE).

As sugestões em Como melhorar a segurança do cluster podem melhorar a segurança das cargas de trabalho do GKE. Os incidentes de segurança, no entanto, podem ocorrer mesmo quando há medidas para proteger suas cargas de trabalho.

Como detectar incidentes

Para detectar possíveis incidentes, recomendamos que você configure um processo que colete e monitore os registros da carga de trabalho. Em seguida, configure alertas com base em eventos anormais detectados nos registros. Os alertas notificam sua equipe de segurança quando algo incomum é detectado. Sua equipe de segurança pode analisar o possível incidente.

Como gerar alertas a partir de registros

É possível personalizar alertas com base em métricas ou ações específicas. Por exemplo, alertar sobre o alto uso de CPU nos nós do GKE pode indicar que eles estão comprometidos para criptomineração.

Os alertas devem ser gerados quando você agrega seus registros e métricas. Por exemplo, é possível usar a geração de registros de auditoria do GKE em combinação com alertas baseados em registros no Cloud Logging.

Para saber mais sobre consultas relevantes para a segurança, consulte a documentação Geração de registros de auditoria.

Como responder a um incidente de segurança

Depois de ser alertado sobre um incidente, tome as medidas necessárias. Corrija a vulnerabilidade, se puder. Se você não souber a causa da vulnerabilidade ou não tiver uma correção pronta, aplique mitigações.

As mitigações que você pode tomar dependem da gravidade do incidente e da certeza de que você identificou o problema.

Este guia aborda ações que você pode realizar depois de detectar um incidente em uma carga de trabalho em execução no GKE. Você pode, em ordem crescente de gravidade:

Essas mitigações são descritas nas seções a seguir.

Antes de começar

Os métodos usados neste tópico usam as seguintes informações:

  • O nome dos pods que você acredita terem sido comprometidos, ou POD_NAME.
  • O nome da VM do host que está executando o contêiner ou Pods, ou NODE_NAME.

Além disso, antes de tomar qualquer uma das ações, considere se haverá uma reação negativa do invasor caso ele seja descoberto. O invasor pode decidir excluir dados ou destruir cargas de trabalho. Se o risco for muito alto, considere mitigações mais drásticas, como a exclusão de uma carga de trabalho, antes de realizar uma investigação mais detalhada.

Instantâneo do disco da VM

A criação de um instantâneo do disco da VM permite a investigação forense depois que a carga de trabalho tiver sido reimplantada ou excluída. Os instantâneos podem ser criados enquanto os discos estiverem anexados às instâncias em execução.

  1. Para criar um snapshot do seu disco permanente, primeiro encontre os discos anexados à sua VM. Execute o seguinte comando e observe o campo source:

    gcloud compute instances describe NODE_NAME --zone COMPUTE_ZONE \
        --format="flattened([disks])"
    
  2. Procure as linhas que contêm disks[NUMBER].source. A resposta será parecida com esta:

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

    O nome do disco é a parte do nome da origem após a barra final. Por exemplo, o nome do disco é gke-cluster-pool-1-abcdeffff-zgt8.

  3. Para concluir o snapshot, execute o seguinte comando:

    gcloud compute disks snapshot DISK_NAME
    

Para mais informações, consulte Como criar snapshots de discos permanentes na documentação do Compute Engine.

Inspecionar a VM enquanto a carga de trabalho continua a ser executada

Além disso, considere qual acesso um invasor pode ter antes de agir. Se você suspeitar que um contêiner foi comprometido e estiver preocupado em informar o invasor, poderá se conectar ao contêiner e inspecioná-lo. Inspecionar é útil para uma investigação rápida antes de realizar ações mais disruptivas. A inspeção também é a abordagem menos disruptiva para a carga de trabalho, mas não interrompe o incidente.

Como alternativa, para evitar o login em uma máquina com uma credencial privilegiada, você pode analisar suas cargas de trabalho configurando a perícia em tempo real (como a Resposta rápida de GRR), agentes no nó ou filtragem de rede.

Reduzir o acesso antes de inspecionar a VM ativa

Ao restringir, drenar e limitar o acesso à VM hospedada em um contêiner comprometido, você pode isolar parcialmente o contêiner comprometido do restante do seu cluster. Limitar o acesso à VM reduz o risco, mas não impede que um invasor se mova lateralmente em seu ambiente se ele aproveitar uma vulnerabilidade crítica.

Restringir o nó e drenar as outras cargas de trabalho dele

Restringir e drenar um nó move as cargas de trabalho localizadas junto com o contêiner comprometido para outras VMs no cluster. Restringir e drenar reduz a capacidade do impacto de um invasor em outras cargas de trabalho no mesmo nó. Isso não necessariamente impede que eles inspecionem o estado persistente de uma carga de trabalho (por exemplo, inspecionando o conteúdo da imagem do contêiner).

  1. Use kubectl para restringir o nó e garantir que nenhum outro pod esteja programado nele:

    kubectl cordon NODE_NAME
    

    Depois de restringir o nó, drene-o de outros pods.

  2. Marque o pod que está colocando em quarentena:

    kubectl label pods POD_NAME quarantine=true
    

    Substitua POD_NAME pelo nome do pod que você quer colocar em quarentena.

  3. Drene o nó dos pods que não estão rotulados com quarantine:

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

Restringir o acesso da rede ao nó

Recomendamos impedir o tráfego interno e externo de acessar a VM do host. Em seguida, permita que as conexões de entrada de uma VM específica em sua rede ou em uma VPC se conecte à VM em quarentena.

A primeira etapa é abandonar a VM do Grupo de instâncias gerenciadas que a contém. Abandonar a VM evita que o nó seja marcado como não íntegro e seja reparado automaticamente (recriado) antes que sua investigação seja concluída.

Para abandonar a VM, execute o seguinte comando:

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

Coloque um Firewall na VM

Criar um firewall entre o contêiner afetado e outras cargas de trabalho na mesma rede ajuda a impedir que um invasor se mova para outras partes do seu ambiente enquanto você realiza uma análise mais aprofundada. Como você já esvaziou a VM de outros contêineres, isso afetará apenas o contêiner em quarentena.

As seguintes instruções sobre o firewall da VM impedem:

  • Novas conexões de saída para outras VMs no cluster usando uma regra de saída.
  • Conexões de entrada para a VM comprometida usando uma regra de entrada.

Para proteger a VM das outras instâncias, siga estas etapas para o nó que hospeda o Pod que você quer colocar em quarentena:

  1. Coloque uma tag na instância para aplicar uma nova regra de firewall.

    gcloud compute instances add-tags NODE_NAME \
        --zone COMPUTE_ZONE \
        --tags quarantine
    
  2. Crie uma regra de firewall para negar todo o tráfego TCP de saída de instâncias com a tag quarantine:

    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. Crie uma regra de firewall para negar todo o tráfego TCP de entrada a instâncias com a tag quarantine. Conceda a esta regra de entrada uma priority de 1, o que permite substituí-la por outra regra que permita SSH de uma VM especificada.

    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
    

Remover o endereço IP externo da VM

A remoção do endereço IP externo da VM quebra todas as conexões de rede existentes fora da sua VPC.

Para remover o endereço externo de uma VM, execute as etapas a seguir:

  1. Encontre e exclua a configuração de acesso que associa o IP externo à VM. Primeiro, encontre a configuração de acesso descrevendo a VM:

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

    Procure as linhas que contêm name e natIP. Elas são semelhantes a isto:

    networkInterfaces[0].accessConfigs[0].name:              ACCESS_CONFIG_NAME
    networkInterfaces[0].accessConfigs[0].natIP:             EXTERNAL_IP_ADDRESS
    
  2. Encontre o valor de natIP que corresponde ao IP externo que você quer remover. Anote o nome da configuração de acesso.

  3. Para remover o IP externo, execute o seguinte comando:

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

SSH para a VM do host por meio de uma VM intermediária

Depois de remover o IP externo da VM do host, você não poderá fazer ssh de fora da sua VPC. Você o acessa de outra VM na mesma rede. Para o restante desta seção, chamamos isso de VM intermediária.

Pré-requisitos

  • Uma VM intermediária com acesso à sub-rede da VM do host. Se você ainda não tiver, crie uma VM para essa finalidade.
  • O endereço IP interno da VM intermediária.
  • Uma chave pública SSH da VM intermediária. Para saber mais, consulte Gerenciar chaves SSH

Como se conectar à VM do host

  1. Adicione a chave pública da VM intermediária à VM do host. Para obter mais informações, consulte Como adicionar e remover chaves SSH na documentação do Compute Engine.
  2. Adicione uma tag à VM intermediária.

    gcloud compute instances add-tags INTERMEDIATE_NODE_NAME \
      --zone COMPUTE_ZONE \
      --tags intermediate
    
  3. Adicione uma regra de permissão de entrada para substituir a regra de proibição que você adicionou anteriormente. Para adicionar a regra, execute o seguinte comando.

    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
    

    Essa regra permite o tráfego de entrada na porta 22 (SSH) das VMs na sua rede com a tag intermediate. Ela substitui a regra de negação por uma priority de 0.

  4. Conecte-se à VM em quarentena usando seu IP interno:

    ssh -i KEY_PATH USER@QUARANTINED_VM_INTERNAL_IP
    

    Substitua:

    • KEY_PATH: o caminho para sua chave privada SSH.
    • USER: o endereço de e-mail da sua conta do Google Cloud.
    • QUARANTINED_VM_INTERNAL_IP: o endereço IP interno.

Reimplantar um contêiner

Ao reimplantar seu contêiner, você inicia uma nova cópia do contêiner e exclui o contêiner comprometido.

Você reimplanta um contêiner excluindo o Pod que o hospeda. Se o Pod for gerenciado por uma construção de nível superior do Kubernetes (por exemplo, uma implantação ou DaemonSet), a exclusão do Pod programará um novo Pod. Este Pod executa novos contêineres.

A reimplantação faz sentido quando:

  • Você já sabe a causa da vulnerabilidade.
  • Você acha que é necessário um esforço ou tempo significativo para o invasor comprometer seu contêiner novamente.
  • Você acha que o contêiner pode ser rapidamente comprometido novamente e não quer ficar off-line. Por isso, você planeja colocá-lo em um sandbox para limitar o impacto.

Ao reimplantar a carga de trabalho, se a possibilidade de outro compromisso for alto, coloque a carga em um ambiente sandbox, como o GKE Sandbox. O sandbox limitará o acesso ao kernel do nó do host se o invasor comprometer o contêiner novamente.

Para reimplantar um contêiner no Kubernetes, exclua o Pod que o contém:

kubectl delete pods POD_NAME --grace-period=10

Se os contêineres no Pod excluído continuarem a ser executados, você poderá excluir a carga de trabalho.

Para reimplantar o contêiner em um sandbox, siga as instruções em Aumentar a proteção de isolamento de carga de trabalho com o GKE Sandbox.

Excluir uma carga de trabalho

Excluir uma carga de trabalho, como uma implantação ou DaemonSet, faz com que todos os Pods de membros sejam excluídos. Todos os contêineres dentro desses Pods deixam de ser executados. A exclusão de uma carga de trabalho pode fazer sentido quando:

  • Você quer interromper um ataque em andamento.
  • Você está disposto a deixar a carga de trabalho off-line.
  • Interromper imediatamente o ataque é mais importante do que o tempo de atividade do aplicativo ou a análise forense.

Para excluir uma carga de trabalho, use kubectl delete CONTROLLER_TYPE. Por exemplo, para excluir uma implantação, execute o seguinte comando:

kubectl delete deployments DEPLOYMENT

Se a exclusão da carga de trabalho não excluir todos os Pods ou contêineres associados, você poderá excluir manualmente os contêineres usando a ferramenta CLI do contêiner, geralmente docker. Se os nós executarem containerd, use crictl.

Docker

Para interromper um contêiner usando o ambiente de execução de contêiner do Docker, é possível usar docker stop ou docker kill.

docker stop interrompe o contêiner enviando um sinal SIGTERM para o processo raiz e aguarda 10 segundos para que o processo saia por padrão. Se o processo não tiver sido encerrado nesse período, ele enviará um sinal SIGKILL. É possível especificar esse período de carência com a opção --time.

docker stop --time TIME_IN_SECONDS CONTAINER

docker kill é o método mais rápido para interromper um contêiner. Ele envia o sinal SIGKILL imediatamente.

docker kill CONTAINER

Também é possível interromper e remover um contêiner em um comando com docker rm -f:

docker rm -f CONTAINER

containerd

Se você usar o ambiente de execução containerd no GKE, interrompa ou remova contêineres com crictl.

Para interromper um contêiner em containerd, execute o seguinte comando:

crictl stop CONTAINER

Para remover um contêiner em containerd, execute o seguinte comando:

crictl rm -f CONTAINER

Como excluir a VM do host

Se não for possível excluir ou remover o contêiner, você poderá excluir a máquina virtual que hospeda o contêiner afetado.

Se o Pod ainda estiver visível, você poderá encontrar o nome da VM do host com o seguinte comando:

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

Para excluir a VM do host, execute o seguinte comando gcloud:

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

Abandonar a instância do grupo de instâncias gerenciadas reduz uma VM do tamanho do grupo. É possível adicionar manualmente uma instância de volta ao grupo com o seguinte comando:

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

A seguir