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 seu cluster configurando o GKE no Azure para programar suas 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 no Azure usam cargas de trabalho privilegiadas que gerenciamos para ativar funcionalidades e recursos específicos do cluster, como 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 junto com cargas de trabalho de sistema privilegiados significa que um invasor que sai de um contêiner comprometido pode usar as credenciais da carga de trabalho privilegiada no nó para transferir privilégios no seu cluster.

Como evitar rompimentos de contêiner

Sua defesa principal deve ser os aplicativos. O GKE no Azure tem vários recursos que podem ser usados para aumentar a proteção dos seus clusters e pods. Na maioria dos casos, é altamente recomendável usar o Policy Controller e os recursos de segurança do kernel para aumentar a proteção das suas 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ós para programar as cargas de trabalho em um pool de nós dedicado.

Um taint de nó informa ao GKE no Azure para evitar a programação de cargas de trabalho sem uma tolerância correspondente (como o GKE em cargas de trabalho gerenciadas pelo Azure) nesses nós. A afinidade do nó em suas próprias cargas de trabalho instrui o GKE no Azure 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 suas cargas de trabalho pode afetar a eficiência de custos, 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 do GKE no Azure 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 no Azure removerá todos os pods em execução nos nós que não tiverem uma tolerância para o novo taint.

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

gcloud container azure 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 no Azure.
  • 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ê rotulou o pool de nós dedicado, também é possível adicionar uma regra de afinidade de nó para instruir o GKE no Azure a programar apenas suas 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 no Azure 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:

  • Adicione o taint components.gke.io/gke-managed-components para restringir pools de nós específicos ao GKE em cargas de trabalho gerenciadas pelo Azure. 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 do GKE gerenciadas pelo Azure seja executada nesses nós adicionando seu próprio taint a esses pools.
  • 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