Isolar cargas de trabalho em pools de nós dedicados

Nesta página, mostramos como reduzir o risco de ataques de escalonamento de privilégios no cluster configurando o GKE na AWS para programar as cargas de trabalho em um pool de nós separado e dedicado, longe de cargas de trabalho gerenciadas com privilégios.

Visão geral

Os clusters do GKE na AWS usam cargas de trabalho com privilégios que gerenciamos para ativar funcionalidades e recursos específicos do cluster, como a coleta de métricas. Essas cargas de trabalho recebem permissões especiais para serem executadas corretamente no cluster.

As cargas de trabalho que você implanta nos nós podem ser comprometidas por uma entidade maliciosa. A execução dessas cargas de trabalho com as cargas de trabalho privilegiadas do sistema significa que um invasor que viola um contêiner comprometido pode usar as credenciais da carga de trabalho privilegiada no nó para escalonar privilégios no cluster.

Como evitar rompimentos de contêiner

Sua defesa principal deve ser os aplicativos. O GKE na AWS tem vários recursos que podem ser usados para aumentar a proteção dos clusters e pods. Na maioria dos casos, é altamente recomendável usar o Controlador de Políticas e os recursos de segurança do kernel para aumentar a proteção das cargas de trabalho. Para mais recomendações de segurança, consulte a Visão geral de segurança.

Como evitar ataques de escalonamento de privilégios

Se você quiser uma camada extra de isolamento, além de outras medidas de aumento da proteção, use taints de nó e afinidade de nó para programar cargas de trabalho em um pool de nós dedicado.

Um taint de nó informa ao GKE na AWS que ele deve evitar a programação de cargas de trabalho sem uma tolerância correspondente (como as cargas de trabalho gerenciadas pelo GKE na AWS) nesses nós. A afinidade do nó em suas próprias cargas de trabalho instrui o GKE na AWS a programar seus pods nos nós dedicados.

Limitações do isolamento de nós

  • Os invasores ainda podem iniciar ataques de negação de serviço (DoS) no nó comprometido.
  • Os nós comprometidos ainda podem ler muitos recursos, incluindo todos os pods e namespaces no cluster.
  • Os nós comprometidos podem acessar Secrets e credenciais usadas por todos os pods em execução nesse nó.
  • O uso de um pool de nós separado para isolar as cargas de trabalho pode afetar o custo, o escalonamento automático e a utilização de recursos.
  • Os nós comprometidos ainda podem ignorar as políticas de saída de rede.
  • Algumas cargas de trabalho gerenciadas pelo GKE na AWS precisam ser executadas em todos os nós do cluster e são configuradas para tolerar todos os taints.
  • Se você implantar DaemonSets que tenham permissões elevadas e possam tolerar qualquer taint, esses pods poderão ser um caminho para o escalonamento de privilégios de um nó comprometido.

Como funciona o isolamento de nós

Para implementar o isolamento de nós para as cargas de trabalho, faça isto:

  1. Atribua um taint e um identificador a um pool de nós para as cargas de trabalho.
  2. Atualize as cargas de trabalho com a tolerância e a regra de afinidade de nó correspondente.

Este guia pressupõe que você começará com um pool de nós no cluster. O uso da afinidade de nó, além dos taints de nó, não é obrigatório, mas isso é recomendável porque você se beneficia de um maior controle sobre a programação.

Antes de começar

Para executar as etapas desta página, primeiro conclua o seguinte:

Atribuir um taint e um identificador a um pool de nós para as cargas de trabalho

Crie um novo pool de nós para as cargas de trabalho e atribua um taint e um identificador de nó. Quando você atribui um taint ou um identificador no nível do pool de nós, todos os novos nós, como aqueles criados pelo escalonamento automático, recebem automaticamente os taints e identificadores especificados.

Também é possível adicionar taints e identificadores de nós a pools de nós que já existem. Se você usar o efeito NoExecute, o GKE na AWS removerá todos os pods em execução nesses nós que não tiverem uma tolerância ao novo taint.

Para adicionar um taint e um identificador a um novo pool de nós, execute o seguinte comando:

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

Substitua:

  • POOL_NAME: o nome do novo pool de nós para as cargas de trabalho.
  • CLUSTER_NAME: o nome do cluster do GKE na AWS.
  • TAINT_KEY=TAINT_VALUE: um par de chave-valor associado a um TAINT_EFFECT de programação. Por exemplo, workloadType=untrusted.
  • TAINT_EFFECT: um dos seguintes valores de efeito: NoSchedule, PreferNoSchedule ou NoExecute. NoExecute oferece uma garantia de remoção melhor do que NoSchedule.
  • LABEL_KEY=LABEL_VALUE: pares de chave-valor para os rótulos de nó, que correspondem aos seletores especificados nos manifestos das cargas de trabalhos.

Adicionar uma tolerância e uma regra de afinidade de nó às cargas de trabalho

Depois que você atribui um taint ao pool de nós dedicado, nenhuma carga de trabalho poderá ser programada nele, a menos que tenha uma tolerância correspondente ao taint adicionado. Adicione a tolerância à especificação das cargas de trabalho para permitir que esses pods sejam programados no pool de nós com taint.

Se você atribuiu um identificador ao pool de nós dedicado, também é possível adicionar uma regra de afinidade de nó para instruir o GKE na AWS a programar apenas as cargas de trabalho nesse pool de nós.

O exemplo a seguir adiciona uma tolerância ao taint workloadType=untrusted:NoExecute e uma regra de afinidade de nó para o identificador de nó 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"]

Substitua:

  • TAINT_KEY: a chave do taint que você aplicou ao pool de nós dedicado.
  • TAINT_VALUE: o valor do taint que você aplicou ao pool de nós dedicado.
  • LABEL_KEY: a chave do rótulo do nó que você aplicou ao pool de nós dedicado.
  • LABEL_VALUE: o valor do rótulo do nó que você aplicou ao pool de nós dedicado.

Quando você atualiza sua implantação com kubectl apply, o GKE na AWS recria os pods afetados. A regra de afinidade de nó força os pods no pool de nós dedicado que você criou. A tolerância permite que apenas esses pods sejam posicionados nos nós.

Verificar se a separação funciona

Para verificar se a programação funciona corretamente, execute o comando a seguir e verifique se as cargas de trabalho estão no pool de nós dedicado:

kubectl get pods -o=wide

Conselhos e práticas recomendadas

Após configurar o isolamento de nós, recomendamos que você faça isto:

  • Restrinja pools de nós específicos apenas para cargas de trabalho gerenciadas pelo GKE na AWS adicionando o taint components.gke.io/gke-managed-components. A adição desse taint impede que seus próprios pods sejam programados nesses nós, melhorando o isolamento.
  • Ao criar novos pools de nós, impeça que a maioria das cargas de trabalho gerenciadas pelo GKE na AWS seja executada nesses nós adicionando seu próprio taint a esses pools de nós.
  • Sempre que você implantar novas cargas de trabalho no cluster, como ao instalar ferramentas de terceiros, faça a auditoria das permissões exigidas pelos pods. Sempre que possível, evite implantar cargas de trabalho que usam permissões elevadas para nós compartilhados.

A seguir