Résoudre les problèmes liés aux comptes de service dans GKE

Des autorisations mal configurées ou manquantes pour les comptes de service Google Kubernetes Engine (GKE) peuvent entraîner divers problèmes, tels que l'échec de l'enregistrement des nœuds ou l'impossibilité pour les charges de travail d'accéder aux services. Google Cloud

Utilisez ce document pour atténuer les problèmes causés par des comptes de service mal configurés, désactivés ou supprimés.

Ces informations sont importantes pour les administrateurs et opérateurs de plate-forme, ainsi que pour les ingénieurs en sécurité qui configurent et gèrent les autorisations IAM au niveau du projet pour les nœuds GKE et les composants GKE de base. Pour en savoir plus sur les rôles courants et les exemples de tâches que nous citons dans le Google Cloud contenu, consultez Rôles utilisateur et tâches courantes de GKE.

Attribuer le rôle requis pour GKE aux comptes de service de nœud

Pour les clusters GKE utilisant la version 1.33 de Kubernetes ou une version antérieure, les comptes de service IAM utilisés par vos nœuds GKE doivent disposer de toutes les autorisations incluses dans le rôle IAM Compte de service de nœud par défaut Kubernetes Engine (roles/container.defaultNodeServiceAccount). Si un compte de service de nœud GKE ne dispose pas d'une ou de plusieurs de ces autorisations, GKE ne peut pas effectuer de tâches système telles que les suivantes :

Les comptes de service de nœud peuvent ne pas disposer de certaines autorisations requises pour les raisons suivantes :

  • L'organisation applique la contrainte liée aux règles d'administration iam.automaticIamGrantsForDefaultServiceAccounts, ce qui empêche Google Cloud d'attribuer automatiquement des rôles IAM aux comptes de service IAM par défaut.
  • Le rôle IAM que vous attribuez aux comptes de service de nœud personnalisés n'inclut pas toutes les autorisations requises incluses dans le rôle roles/container.defaultNodeServiceAccount.

Si votre compte de service de nœud ne dispose pas des autorisations requises par GKE, des erreurs et des notifications semblables à celles-ci peuvent s'afficher :

  • Dans la Google Cloud console, sur la page Clusters Kubernetes, un message d'erreur Accorder des autorisations critiques s'affiche dans la colonne Notifications d'un cluster spécifique.
  • Dans la Google Cloud console, sur la page d'informations du cluster pour un cluster spécifique, le message d'erreur suivant s'affiche :

    Grant roles/container.defaultNodeServiceAccount role to Node service account to allow for non-degraded operations.
    
  • Dans Cloud Audit Logs, les journaux d'audit des activités d'administration pour les Google Cloud API telles que monitoring.googleapis.com présentent les valeurs suivantes si les autorisations correspondantes pour accéder à ces API sont manquantes dans le compte de service de nœud :

    • Gravité : ERROR
    • Message : Permission denied (or the resource may not exist) (Autorisation refusée ou la ressource n'existe peut-être pas)
  • Les journaux de nœuds spécifiques sont manquants dans Cloud Logging et les journaux de pods de l'agent de journalisation sur ces nœuds affichent des erreurs 401. Pour obtenir ces journaux de pods, exécutez la commande suivante :

    [[ $(kubectl logs -l k8s-app=fluentbit-gke -n kube-system -c fluentbit-gke | grep -cw "Received 401") -gt 0 ]] && echo "true" || echo "false"
    

    Si le résultat est true, cela signifie que la charge de travail système rencontre des erreurs 401, qui indiquent un manque d'autorisations.

Pour résoudre ce problème, attribuez le rôle Compte de service de nœud par défaut Kubernetes Engine (roles/container.defaultNodeServiceAccount) sur le projet au compte de service qui provoque les erreurs. Sélectionnez l'une des options suivantes :

Console

Pour trouver le nom du compte de service utilisé par vos nœuds, procédez comme suit :

  1. Accédez à la page Clusters Kubernetes :

    Accéder à la page "Clusters Kubernetes"

  2. Dans la liste des clusters, cliquez sur le nom du cluster que vous souhaitez inspecter.

  3. Recherchez le nom du compte de service de nœud. Vous en aurez besoin plus tard.

    • Pour les clusters en mode Autopilot, dans la section Sécurité , recherchez le champ Compte de service.
    • Pour les clusters en mode Standard, procédez comme suit :
    1. Cliquez sur l'onglet Nœuds.
    2. Dans la table Pools de nœuds, cliquez sur le nom d'un pool de nœuds. La page Détails du pool de nœuds s'ouvre.
    3. Dans la section Sécurité, recherchez le champ Compte de service.

    Si la valeur du champ Compte de service est default, vos nœuds utilisent le compte de service Compute Engine par défaut. Si la valeur de ce champ n'est pas default, vos nœuds utilisent un compte de service personnalisé.

Pour attribuer le rôle Kubernetes Engine Default Node Service Account au compte de service, procédez comme suit :

  1. Accédez à la page d'accueil :

    Accéder à la page d'accueil

  2. Dans le champ Numéro du projet, cliquez sur Copier dans le presse-papiers.

  3. Accédez à la page IAM :

    Accéder à la page "IAM"

  4. Cliquez sur Accorder l'accès.

  5. Dans le champ Nouveaux comptes principaux, spécifiez le nom de votre compte de service de nœud. Si vos nœuds utilisent le compte de service Compute Engine par défaut, spécifiez la valeur suivante :

    PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Remplacez PROJECT_NUMBER par le numéro de projet que vous avez copié.

  6. Dans le menu Sélectionner un rôle, choisissez le rôle Compte de service de nœud par défaut Kubernetes Engine.

  7. Cliquez sur Enregistrer.

Pour vérifier que le rôle a été attribué, procédez comme suit :

  1. Sur la page IAM, cliquez sur l'onglet Afficher par rôle.
  2. Développez la section Compte de service de nœud par défaut Kubernetes Engine. Une liste des comptes principaux disposant de ce rôle s'affiche.
  3. Recherchez votre compte de service de nœud dans la liste des comptes principaux.

gcloud

  1. Recherchez le nom du compte de service utilisé par vos nœuds :

    • Pour les clusters en mode Autopilot, exécutez la commande suivante :
    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccount
    
    • Pour les clusters en mode Standard, exécutez la commande suivante :
    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --format="table(nodePools.name,nodePools.config.serviceAccount)"
    

    Si le résultat est default, vos nœuds utilisent le compte de service Compute Engine par défaut. Si le résultat n'est pas default, vos nœuds utilisent un compte de service personnalisé.

  2. Recherchez le numéro de votre Google Cloud projet :

    gcloud projects describe PROJECT_ID \
        --format="value(projectNumber)"
    

    Remplacez PROJECT_ID par l'ID du projet.

    Le résultat ressemble à ce qui suit :

    12345678901
    
  3. Attribuez le rôle roles/container.defaultNodeServiceAccount au compte de service :

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member="SERVICE_ACCOUNT_NAME" \
        --role="roles/container.defaultNodeServiceAccount"
    

    Remplacez SERVICE_ACCOUNT_NAME par le nom du compte de service que vous avez trouvé à l'étape précédente. Si vos nœuds utilisent le compte de service Compute Engine par défaut, spécifiez la valeur suivante :

    serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Remplacez PROJECT_NUMBER par le numéro de projet de l'étape précédente.

  4. Vérifiez que le rôle a bien été attribué :

    gcloud projects get-iam-policy PROJECT_ID \
        --flatten="bindings[].members" --filter=bindings.role:roles/container.defaultNodeServiceAccount \
        --format='value(bindings.members)'
    

    Le résultat correspond au nom de votre compte de service.

Identifier les clusters dont les comptes de service de nœud ne disposent pas d'autorisations

Utilisez les recommandations GKE du NODE_SA_MISSING_PERMISSIONS sous-type de l'outil de recommandation pour identifier les clusters Autopilot et Standard dont les comptes de service de nœud ne disposent pas d'autorisations. L'outil de recommandation n'identifie que les clusters créés à compter du 1er janvier 2024. Pour trouver et corriger les autorisations manquantes à l'aide de l'outil de recommandation, procédez comme suit :

  1. Recherchez les recommandations actives dans votre projet pour le sous-type de l'outil de recommandation NODE_SA_MISSING_PERMISSIONS :

    gcloud recommender recommendations list \
        --recommender=google.container.DiagnosisRecommender \
        --location LOCATION \
        --project PROJECT_ID \
        --format yaml \
        --filter="recommenderSubtype:NODE_SA_MISSING_PERMISSIONS"
    

    Remplacez les éléments suivants :

    • LOCATION : emplacement où trouver les recommandations.
    • PROJECT_ID: ID de votre Google Cloud projet.

    Le résultat ressemble à ce qui suit, ce qui indique qu'un cluster possède un compte de service de nœud qui ne dispose pas d'autorisations :

    associatedInsights:
    # lines omitted for clarity
    recommenderSubtype: NODE_SA_MISSING_PERMISSIONS
    stateInfo:
      state: ACTIVE
    targetResources:
    - //container.googleapis.com/projects/12345678901/locations/us-central1/clusters/cluster-1
    

    L'affichage de la recommandation peut prendre jusqu'à 24 heures. Pour obtenir des instructions détaillées, consultez Afficher les insights et les recommandations.

  2. Pour chaque cluster figurant dans le résultat de l'étape précédente, recherchez les comptes de service de nœud associés et attribuez le rôle requis à ces comptes de service. Pour en savoir plus, consultez les instructions de la section Attribuer le rôle requis pour GKE aux comptes de service de nœud.

    Une fois que vous avez attribué le rôle requis aux comptes de service de nœud identifiés, la recommandation peut persister jusqu'à 24 heures, sauf si vous la supprimez manuellement.

Identifier tous les comptes de service de nœud qui ne disposent pas d'autorisations

Vous pouvez exécuter un script qui recherche des pools de nœuds dans les clusters en mode Standard et Autopilot de votre projet pour les comptes de service de nœud qui ne disposent pas des autorisations requises pour GKE. Ce script utilise la gcloud CLI et l'utilitaire jq. Pour afficher le script, développez la section suivante :

Afficher le script

#!/bin/bash

# Set your project ID
project_id=PROJECT_ID
project_number=$(gcloud projects describe "$project_id" --format="value(projectNumber)")
declare -a all_service_accounts
declare -a sa_missing_permissions

# Function to check if a service account has a specific permission
# $1: project_id
# $2: service_account
# $3: permission
service_account_has_permission() {
  local project_id="$1"
  local service_account="$2"
  local permission="$3"

  local roles=$(gcloud projects get-iam-policy "$project_id" \
          --flatten="bindings[].members" \
          --format="table[no-heading](bindings.role)" \
          --filter="bindings.members:\"$service_account\"")

  for role in $roles; do
    if role_has_permission "$role" "$permission"; then
      echo "Yes" # Has permission
      return
    fi
  done

  echo "No" # Does not have permission
}

# Function to check if a role has the specific permission
# $1: role
# $2: permission
role_has_permission() {
  local role="$1"
  local permission="$2"
  gcloud iam roles describe "$role" --format="json" | \
  jq -r ".includedPermissions" | \
  grep -q "$permission"
}

# Function to add $1 into the service account array all_service_accounts
# $1: service account
add_service_account() {
  local service_account="$1"
  all_service_accounts+=( ${service_account} )
}

# Function to add service accounts into the global array all_service_accounts for a Standard GKE cluster
# $1: project_id
# $2: location
# $3: cluster_name
add_service_accounts_for_standard() {
  local project_id="$1"
  local cluster_location="$2"
  local cluster_name="$3"

  while read nodepool; do
    nodepool_name=$(echo "$nodepool" | awk '{print $1}')
    if [[ "$nodepool_name" == "" ]]; then
      # skip the empty line which is from running `gcloud container node-pools list` in GCP console
      continue
    fi
    while read nodepool_details; do
      service_account=$(echo "$nodepool_details" | awk '{print $1}')

      if [[ "$service_account" == "default" ]]; then
        service_account="${project_number}-compute@developer.gserviceaccount.com"
      fi
      if [[ -n "$service_account" ]]; then
        printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" $service_account $project_id  $cluster_name $cluster_location $nodepool_name
        add_service_account "${service_account}"
      else
        echo "cannot find service account for node pool $project_id\t$cluster_name\t$cluster_location\t$nodepool_details"
      fi
    done <<< "$(gcloud container node-pools describe "$nodepool_name" --cluster "$cluster_name" --zone "$cluster_location" --project "$project_id" --format="table[no-heading](config.serviceAccount)")"
  done <<< "$(gcloud container node-pools list --cluster "$cluster_name" --zone "$cluster_location" --project "$project_id" --format="table[no-heading](name)")"

}

# Function to add service accounts into the global array all_service_accounts for an Autopilot GKE cluster
# Autopilot cluster only has one node service account.
# $1: project_id
# $2: location
# $3: cluster_name
add_service_account_for_autopilot(){
  local project_id="$1"
  local cluster_location="$2"
  local cluster_name="$3"

  while read service_account; do
      if [[ "$service_account" == "default" ]]; then
        service_account="${project_number}-compute@developer.gserviceaccount.com"
      fi
      if [[ -n "$service_account" ]]; then
        printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" $service_account $project_id  $cluster_name $cluster_location $nodepool_name
        add_service_account "${service_account}"
      else
        echo "cannot find service account" for cluster  "$project_id\t$cluster_name\t$cluster_location\t"
      fi
  done <<< "$(gcloud container clusters describe "$cluster_name" --location "$cluster_location" --project "$project_id" --format="table[no-heading](autoscaling.autoprovisioningNodePoolDefaults.serviceAccount)")"
}


# Function to check whether the cluster is an Autopilot cluster or not
# $1: project_id
# $2: location
# $3: cluster_name
is_autopilot_cluster() {
  local project_id="$1"
  local cluster_location="$2"
  local cluster_name="$3"
  autopilot=$(gcloud container clusters describe "$cluster_name" --location "$cluster_location" --format="table[no-heading](autopilot.enabled)")
  echo "$autopilot"
}


echo "--- 1. List all service accounts in all GKE node pools"
printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" "service_account" "project_id" "cluster_name" "cluster_location" "nodepool_name"
while read cluster; do
  cluster_name=$(echo "$cluster" | awk '{print $1}')
  cluster_location=$(echo "$cluster" | awk '{print $2}')
  # how to find a cluster is a Standard cluster or an Autopilot cluster
  autopilot=$(is_autopilot_cluster "$project_id" "$cluster_location" "$cluster_name")
  if [[ "$autopilot" == "True" ]]; then
    add_service_account_for_autopilot "$project_id" "$cluster_location"  "$cluster_name"
  else
    add_service_accounts_for_standard "$project_id" "$cluster_location"  "$cluster_name"
  fi
done <<< "$(gcloud container clusters list --project "$project_id" --format="value(name,location)")"

echo "--- 2. Check if service accounts have permissions"
unique_service_accounts=($(echo "${all_service_accounts[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

echo "Service accounts: ${unique_service_accounts[@]}"
printf "%-60s| %-40s| %-40s| %-20s\n" "service_account" "has_logging_permission" "has_monitoring_permission" "has_performance_hpa_metric_write_permission"
for sa in "${unique_service_accounts[@]}"; do
  logging_permission=$(service_account_has_permission "$project_id" "$sa" "logging.logEntries.create")
  time_series_create_permission=$(service_account_has_permission "$project_id" "$sa" "monitoring.timeSeries.create")
  metric_descriptors_create_permission=$(service_account_has_permission "$project_id" "$sa" "monitoring.metricDescriptors.create")
  if [[ "$time_series_create_permission" == "No" || "$metric_descriptors_create_permission" == "No" ]]; then
    monitoring_permission="No"
  else
    monitoring_permission="Yes"
  fi
  performance_hpa_metric_write_permission=$(service_account_has_permission "$project_id" "$sa" "autoscaling.sites.writeMetrics")
  printf "%-60s| %-40s| %-40s| %-20s\n" $sa $logging_permission $monitoring_permission $performance_hpa_metric_write_permission

  if [[ "$logging_permission" == "No" || "$monitoring_permission" == "No" || "$performance_hpa_metric_write_permission" == "No" ]]; then
    sa_missing_permissions+=( ${sa} )
  fi
done

echo "--- 3. List all service accounts that don't have the above permissions"
if [[ "${#sa_missing_permissions[@]}" -gt 0 ]]; then
  printf "Grant roles/container.defaultNodeServiceAccount to the following service accounts: %s\n" "${sa_missing_permissions[@]}"
else
  echo "All service accounts have the above permissions"
fi

Ce script s'applique à tous les clusters GKE de votre projet.

Une fois que vous avez identifié les noms des comptes de service qui ne disposent pas d'autorisations, attribuez-leur le rôle requis. Pour en savoir plus, consultez les instructions de la section Attribuer le rôle requis pour GKE aux comptes de service de nœud.

Restaurer le compte de service par défaut dans votre Google Cloud projet

Le compte de service par défaut de GKE, container-engine-robot, peut être dissocié accidentellement d'un projet. Le rôle d'agent de service Kubernetes Engine (roles/container.serviceAgent) est un rôle Identity and Access Management (IAM) qui accorde au compte de service les autorisations nécessaires pour gérer les ressources du cluster. Si vous supprimez cette liaison de rôle du compte de service, le compte de service par défaut est dissocié du projet, ce qui peut vous empêcher de déployer des applications et d'effectuer d'autres opérations sur le cluster.

Pour vérifier si le compte de service a été supprimé de votre projet, vous pouvez utiliser la Google Cloud console ou Google Cloud CLI.

Console

gcloud

  • Exécutez la commande suivante :

    gcloud projects get-iam-policy PROJECT_ID
    

    Remplacez PROJECT_ID par l'ID du projet.

Si le tableau de bord ou la commande n'affiche pas container-engine-robot parmi vos comptes de service, le rôle est dissocié.

Pour restaurer la liaison du rôle d'agent de service Kubernetes Engine (roles/container.serviceAgent), exécutez les commandes suivantes :

PROJECT_NUMBER=$(gcloud projects describe "PROJECT_ID" \
    --format 'get(projectNumber)') \
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member "serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.iam.gserviceaccount.com" \
    --role roles/container.serviceAgent

Vérifiez que la liaison de rôle est restaurée :

gcloud projects get-iam-policy PROJECT_ID

Si vous voyez le nom du compte de service avec le rôle container.serviceAgent, la liaison de rôle est restaurée. Par exemple :

- members:
  - serviceAccount:service-1234567890@container-engine-robot.iam.gserviceaccount.com
  role: roles/container.serviceAgent

Activer le compte de service Compute Engine par défaut

Le compte de service utilisé pour le pool de nœuds est généralement le compte de service Compute Engine par défaut. Si ce compte de service par défaut est désactivé, vos nœuds risquent de ne pas s'enregistrer auprès du cluster.

Pour vérifier si le compte de service est désactivé dans votre projet, vous pouvez utiliser la Google Cloud console ou gcloud CLI.

Console

gcloud

  • Exécutez la commande suivante :
gcloud iam service-accounts list  --filter="NAME~'compute' AND disabled=true"

Si le compte de service est désactivé, exécutez les commandes suivantes pour l'activer :

  1. Recherchez le numéro de votre Google Cloud projet :

    gcloud projects describe PROJECT_ID \
        --format="value(projectNumber)"
    

    Remplacez PROJECT_ID par l'ID du projet.

    Le résultat ressemble à ce qui suit :

    12345678901
    
  2. Activez le compte de service :

    gcloud iam service-accounts enable PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Remplacez PROJECT_NUMBER par le numéro de projet issu du résultat de l'étape précédente.

Pour en savoir plus, consultez Résoudre les problèmes d'enregistrement de nœuds.

Erreur 400/403 : le compte n'est pas autorisé à apporter des modifications

Si votre compte de service est supprimé, une erreur d'autorisation de modification peut s'afficher. Pour savoir comment résoudre ce problème, consultez Erreur 400/403 : le compte n'est pas autorisé à apporter des modifications.

Étape suivante