Désactiver le port accessible en lecture seule du kubelet dans les clusters GKE

Cette page explique comment désactiver le port non sécurisé du kubelet accessible en lecture seule, dans les clusters Google Kubernetes Engine (GKE), afin de réduire le risque d'accès non autorisé au kubelet. Elle explique également comment migrer des applications vers un port plus sécurisé.

Dans les clusters Kubernetes, y compris GKE, le processus kubelet s'exécutant sur les nœuds diffuse une API en lecture seule à l'aide du port non sécurisé 10255. Kubernetes n'effectue aucune vérification d'authentification ni d'autorisation sur ce port. Le kubelet diffuse les mêmes points de terminaison sur le port 10250, authentifié et plus sécurisé.

Désactivez le port en lecture seule du kubelet et modifiez toutes les charges de travail qui utilisent le port 10255 pour qu'elles utilisent le port plus sécurisé 10250.

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é la gcloud CLI, obtenez la dernière version en exécutant la commande gcloud components update. Il est possible que les versions antérieures de gcloud CLI ne permettent pas d'exécuter les commandes de ce document.
  • Assurez-vous de disposer d'un cluster Autopilot ou Standard existant. Pour créer un cluster, consultez la page Créer un cluster Autopilot.

Conditions requises

  • Vous ne pouvez désactiver le port accessible en lecture seule et non sécurisé du kubelet que dans les versions 1.26.4-gke.500 ou ultérieures de GKE.

Rechercher l'utilisation de ports non sécurisés et migrer les applications

Avant de désactiver le port en lecture seule non sécurisé, migrez toutes les applications en cours d'exécution qui utilisent le port vers le port en lecture seule plus sécurisé. Les charges de travail qui peuvent nécessiter une migration incluent les pipelines de métriques personnalisées et les charges de travail qui accèdent aux points de terminaison kubelet.

  • Pour les charges de travail qui ont besoin d'accéder aux informations fournies par l'API kubelet sur le nœud, comme les métriques, utilisez le port 10250.
  • Pour les charges de travail qui obtiennent des informations Kubernetes à l'aide de l'API Kubelet sur le nœud, comme la liste des pods sur le nœud, utilisez plutôt l'API Kubernetes.

Vérifier si les applications utilisent le port accessible en lecture seule et non sécurisé du kubelet

Cette section explique comment vérifier l'utilisation de ports non sécurisés dans votre cluster.

Vérifier l'utilisation des ports en mode Autopilot

Pour vérifier l'utilisation des ports dans un cluster Autopilot, assurez-vous qu'au moins une charge de travail autre qu'un DaemonSet s'exécute dans le cluster. Si vous effectuez les étapes suivantes sur un cluster Autopilot vide, les résultats peuvent être non valides.

  1. Enregistrez le manifeste suivant sous le nom read-only-port-metrics.yaml :

    # Create a namespace for the DaemonSet that checks for port usage.
    apiVersion: v1
    kind: Namespace
    metadata:
      name: node-metrics-printer-namespace
    ---
    # Grant access to read node metrics in the cluster.
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: node-metrics-printer-role
    rules:
    - apiGroups:
      - ""
      resources:
      - nodes/metrics
      verbs:
      - get
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: node-metrics-printer-binding
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: node-metrics-printer-role
    # Bind the ClusterRole to the ServiceAccount that the DaemonSet will use.
    subjects:
    - kind: ServiceAccount
      name: node-metrics-printer-sa
      namespace: node-metrics-printer-namespace
    ---
    # Create a ServiceAccount for the DaemonSet.
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: node-metrics-printer-sa
      namespace: node-metrics-printer-namespace
    ---
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-metrics-printer
      namespace: node-metrics-printer-namespace
    spec:
      selector:
        matchLabels:
          app: node-metrics-printer
      template:
        metadata:
          labels:
            app: node-metrics-printer
        spec:
          # Assign the ServiceAccount to the DaemonSet.
          serviceAccountName: node-metrics-printer-sa
          containers:
          - name: metrics-printer
            image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest
            command: ["sh", "-c"]
            # Call the /metrics endpoint using the insecure kubelet read-only
            # port.
            args:
            - 'while true; do curl -s --cacert "${CA_CERT}" -H "Authorization: Bearer $(cat ${TOKEN_FILE})" "https://${NODE_ADDRESS}:10250/metrics"|grep kubelet_http_requests_total; sleep 20; done'
            env:
            # Provide credentials and the IP address for the command.
            - name: CA_CERT
              value: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            - name: TOKEN_FILE
              value: /var/run/secrets/kubernetes.io/serviceaccount/token
            - name: NODE_ADDRESS
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
    

    Ce fichier manifeste effectue les opérations suivantes :

    1. Crée un espace de noms et configure des rôles RBAC pour permettre la lecture des métriques de nœud.
    2. Déploie un DaemonSet qui vérifie les métriques du kubelet pour le port en lecture seule non sécurisé.
  2. Déployez le fichier manifeste :

    kubectl create -f read-only-port-metrics.yaml
    
  3. Vérifiez les journaux DaemonSet :

    kubectl logs --namespace=node-metrics-printer-namespace \
        --all-containers --prefix \
        --selector=app=node-metrics-printer
    

    Si le résultat contient des entrées avec la chaîne server_type=readonly, cela signifie qu'une application utilise le port non sécurisé en lecture seule.

Vérifier l'utilisation des ports en mode standard

Exécutez la commande suivante sur au moins un nœud de chaque pool de nœuds de votre cluster :

kubectl get --raw /api/v1/nodes/NODE_NAME/proxy/metrics | grep http_requests_total | grep readonly

Remplacez NODE_NAME par le nom du nœud.

Si le résultat contient des entrées avec la chaîne server_type="readonly", les scénarios suivants peuvent se produire :

  • Les charges de travail du nœud utilisent le port accessible en lecture seule non sécurisé du kubelet.
  • Même après la désactivation du port non sécurisé, la commande renvoie toujours la chaîne server_type="readonly". En effet, la métrique kubelet_http_requests_total représente le nombre cumulé de requêtes HTTP reçues par le serveur kubelet depuis son dernier redémarrage. Ce nombre n'est pas réinitialisé lorsque le port non sécurisé est désactivé. Ce nombre est réinitialisé après le redémarrage du serveur kubelet par GKE, par exemple lors d'une mise à niveau de nœud. Pour en savoir plus, consultez la documentation de référence sur les métriques Kubernetes.

Si le résultat est vide, aucune charge de travail sur ce nœud n'utilise le port non sécurisé en lecture seule.

Identifier les charges de travail qui utilisent le port accessible en lecture seule et non sécurisé du kubelet

Pour identifier les charges de travail qui utilisent le port non sécurisé, vérifiez les fichiers de configuration de la charge de travail, tels que les ConfigMaps et les pods.

Exécutez les commandes suivantes :

kubectl get pods --all-namespaces -o yaml | grep 10255
kubectl get configmaps --all-namespaces -o yaml | grep 10255

Si le résultat de la commande n'est pas vide, utilisez le script suivant pour identifier les noms des ConfigMaps ou des pods qui utilisent le port non sécurisé :

# This function checks if a Kubernetes resource is using the insecure port 10255.
#
# Arguments:
#  $1 - Resource type (e.g., pod, configmap, )
#  $2 - Resource name
#  $3 - Namespace
#
# Output:
#  Prints a message indicating whether the resource is using the insecure port.
isUsingInsecurePort() {
  resource_type=$1
  resource_name=$2
  namespace=$3

  config=$(kubectl get $resource_type $resource_name -n $namespace -o yaml)

  # Check if kubectl output is empty
  if [[ -z "$config" ]]; then
    echo "No configuration file detected for $resource_type: $resource_name (Namespace: $namespace)"
    return
  fi

  if echo "$config" | grep -q "10255"; then
    echo "Warning: The configuration file ($resource_type: $namespace/$resource_name) is using insecure port 10255. It is recommended to migrate to port 10250 for enhanced security."
  else
    echo "Info: The configuration file ($resource_type: $namespace/$resource_name) is not using insecure port 10255."
  fi
}

# Get the list of ConfigMaps with their namespaces
configmaps=$(kubectl get configmaps -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name | tail -n +2 | awk '{print $1"/"$2}')

# Iterate over each ConfigMap
for configmap in $configmaps; do
  namespace=$(echo $configmap | cut -d/ -f1)
  configmap_name=$(echo $configmap | cut -d/ -f2)
  isUsingInsecurePort "configmap" "$configmap_name" "$namespace"
done

# Get the list of Pods with their namespaces
pods=$(kubectl get pods -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name | tail -n +2 | awk '{print $1"/"$2}')

# Iterate over each Pod
for pod in $pods; do
  namespace=$(echo $pod | cut -d/ -f1)
  pod_name=$(echo $pod | cut -d/ -f2)
  isUsingInsecurePort "pod" "$pod_name" "$namespace"
done

Une fois que vous avez identifié les charges de travail concernées, migrez-les pour qu'elles utilisent le port sécurisé 10250 en suivant la procédure décrite dans la section suivante.

Migrer depuis le port accessible en lecture seule et non sécurisé du kubelet

En règle générale, la migration d'une application vers le port sécurisé implique les étapes suivantes :

  1. Mettez à jour les URL ou les points de terminaison qui font référence au port en lecture seule non sécurisé afin d'utiliser le port en lecture seule sécurisé à la place. Par exemple, remplacez http://203.0.113.104:10255 par http://203.0.113.104:10250.

  2. Définissez le certificat de l'autorité de certification (CA) du client HTTP sur le certificat CA du cluster. Pour trouver ce certificat, exécutez la commande suivante :

    gcloud container clusters describe CLUSTER_NAME \
        --location=LOCATION \
        --format="value(masterAuth.clusterCaCertificate)"
    

    Remplacez les éléments suivants :

    • CLUSTER_NAME : nom du cluster
    • LOCATION : emplacement de votre cluster.

Le port authentifié 10250 exige que vous accordiez les rôles RBAC appropriés au sujet pour accéder aux ressources spécifiques. Pour en savoir plus, consultez la section Autorisation kubelet de la documentation Kubernetes.

Si votre charge de travail utilise le point de terminaison /pods sur le port en lecture seule non sécurisé du kubelet, vous devez accorder l'autorisation RBAC nodes/proxy d'accéder au point de terminaison sur le port sécurisé du kubelet. nodes/proxy est une autorisation puissante que vous ne pouvez pas accorder dans les clusters GKE Autopilot et que vous ne devez pas accorder dans les clusters GKE Standard. Utilisez plutôt l'API Kubernetes avec un fieldSelector pour le nom du nœud.

Si vous utilisez des applications tierces qui dépendent du port en lecture seule non sécurisé du kubelet, contactez le fournisseur de l'application pour obtenir des instructions sur la migration vers le port sécurisé 10250.

Exemple de migration

Prenons l'exemple d'un pod qui interroge les métriques du port en lecture seule non sécurisé du kubelet.

apiVersion: v1
kind: Pod
metadata:
  name: kubelet-readonly-example
spec:
  restartPolicy: Never
  containers:
  - name: kubelet-readonly-example
    image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest
    command:
      - curl
      - http://${NODE_ADDRESS}:10255/metrics
    env:
    - name: NODE_ADDRESS
      valueFrom:
        fieldRef:
          fieldPath: status.hostIP

Cette application effectue les opérations suivantes :

  • Utilise le compte de service default dans l'espace de noms default.
  • Exécute la commande curl sur le point de terminaison /metrics du nœud.

Pour mettre à jour ce pod afin qu'il utilise le port sécurisé 10250, procédez comme suit :

  1. Créez un objet ClusterRole ayant accès pour obtenir des métriques de nœud :

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: curl-authenticated-role
    rules:
    # Grant access to read node metrics in the cluster.
    - apiGroups:
      - ""
      resources:
      - nodes/metrics
      verbs:
      - get
    
  2. Liez le ClusterRole à l'identité de votre application :

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: curl-authenticated-role-binding
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: curl-authenticated-role
    # Bind the ClusterRole to the default ServiceAccount in the default
    # namespace.
    subjects:
    - kind: ServiceAccount
      name: default
      namespace: default
    
  3. Mettez à jour la commande curl pour utiliser le point de terminaison du port sécurisé avec les en-têtes d'autorisation correspondants :

    apiVersion: v1
    kind: Pod
    metadata:
      name: kubelet-authenticated-example
    spec:
      restartPolicy: Never
      containers:
      - name: kubelet-readonly-example
        image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest
        env:
        - name: NODE_ADDRESS
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        # Update the command to send a request with the ServiceAccount
        # credentials in the header.
        command:
        - sh
        - -c
        - 'curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization:
          Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://${NODE_ADDRESS}:10250/metrics'
    

Modifier des règles de pare-feu VPC

Si vous mettez à jour des charges de travail pour qu'elles utilisent le port 10250, créez des règles de pare-feu afin que les pods du cluster puissent accéder au port dans vos plages d'adresses IP de nœud. Les règles de pare-feu doivent effectuer les opérations suivantes :

  • Autorisez le trafic entrant vers le port TCP 10250 sur les plages d'adresses IP de vos nœuds à partir des plages d'adresses IP de vos pods internes.
  • Refusez le trafic entrant vers le port TCP 10250 sur les plages d'adresses IP de vos nœuds provenant de l'Internet public.

Vous pouvez utiliser les règles de pare-feu GKE par défaut ci-dessous comme modèle pour les paramètres à spécifier dans vos nouvelles règles :

  • gke-[cluster-name]-[cluster-hash]-inkubelet
  • gke-[cluster-name]-[cluster-hash]-exkubelet

Désactiver le port accessible en lecture seule et non sécurisé sur les clusters Autopilot

Vous pouvez désactiver le port accessible en lecture seule et non sécurisé du kubelet pour les clusters Autopilot nouveaux et existants.

Pour désactiver le port accessible en lecture seule et non sécurisé du kubelet sur un cluster Autopilot, utilisez l'option --no-autoprovisioning-enable-insecure-kubelet-readonly-port, comme dans la commande suivante. Tous les nœuds nouveaux et existants du cluster cessent d'utiliser le port.

gcloud container clusters update CLUSTER_NAME \
    --location=LOCATION \
    --no-autoprovisioning-enable-insecure-kubelet-readonly-port

Remplacez les éléments suivants :

  • CLUSTER_NAME : nom de votre cluster existant.
  • LOCATION : emplacement de votre cluster existant.

Vous pouvez également utiliser l'option --no-autoprovisioning-enable-insecure-kubelet-readonly-port lorsque vous créez un cluster à l'aide de la commande gcloud container clusters create-auto.

Désactiver le port accessible en lecture seule et non sécurisé sur les clusters standards

Vous pouvez désactiver le port non sécurisé en lecture seule du kubelet pour des clusters standards ou pour les pools de nœuds individuels. Nous vous recommandons de désactiver le port pour l'ensemble du cluster.

Si vous utilisez le provisionnement automatique des nœuds, les pools de nœuds provisionnés automatiquement héritent du paramètre de port que vous spécifiez au niveau du cluster. Vous pouvez éventuellement spécifier un paramètre différent pour les pools de nœuds provisionnés automatiquement, mais nous vous recommandons de désactiver le port sur tous les nœuds de votre cluster.

Vous pouvez également utiliser un fichier de configuration du système de nœud pour désactiver de manière déclarative le port en lecture seule non sécurisé du kubelet. Si vous utilisez ce fichier, vous ne pouvez pas utiliser les commandes des sections suivantes pour contrôler le paramètre kubelet.

Désactiver le port accessible en lecture seule et non sécurisé sur les clusters Standard existants

Pour désactiver le port accessible en lecture seule et non sécurisé du kubelet sur un cluster Standard existant, utilisez l'option --no-enable-insecure-kubelet-readonly-port comme dans la commande suivante. Les nouveaux pools de nœuds n'utiliseront pas le port non sécurisé. GKE ne met pas à jour automatiquement les pools de nœuds existants.

gcloud container clusters update CLUSTER_NAME \
    --location=LOCATION \
    --no-enable-insecure-kubelet-readonly-port

Remplacez les éléments suivants :

  • CLUSTER_NAME : nom de votre cluster Standard existant.
  • LOCATION : emplacement de votre cluster standard existant.

Vous pouvez également utiliser l'indicateur --no-autoprovisioning-enable-insecure-kubelet-readonly-port lorsque vous créez un cluster à l'aide de la commande gcloud container clusters create.

Désactiver le port accessible en lecture seule et non sécurisé sur les pools de nœuds Standard

Nous vous recommandons de définir le paramètre de port en lecture seule au niveau du cluster dans tous les cas. Si vous avez désactivé le port en lecture seule sur un cluster existant qui comportait déjà des pools de nœuds en cours d'exécution, utilisez la commande suivante pour désactiver le port sur ces pools de nœuds.

gcloud container node-pools update NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --no-enable-insecure-kubelet-readonly-port

Remplacez les éléments suivants :

  • NODE_POOL_NAME : nom de votre pool de nœuds.
  • CLUSTER_NAME : nom du cluster.
  • LOCATION : emplacement du cluster.

Vérifier que le port est désactivé

Pour vérifier que le port accessible en lecture seule et non sécurisé du kubelet est désactivé, décrivez la ressource GKE.

Vérifier l'état du port dans les clusters Autopilot

Exécutez la commande ci-dessous.

gcloud container clusters describe CLUSTER_NAME \
    --location=LOCATION \
    --flatten=nodePoolAutoConfig \
    --format="value(nodeKubeletConfig)"

Remplacez les éléments suivants :

  • CLUSTER_NAME : nom de votre cluster Autopilot.
  • LOCATION : emplacement de votre cluster Autopilot.

Si le port est désactivé, le résultat est le suivant :

insecureKubeletReadonlyPortEnabled: false

Vérifier l'état du port dans les clusters standards

L'état du port est disponible dans le champ nodePoolDefaults.nodeConfigDefaults.nodeKubeletConfig lorsque vous décrivez votre cluster à l'aide de l'API GKE.

Dans les clusters Standard, vous verrez également un champ nodeConfig qui définit une valeur pour l'état du port kubelet en lecture seule. Le champ nodeConfig est obsolète et ne s'applique qu'au pool de nœuds par défaut créé par GKE lorsque vous créez un cluster en mode Standard. L'état du port dans le champ nodeConfig obsolète ne s'applique pas aux autres pools de nœuds du cluster.

Exécutez la commande ci-dessous.

gcloud container clusters describe CLUSTER_NAME \
    --location=LOCATION \
    --flatten=nodePoolDefaults.nodeConfigDefaults \
    --format="value(nodeKubeletConfig)"

Remplacez les éléments suivants :

  • CLUSTER_NAME : nom de votre cluster Standard.
  • LOCATION : emplacement de votre cluster standard.

Si le port est désactivé, le résultat est le suivant :

insecureKubeletReadonlyPortEnabled: false

Si le résultat de cette commande est vide, il est possible que le port kubelet non sécurisé en lecture seule soit toujours activé. Pour désactiver le port, exécutez la commande dans la section Désactiver le port accessible en lecture seule et non sécurisé sur les clusters Standard existants.

Vérifier l'état du port dans les pools de nœuds standards

Exécutez la commande ci-dessous.

gcloud container node-pools describe NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --flatten=config \
    --format="value(kubeletConfig)"

Remplacez les éléments suivants :

  • NODE_POOL_NAME : nom de votre pool de nœuds.
  • CLUSTER_NAME : nom du cluster.
  • LOCATION : emplacement du cluster.

Si le port est désactivé, le résultat est le suivant :

insecureKubeletReadonlyPortEnabled: false

Empêcher l'utilisation de ports non sécurisés avec une règle d'administration

Vous pouvez utiliser le service de règles d'administration pour appliquer la désactivation du port en lecture seule non sécurisé kubelet aux clusters de votre organisation, de votre dossier ou de votre projet. Pour appliquer cette exigence, vous devez créer une contrainte personnalisée, puis faire référence à cette contrainte dans une règle d'administration. Pour refuser les opérations de création ou de mise à jour lorsque le port kubelet accessible en lecture seule et non sécurisé est activé, consultez les exemples suivants :

  • La contrainte personnalisée suivante refuse les opérations de création ou de mise à jour de cluster si le port kubelet accessible en lecture seule et non sécurisé est activé :

    name: organizations/ORGANIZATION_ID/customConstraints/custom.disableClusterInsecureKubeletReadOnlyPort
    resourceTypes:
    - container.googleapis.com/Cluster
    methodTypes:
    - CREATE
    - UPDATE
    condition:
      "resource.NodeKubeletConfig.insecureKubeletReadonlyPortEnabled == true ||
      resource.AutoprovisioningNodePoolDefaults.insecureKubeletReadonlyPortEnabled == true ||
      resource.NodePoolAutoConfig.NodeKubeletConfig.insecureKubeletReadonlyPortEnabled == true"
    actionType: DENY
    displayName: Disable insecure kubelet read-only port
    description: All new and existing clusters must disable the insecure kubelet read-only port.
    

    Remplacez ORGANIZATION_ID par votre ID d'organisation.

  • La contrainte personnalisée suivante refuse les opérations de création ou de mise à jour de pool de nœuds si le port kubelet en lecture seule non sécurisé est activé :

    name: organizations/ORGANIZATION_ID/customConstraints/custom.disableNodePoolInsecureKubeletReadOnlyPort
    resourceTypes:
    - container.googleapis.com/NodePool
    methodTypes:
    - CREATE
    - UPDATE
    condition: "resource.NodeConfig.NodeKubeletConfig.insecureKubeletReadonlyPortEnabled == true"
    actionType: DENY
    displayName: Disable insecure kubelet read-only port
    description: All new and existing node pools must disable the insecure kubelet read-only port.
    

    Remplacez ORGANIZATION_ID par votre ID d'organisation.

Pour savoir comment créer une contrainte personnalisée et l'appliquer dans une règle d'administration, consultez Limiter les actions sur les ressources GKE à l'aide de règles d'administration personnalisées.