Isolare i carichi di lavoro in pool di nodi dedicati

Questa pagina mostra come isolare i carichi di lavoro dei container su pool di nodi dedicati in Google Distributed Cloud (GDC) air-gapped per avere un maggiore controllo dei pod. L'isolamento del workload offre alcuni vantaggi, ad esempio:

  • Rischio ridotto di attacchi di escalation dei privilegi nel cluster Kubernetes.
  • Maggiore controllo sui pod che richiedono risorse aggiuntive.

In questi casi, valuta la possibilità di isolare i carichi di lavoro dei container per un maggiore controllo e ottimizzazione.

Perché dovrei isolare i miei carichi di lavoro?

L'isolamento dei workload su node pool dedicati non è obbligatorio, ma può essere un'azione prudente da intraprendere per evitare potenziali problemi. Tieni presente, tuttavia, che la gestione dei node pool dedicati richiede una maggiore supervisione ed è spesso inutile.

I cluster Kubernetes utilizzano carichi di lavoro con privilegi gestiti da GDC per abilitare funzionalità specifiche del cluster, come la raccolta delle metriche. A questi carichi di lavoro vengono concesse autorizzazioni speciali per essere eseguiti correttamente nel cluster.

I carichi di lavoro di cui esegui il deployment sui nodi potrebbero essere compromessi da un'entità dannosa. L'esecuzione di questi carichi di lavoro insieme a carichi di lavoro con privilegi gestiti da GDC significa che un malintenzionato che esce da un container compromesso può utilizzare le credenziali del carico di lavoro con privilegi sul nodo per aumentare i privilegi nel cluster.

I pool di nodi dedicati sono utili anche quando devi pianificare pod che richiedono più risorse rispetto ad altri, ad esempio più memoria o più spazio su disco locale.

Puoi utilizzare i seguenti meccanismi per pianificare i tuoi workload su un pool di nodi dedicato:

Un'incompatibilità del nodo indica al cluster Kubernetes di evitare di pianificare carichi di lavoro senza una tolleranza corrispondente, come i carichi di lavoro gestiti da GDC, su questi nodi. L'affinità dei nodi sui tuoi carichi di lavoro indica al cluster di pianificare i pod sui nodi dedicati.

Limitazioni dell'isolamento dei nodi

  • Gli autori degli attacchi possono comunque avviare attacchi denial of service (DoS) dal nodo compromesso.

  • I nodi compromessi possono comunque leggere molte risorse, inclusi tutti i pod e gli spazi dei nomi nel cluster.

  • I nodi compromessi possono accedere a secret e credenziali utilizzati da ogni pod in esecuzione su quel nodo.

  • L'utilizzo di un pool di nodi separato per isolare i workload può influire sull'efficienza dei costi, sulla scalabilità automatica e sull'utilizzo delle risorse.

  • I nodi compromessi possono comunque ignorare i criteri di rete in uscita.

  • Alcuni carichi di lavoro gestiti da GDC devono essere eseguiti su ogni nodo del cluster e sono configurati per tollerare tutte le incompatibilità.

  • Se esegui il deployment di risorse DaemonSet con privilegi elevati e che possono tollerare qualsiasi taint, questi pod potrebbero essere un percorso per l'escalation dei privilegi da un nodo compromesso.

Come funziona l'isolamento dei nodi

Per implementare l'isolamento dei nodi per i tuoi carichi di lavoro, devi:

  1. Applica taint ed etichette a un pool di nodi per i tuoi carichi di lavoro.

  2. Aggiorna i tuoi carichi di lavoro con la regola di tolleranza e affinità del nodo corrispondente.

Questa guida presuppone che tu inizi con un pool di nodi nel cluster. L'utilizzo dell'affinità dei nodi in aggiunta ai taint dei nodi non è obbligatorio, ma lo consigliamo perché ti consente di avere un maggiore controllo sulla pianificazione.

Prima di iniziare

Prima di iniziare, assicurati di aver eseguito le seguenti operazioni:

  • Scegli un nome specifico per il taint del nodo e l'etichetta del nodo che vuoi utilizzare per i node pool dedicati. Ad esempio, workloadType=untrusted.

  • Se necessario, chiedi all'amministratore IAM dell'organizzazione di concederti il ruolo Sviluppatore cluster utente (user-cluster-developer), che non è associato a uno spazio dei nomi.

Contaminare ed etichettare un nuovo pool di nodi

Quando applichi un taint o un'etichetta a un nuovo pool di nodi, tutti i nodi, inclusi quelli aggiunti in un secondo momento, riceveranno automaticamente i taint e le etichette specificati.

Per aggiungere un taint e un'etichetta a un nuovo pool di nodi:

  1. Modifica la sezione nodePools della risorsa personalizzata Cluster direttamente durante la creazione del node pool:

    nodePools:
      ...
      - machineTypeName: n2-standard-2-gdc
        name: nodepool-1
        nodeCount: 3
        taints: TAINT_KEY=TAINT_VALUE:TAINT_EFFECT
        labels: LABEL_KEY=LABEL_VALUE
    

    Sostituisci quanto segue:

    • TAINT_KEY=TAINT_VALUE: una coppia chiave-valore associata a una pianificazione TAINT_EFFECT. Ad esempio, workloadType=untrusted.
    • TAINT_EFFECT: uno dei seguenti valori di effetto:
      • NoSchedule: i pod che non tollerano questa incompatibilità non vengono pianificati sul nodo; i pod esistenti non vengono rimossi dal nodo.
      • PreferNoSchedule: Kubernetes evita di pianificare pod che non tollerano questa incompatibilità sul nodo.
      • NoExecute: il pod viene rimosso dal nodo se è già in esecuzione sul nodo, mentre non viene pianificato sul nodo se non è ancora in esecuzione sul nodo stesso.
    • LABEL_KEY=LABEL_VALUE: le coppie chiave-valore per le etichette dei nodi, che corrispondono ai selettori specificati nei manifest dei carichi di lavoro.
  2. Applica la risorsa Cluster per creare il nuovo pool di nodi:

    kubectl apply -f cluster.yaml \
        --kubeconfig MANAGEMENT_API_SERVER
    

    Sostituisci MANAGEMENT_API_SERVER con il percorso kubeconfig del server API zonale in cui è ospitato il cluster Kubernetes. Se non hai ancora generato un file kubeconfig per il server API nella zona di destinazione, consulta Accedi per maggiori dettagli.

Contaminare ed etichettare un pool di nodi esistente

Per applicare un taint o un'etichetta a un pool di nodi esistente, devi applicare le modifiche a ogni nodo esistente. Non puoi aggiornare dinamicamente le configurazioni pool di nodi.

Per aggiungere un taint e un'etichetta a un pool di nodi esistente:

  1. Elenca i nodi nel pool di nodi dedicato:

    kubectl get node --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG \
        -l baremetal.cluster.gke.io/node-pool=NODE_POOL_NAME
    

    Sostituisci le seguenti variabili:

    • KUBERNETES_CLUSTER_KUBECONFIG: il percorso kubeconfig per il cluster Kubernetes.
    • NODE_POOL_NAME: il nome del tuo pool di nodi dedicato.

    Prendi nota di ogni ID nodo di tutti i nodi nel pool di nodi dall'output.

  2. Per ogni nodo nel pool di nodi, applica le incompatibilità:

    kubectl taint nodes NODE_ID \
        TAINT_KEY=TAINT_VALUE:TAINT_EFFECT \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    Sostituisci le seguenti variabili:

    • NODE_ID: l'ID del nodo worker nel pool di nodi dedicato.
    • TAINT_KEY=TAINT_VALUE: una coppia chiave-valore associata a una pianificazione TAINT_EFFECT. Ad esempio workloadType=untrusted.
    • TAINT_EFFECT: uno dei seguenti valori dell'effetto:
      • NoSchedule: i pod che non tollerano questa incompatibilità non vengono pianificati sul nodo; i pod esistenti non vengono rimossi dal nodo.
      • PreferNoSchedule: Kubernetes evita di pianificare pod che non tollerano questa incompatibilità sul nodo.
      • NoExecute: il pod viene rimosso dal nodo se è già in esecuzione sul nodo, mentre non viene pianificato sul nodo se non è ancora in esecuzione sul nodo stesso.
    • KUBERNETES_CLUSTER_KUBECONFIG: il percorso kubeconfig per il cluster Kubernetes.
  3. Per ogni nodo nel pool di nodi, applica le etichette corrispondenti ai selettori che definirai nei tuoi carichi di lavoro dei container:

    kubectl label NODE_ID \
        LABEL_KEY:LABEL_VALUE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    Sostituisci le seguenti variabili:

    • NODE_ID: l'ID del nodo worker nel pool di nodi dedicato.
    • LABEL_KEY:LABEL_VALUE: le coppie chiave-valore per le etichette dei nodi, che corrispondono ai selettori specificati nei manifest dei workload.
    • KUBERNETES_CLUSTER_KUBECONFIG: il percorso kubeconfig per il cluster Kubernetes.

Aggiungere una tolleranza e una regola di affinità dei nodi

Dopo aver applicato l'incompatibilità al pool di nodi dedicato, nessun workload può essere pianificato al suo interno a meno che non abbia una tolleranza corrispondente all'incompatibilità che hai aggiunto. Aggiungi la tolleranza alla specifica dei tuoi workload per consentire la pianificazione di questi pod nelpool di nodil con incompatibilità.

Se hai etichettato il pool di nodi dedicato, puoi anche aggiungere una regola di affinità dei nodi per indicare a GDC di pianificare i carichi di lavoro solo su quel pool di nodi.

Per configurare il workload del container in modo che venga eseguito nel pool di nodi dedicato, completa i seguenti passaggi:

  1. Aggiungi le seguenti sezioni alla sezione .spec.template.spec del carico di lavoro con container:

    kind: Deployment
    apiVersion: apps/v1
        ...
        spec:
        ...
          template:
            spec:
              tolerations:
              - key: TAINT_KEY
                operator: Equal
                value: TAINT_VALUE
                effect: TAINT_EFFECT
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: LABEL_KEY
                        operator: In
                        values:
                        - "LABEL_VALUE"
              ...
    

    Sostituisci quanto segue:

    • TAINT_KEY: la chiave di taint che hai applicato al tuo pool di nodi dedicato.
    • TAINT_VALUE: il valore di taint che hai applicato al tuo pool di nodi dedicato.
    • TAINT_EFFECT: uno dei seguenti valori di effetto:
      • NoSchedule: i pod che non tollerano questa incompatibilità non vengono pianificati sul nodo; i pod esistenti non vengono rimossi dal nodo.
      • PreferNoSchedule: Kubernetes evita di pianificare pod che non tollerano questa incompatibilità sul nodo.
      • NoExecute: il pod viene rimosso dal nodo se è già in esecuzione sul nodo, mentre non viene pianificato sul nodo se non è ancora in esecuzione sul nodo stesso.
    • LABEL_KEY: la chiave dell'etichetta del nodo che hai applicato al tuo pool di nodi dedicato.
    • LABEL_VALUE: il valore dell'etichetta del nodo che hai applicato al tuo pool di nodi dedicato.

    Ad esempio, la seguente risorsa Deployment aggiunge una tolleranza per il taint workloadType=untrusted:NoExecute e una regola di affinità dei nodi per l'etichetta del nodo 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: workloadType
            operator: Equal
            value: untrusted
            effect: NoExecute
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: workloadType
                    operator: In
                    values:
                    - "untrusted"
          containers:
          - name: my-app
            image: harbor-1.org-1.zone1.google.gdc.test/harborproject/my-app
            ports:
            - containerPort: 80
          imagePullSecrets:
          - name: SECRET
    
  2. Aggiorna il deployment:

    kubectl apply -f deployment.yaml -n NAMESPACE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    Sostituisci le seguenti variabili:

    • NAMESPACE: lo spazio dei nomi del progetto del tuo carico di lavoro del container.
    • KUBERNETES_CLUSTER_KUBECONFIG: il percorso kubeconfig per il cluster Kubernetes.

GDC ricrea i pod interessati. La regola di affinità dei nodi forza i pod sul pool di nodi dedicato che hai creato. La tolleranza consente di posizionare solo questi pod sui nodi.

Verificare che la separazione funzioni

Per verificare che la pianificazione funzioni correttamente, esegui questo comando e controlla se i tuoi carichi di lavoro si trovano nel pool di nodi dedicato:

kubectl get pods -o=wide -n NAMESPACE \
    --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG

Consigli e best practice

Dopo aver configurato l'isolamento dei nodi, ti consigliamo di procedere come segue:

  • Quando crei nuovi node pool, impedisci l'esecuzione della maggior parte dei carichi di lavoro gestiti da GDC su questi nodi aggiungendo la tua incompatibilità a questi node pool.
  • Ogni volta che esegui il deployment di nuovi carichi di lavoro nel cluster, ad esempio quando installi strumenti di terze parti, controlla le autorizzazioni richieste dai pod. Se possibile, evita di eseguire il deployment di workload che utilizzano autorizzazioni elevate nei nodi condivisi.