Isoler des charges de travail dans des pools de nœuds dédiés


Cette page explique comment réduire le risque d'attaques par élévation des privilèges dans votre cluster en indiquant à Google Kubernetes Engine (GKE) de planifier les charges de travail dans un pool de nœuds dédié isolé des charges de travail privilégiées gérées par GKE. Cette page s'applique aux clusters Standard sans provisionnement automatique des nœuds. Pour séparer les charges de travail des clusters Autopilot et des clusters Standard avec le provisionnement automatique des nœuds activé, consultez la page Configurer la séparation des charges de travail dans GKE.

Présentation

Les clusters GKE utilisent des charges de travail privilégiées gérées par GKE pour activer des fonctionnalités de clusters spécifiques, telles que la collecte de métriques. Ces charges de travail disposent d'autorisations spéciales pour s'exécuter correctement dans le cluster.

Les charges de travail que vous déployez sur vos nœuds peuvent être compromises par une entité malveillante. Si vous exécutez ces charges de travail avec des charges de travail privilégiées gérées par GKE, un pirate informatique qui s'échappe avec un conteneur compromis peut utiliser les identifiants de la charge de travail privilégiée sur le nœud pour élever les privilèges dans votre cluster.

Empêcher la fuite des conteneurs

Votre principale défense doit être vos applications. GKE offre plusieurs fonctionnalités permettant de renforcer vos clusters et vos pods. Dans la plupart des cas, nous vous recommandons vivement d'isoler les charges de travail à l'aide de GKE Sandbox. GKE Sandbox est basé sur le projet Open Source gVisor et implémente l'API du noyau Linux dans l'espace utilisateur. Chaque pod s'exécute sur un noyau dédié qui isole les applications pour empêcher l'accès aux appels système privilégiés dans le noyau hôte. Les charges de travail exécutées dans GKE Sandbox sont automatiquement planifiées sur des nœuds distincts isolés des autres charges de travail.

Vous devez également suivre les recommandations de la page Renforcer la sécurité d'un cluster.

Éviter les attaques par élévation de privilèges

Si vous ne pouvez pas utiliser GKE Sandbox et que vous souhaitez ajouter une couche d'isolation supplémentaire en plus des autres mesures de renforcement, vous pouvez utiliser des rejets de nœuds et une affinité de nœud pour planifier vos charges de travail sur un pool de nœuds dédié. Un rejet de nœud indique à GKE d'éviter de planifier des charges de travail dont la tolérance ne correspond pas (telle que des charges de travail gérées par GKE) sur ces nœuds. L'affinité de nœud sur vos propres charges de travail indique à GKE de planifier vos pods sur les nœuds dédiés.

Limites de l'isolation des nœuds

  • Les pirates informatiques peuvent toujours lancer des attaques par déni de service (DoS) à partir du nœud compromis.
  • Les nœuds compromis peuvent toujours lire de nombreuses ressources, y compris tous les pods et espaces de noms du cluster.
  • Les nœuds compromis peuvent accéder aux secrets et aux identifiants utilisés par chaque pod exécuté sur ce nœud.
  • L'utilisation d'un pool de nœuds distinct pour isoler les charges de travail peut avoir un impact sur la rentabilité, l'autoscaling et l'utilisation des ressources.
  • Les nœuds compromis peuvent toujours contourner les règles de réseau de sortie.
  • Certaines charges de travail gérées par GKE doivent s'exécuter sur chaque nœud de votre cluster et être configurées pour tolérer tous les rejets.
  • Si vous déployez des DaemonSets disposant de privilèges élevés et pouvant tolérer les rejets, ces pods peuvent constituer un moyen d'augmenter les privilèges à partir d'un nœud compromis.

Fonctionnement de l'isolation des nœuds

Pour implémenter l'isolation des nœuds pour vos charges de travail, vous devez procéder comme suit :

  1. Ajoutez un rejet et un libellé au pool de nœuds pour vos charges de travail.
  2. Mettez à jour vos charges de travail avec la tolérance et la règle d'affinité de nœud correspondantes.

Dans ce guide, nous partons du principe que vous disposez d'un pool de nœuds dans votre cluster. L'utilisation de l'affinité des nœuds en plus des rejets de nœuds n'est pas obligatoire, mais nous vous recommandons de l'utiliser, car vous bénéficiez d'un meilleur contrôle sur la planification.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Si vous souhaitez utiliser Google Cloud CLI pour cette tâche, installez puis initialisez gcloud CLI. Si vous avez déjà installé gcloud CLI, assurez-vous de disposer de la dernière version en exécutant la commande gcloud components update.
  • Choisissez un nom spécifique pour le rejet de nœud et le libellé de nœud que vous souhaitez utiliser pour les pools de nœuds dédiés. Dans cet exemple, nous utilisons workloadType=untrusted.

Ajouter un rejet et un libellé à un pool de nœuds pour vos charges de travail

Créez un pool de nœuds pour vos charges de travail, puis appliquez un rejet de nœud et un libellé de nœud. Lorsque vous appliquez un rejet ou un libellé au niveau du pool de nœuds, il est appliqué automatiquement à tous les nouveaux nœuds, tels que ceux créés par l'autoscaling.

Vous pouvez également ajouter des rejets de nœuds et des libellés de nœuds à des pools de nœuds existants. Si vous utilisez l'effet NoExecute, GKE supprime tous les pods exécutés sur ces nœuds qui ne sont pas tolérants au nouveau rejet.

Pour ajouter un rejet et un libellé à un nouveau pool de nœuds, exécutez la commande suivante :

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

Remplacez les éléments suivants :

  • POOL_NAME : nom du nouveau pool de nœuds pour vos charges de travail.
  • CLUSTER_NAME : nom de votre cluster GKE.
  • TAINT_KEY=TAINT_VALUE : paire clé-valeur associée à un TAINT_EFFECT de planification. Exemple : workloadType=untrusted.
  • TAINT_EFFECT : l'une des valeurs d'effet suivantes : NoSchedule, PreferNoSchedule ou NoExecute. NoExecute fournit une meilleure garantie d'éviction que NoSchedule.
  • LABEL_KEY=LABEL_VALUE : paires clé/valeur pour les libellés de nœud, qui correspondent aux sélecteurs que vous spécifiez dans vos fichiers manifestes de charge de travail.

Ajouter une tolérance et une règle d'affinité de nœuds aux charges de travail

Une fois que vous avez ajouté un rejet au pool de nœuds dédié, aucune charge de travail ne peut y être programmée, à moins qu'elle ne dispose d'une tolérance correspondant au rejet que vous avez ajouté. Ajoutez la tolérance à la spécification de vos charges de travail afin de permettre la programmation de ces pods sur votre pool de nœuds rejeté.

Si vous avez attribué un libellé au pool de nœuds dédié, vous pouvez également ajouter une règle d'affinité de nœuds pour indiquer à GKE de ne planifier vos charges de travail que sur ce pool de nœuds.

L'exemple suivant ajoute une tolérance au rejet workloadType=untrusted:NoExecute et une règle d'affinité de nœud pour le libellé de nœud 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"]

Remplacez les éléments suivants :

  • TAINT_KEY : clé de rejet que vous avez appliquée à votre pool de nœuds dédié.
  • TAINT_VALUE : valeur de rejet que vous avez appliquée à votre pool de nœuds dédié.
  • LABEL_KEY : clé de libellé de nœud que vous avez appliquée à votre pool de nœuds dédié.
  • LABEL_VALUE : valeur du libellé de nœud que vous avez appliquée à votre pool de nœuds dédié.

Lorsque vous mettez à jour votre déploiement avec kubectl apply, GKE recrée les pods concernés. La règle d'affinité de nœuds force l'application des pods sur le pool de nœuds dédié que vous avez créé. La tolérance ne permet de placer ces pods que sur les nœuds.

Vérifier que la séparation fonctionne

Pour vérifier que la planification fonctionne correctement, exécutez la commande suivante et vérifiez si vos charges de travail se trouvent dans le pool de nœuds dédié :

kubectl get pods -o=wide

Recommandations et bonnes pratiques

Après avoir configuré l'isolation des nœuds, nous vous recommandons d'effectuer les opérations suivantes :

  • Vous ne pouvez limiter des pools de nœuds spécifiques sur les charges de travail gérées par GKE qu'en ajoutant le rejet components.gke.io/gke-managed-components. L'ajout de ce rejet empêche la planification de vos pods sur ces nœuds, améliorant ainsi l'isolation.
  • Lorsque vous créez des pools de nœuds, empêchez la plupart des charges de travail gérées par GKE de s'exécuter sur ces nœuds en ajoutant un rejet à ces pools.
  • Chaque fois que vous déployez de nouvelles charges de travail sur votre cluster, par exemple lors de l'installation d'outils tiers, vérifiez les autorisations requises par les pods. Dans la mesure du possible, évitez de déployer des charges de travail qui utilisent des autorisations avec privilèges élevés sur des nœuds partagés.

Étape suivante