Soluciona problemas de cuentas de servicio en GKE

Los permisos mal configurados o faltantes para las cuentas de servicio de Google Kubernetes Engine (GKE) pueden generar varios problemas, como que los nodos no se registren o que las cargas de trabajo no puedan acceder a los Google Cloud servicios.

Usa este documento para mitigar los problemas causados por cuentas de servicio mal configuradas, inhabilitadas o borradas.

Esta información es importante para los administradores y operadores de la plataforma, y los ingenieros de seguridad que configuran y administran los permisos de IAM a nivel del proyecto para los nodos de GKE y los componentes principales de GKE. Para obtener más información sobre los roles comunes y las tareas de ejemplo a las que hacemos referencia en Google Cloud el contenido, consulta Roles y tareas comunes del usuario de GKE.

Otorga el rol necesario para GKE a las cuentas de servicio del nodo

Para los clústeres de GKE que usan la versión 1.33 de Kubernetes o versiones anteriores, las cuentas de servicio de IAM que usan tus nodos de GKE deben tener todos los permisos que se incluyen en el rol de IAM de la cuenta de servicio de nodo predeterminada de Kubernetes Engine (roles/container.defaultNodeServiceAccount). Si a una cuenta de servicio del nodo de GKE le falta uno o más de estos permisos, GKE no puede realizar tareas del sistema como las siguientes:

Es posible que las cuentas de servicio del nodo no tengan ciertos permisos necesarios por motivos como los siguientes:

  • La organización aplica la restricción de la política de la organización iam.automaticIamGrantsForDefaultServiceAccounts, que impide que se otorguen automáticamente roles de IAM a las cuentas de servicio predeterminadas de IAM. Google Cloud
  • El rol de IAM que otorgas a las cuentas de servicio del nodo personalizadas no incluye todos los permisos necesarios que se incluyen en el rol roles/container.defaultNodeServiceAccount.

Si a tu cuenta de servicio del nodo le faltan los permisos que requiere GKE, es posible que veas errores y avisos como los siguientes:

  • En la Google Cloud consola, en la página Clústeres de Kubernetes, aparece un Otorgar permisos críticos mensaje de error en la columna Notificaciones para un clúster específico.
  • En la Google Cloud consola de, en la página de detalles del clúster para un clúster específico, aparece el siguiente mensaje de error:

    Grant roles/container.defaultNodeServiceAccount role to Node service account to allow for non-degraded operations.
    
  • En los registros de auditoría de Cloud, los registros de actividad del administrador para Google Cloud las APIs como monitoring.googleapis.com tienen los siguientes valores si faltan los permisos correspondientes para acceder a esas APIs en la cuenta de servicio del nodo:

    • Gravedad: ERROR
    • Mensaje: Permission denied (or the resource may not exist)
  • Faltan registros de nodos específicos en Cloud Logging, y los registros de Pods para el agente de Logging en esos nodos muestran errores 401. Para obtener estos registros de Pods, ejecuta el siguiente comando:

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

    Si el resultado es true, la carga de trabajo del sistema experimenta errores 401, lo que indica una falta de permisos.

Para resolver este problema, otorga el rol de cuenta de servicio de nodo predeterminada de Kubernetes Engine (roles/container.defaultNodeServiceAccount) en el proyecto a la cuenta de servicio que causa los errores. Selecciona una de las siguientes opciones:

Console

Para encontrar el nombre de la cuenta de servicio que usan tus nodos, haz lo siguiente:

  1. Ve a la página Clústeres de Kubernetes:

    Ir a clústeres de Kubernetes

  2. En la lista de clústeres, haz clic en el nombre del clúster que deseas inspeccionar.

  3. Busca el nombre de la cuenta de servicio del nodo. Necesitarás este nombre más adelante.

    • Para los clústeres en modo Autopilot, en la sección Seguridad , busca el campo Cuenta de servicio.
    • Para los clústeres en modo Standard, haz lo siguiente:
    1. Haz clic en la pestaña Nodos.
    2. En la tabla Grupos de nodos, haz clic en el nombre de un grupo de nodos. Se abrirá la página Detalles del grupo de nodos.
    3. En la sección Seguridad, busca el campo Cuenta de servicio.

    Si el valor en el campo Cuenta de servicio es default, tus nodos usan la cuenta de servicio predeterminada de Compute Engine. Si el valor en este campo no es default, tus nodos usan una cuenta de servicio personalizada.

Para otorgar el rol Kubernetes Engine Default Node Service Account a la cuenta de servicio, haz lo siguiente:

  1. Ve a la página Bienvenido:

    Ir a Bienvenido

  2. En el campo Número de proyecto, haz clic en Copiar al portapapeles.

  3. Ve a la página IAM:

    Ir a IAM

  4. Haz clic en Grant access.

  5. En el campo Principales nuevas, especifica el nombre de tu cuenta de servicio del nodo. Si tus nodos usan la cuenta de servicio predeterminada de Compute Engine, especifica el siguiente valor:

    PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Reemplaza PROJECT_NUMBER por el número de proyecto que copiaste.

  6. En el menú Selecciona un rol, selecciona el rol Cuenta de servicio de nodo predeterminada de Kubernetes Engine.

  7. Haz clic en Guardar.

Para verificar que se otorgó el rol, haz lo siguiente:

  1. En la página IAM, haz clic en la pestaña Ver por roles.
  2. Expande la sección Cuenta de servicio de nodo predeterminada de Kubernetes Engine. Se muestra una lista de principales que tienen este rol.
  3. Busca tu cuenta de servicio del nodo en la lista de principales.

gcloud

  1. Busca el nombre de la cuenta de servicio que usan tus nodos:

    • Para los clústeres en modo Autopilot, ejecuta el siguiente comando:
    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccount
    
    • Para los clústeres en modo Standard, ejecuta el siguiente comando:
    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --format="table(nodePools.name,nodePools.config.serviceAccount)"
    

    Si el resultado es default, tus nodos usan la cuenta de servicio predeterminada de Compute Engine. Si el resultado no es default, tus nodos usan una cuenta de servicio personalizada.

  2. Busca el número de tu Google Cloud proyecto:

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

    Reemplaza PROJECT_ID por el ID del proyecto.

    El resultado es similar a este:

    12345678901
    
  3. Otorga el rol roles/container.defaultNodeServiceAccount a la cuenta de servicio:

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

    Reemplaza SERVICE_ACCOUNT_NAME por el nombre de la cuenta de servicio que encontraste en el paso anterior. Si tus nodos usan la cuenta de servicio predeterminada de Compute Engine, especifica el siguiente valor:

    serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com
    

    Reemplaza PROJECT_NUMBER por el número de proyecto del paso anterior.

  4. Verifica que el rol se haya otorgado correctamente:

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

    El resultado es el nombre de tu cuenta de servicio.

Identifica los clústeres que tienen cuentas de servicio del nodo con permisos faltantes

Usa las recomendaciones de GKE del NODE_SA_MISSING_PERMISSIONS subtipo de recomendador para identificar los clústeres de Autopilot y Standard que tienen cuentas de servicio del nodo con permisos faltantes. Recommender solo identifica los clústeres que se crearon a partir del 1 de enero de 2024. Para encontrar y corregir los permisos faltantes con Recommender, haz lo siguiente:

  1. Busca recomendaciones activas en tu proyecto para el subtipo de recomendador 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"
    

    Reemplaza lo siguiente:

    • LOCATION: la ubicación en la que se deben buscar las recomendaciones.
    • PROJECT_ID: Es el ID del Google Cloud proyecto.

    El resultado es similar al siguiente, que indica que un clúster tiene una cuenta de servicio del nodo con permisos faltantes:

    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
    

    Es posible que la recomendación tarde hasta 24 horas en aparecer. Para obtener instrucciones detalladas, consulta Cómo ver estadísticas y recomendaciones.

  2. Para cada clúster que se encuentre en el resultado del paso anterior, busca las cuentas de servicio del nodo asociadas y otórgales el rol necesario. Para obtener más detalles, consulta las instrucciones en la sección Otorga a las cuentas de servicio del nodo el rol necesario para GKE.

    Después de otorgar el rol necesario a las cuentas de servicio del nodo identificadas, la recomendación puede persistir hasta por 24 horas, a menos que la descartes de forma manual.

Identifica todas las cuentas de servicio del nodo con permisos faltantes

Puedes ejecutar una secuencia de comandos que busque grupos de nodos en los clústeres de Standard y Autopilot de tu proyecto para cualquier cuenta de servicio del nodo que no tenga los permisos necesarios para GKE. Esta secuencia de comandos usa gcloud CLI y la jq utilidad. Para ver la secuencia de comandos, expande la siguiente sección:

Ver la secuencia de comandos

#!/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

Esta secuencia de comandos se aplica a todos los clústeres de GKE de tu proyecto.

Después de identificar los nombres de las cuentas de servicio con permisos faltantes, otórgales el rol necesario. Para obtener más detalles, consulta las instrucciones en la sección Otorga a las cuentas de servicio del nodo el rol necesario para GKE.

Restablece la cuenta de servicio predeterminada en tu Google Cloud proyecto

La cuenta de servicio predeterminada de GKE, container-engine-robot, se puede desvincular de un proyecto por accidente. El rol de agente de servicio de Kubernetes Engine (roles/container.serviceAgent) es un rol de Identity and Access Management (IAM) que otorga a la cuenta de servicio los permisos necesarios para administrar los recursos del clúster. Si quitas esta vinculación de rol de la cuenta de servicio, la cuenta de servicio predeterminada se desvincula del proyecto, lo que puede evitar que se implementen aplicaciones y que se realicen otras operaciones de clúster.

Para ver si la cuenta de servicio se quitó de tu proyecto, puedes usar la Google Cloud consola o la Google Cloud CLI.

Console

gcloud

  • Ejecuta el siguiente comando:

    gcloud projects get-iam-policy PROJECT_ID
    

    Reemplaza PROJECT_ID con el ID del proyecto.

Si en el panel o el comando no se muestra container-engine-robot entre las cuentas de servicio, el rol no está vinculado.

Para restablecer la vinculación del rol de agente de servicio de Kubernetes Engine (roles/container.serviceAgent), ejecuta los siguientes comandos:

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

Confirma que se restableció la vinculación de rol:

gcloud projects get-iam-policy PROJECT_ID

Si ves el nombre de la cuenta de servicio junto con el rol container.serviceAgent, se restableció la vinculación de rol. Por ejemplo:

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

Habilita la cuenta de servicio predeterminada de Compute Engine

La cuenta de servicio que se usa para el grupo de nodos suele ser la cuenta de servicio predeterminada de Compute Engine. Si esta cuenta de servicio predeterminada está desactivada, es posible que los nodos no se registren con el clúster.

Para ver si la cuenta de servicio está desactivada en tu proyecto, puedes usar la Google Cloud consola de Google Cloud o gcloud CLI.

Console

gcloud

  • Ejecuta el siguiente comando:
gcloud iam service-accounts list  --filter="NAME~'compute' AND disabled=true"

Si la cuenta de servicio está desactivada, ejecuta los siguientes comandos para habilitarla:

  1. Busca el número de tu Google Cloud proyecto:

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

    Reemplaza PROJECT_ID por el ID del proyecto.

    El resultado es similar a este:

    12345678901
    
  2. Habilita la cuenta de servicio:

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

    Reemplaza PROJECT_NUMBER por el número de proyecto del resultado del paso anterior.

Para obtener más información, consulta Soluciona problemas de registro de nodos.

Error 400/403: Faltan permisos de edición en una cuenta

Si se borra tu cuenta de servicio, es posible que veas un error de permisos de edición faltantes. Para obtener información sobre cómo solucionar este error, consulta Error 400/403: Faltan permisos de edición en una cuenta.

¿Qué sigue?