Inhabilitar el puerto de solo lectura de kubelet en clústeres de GKE


En esta página se explica cómo inhabilitar el puerto de solo lectura de kubelet no seguro en clústeres de Google Kubernetes Engine (GKE) para reducir el riesgo de acceso no autorizado a kubelet y cómo migrar aplicaciones a un puerto más seguro.

En los clústeres de Kubernetes, incluido GKE, el proceso kubelet que se ejecuta en los nodos sirve una API de solo lectura mediante el puerto no seguro 10255. Kubernetes no realiza ninguna comprobación de autenticación ni de autorización en este puerto. El kubelet ofrece los mismos endpoints en el puerto 10250, que es más seguro y autenticado.

Inhabilita el puerto de solo lectura de kubelet y cambia las cargas de trabajo que usen el puerto 10255 para que usen el puerto 10250, que es más seguro.

Antes de empezar

Antes de empezar, asegúrate de haber realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si quieres usar Google Cloud CLI para esta tarea, instálala y, a continuación, inicialízala. Si ya has instalado la gcloud CLI, obtén la versión más reciente ejecutando gcloud components update.

Requisitos

  • Solo puedes inhabilitar el puerto de solo lectura de kubelet no seguro en GKE 1.26.4-gke.500 o versiones posteriores.

Comprobar el uso de puertos no seguros y migrar aplicaciones

Antes de inhabilitar el puerto de solo lectura no seguro, migra las aplicaciones en ejecución que lo utilicen al puerto de solo lectura más seguro. Entre las cargas de trabajo que pueden necesitar migración se incluyen las canalizaciones de métricas personalizadas y las cargas de trabajo que acceden a los endpoints de kubelet.

  • En el caso de las cargas de trabajo que necesiten acceder a la información que proporciona la API kubelet en el nodo, como las métricas, usa el puerto 10250.
  • En el caso de las cargas de trabajo que obtienen información de Kubernetes mediante la API de kubelet en el nodo, como la lista de pods del nodo, usa la API de Kubernetes.

Comprobar si las aplicaciones usan el puerto de solo lectura de kubelet no seguro

En esta sección se explica cómo comprobar si se usan puertos no seguros en tu clúster.

Comprobar el uso de puertos en el modo Autopilot

Para comprobar el uso de puertos en un clúster de Autopilot, asegúrate de que tienes al menos una carga de trabajo que no sea un DaemonSet ejecutándose en el clúster. Si sigues estos pasos en un clúster de Autopilot vacío, los resultados podrían no ser válidos.

  1. Guarda el siguiente archivo de manifiesto como 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
    

    Este archivo de manifiesto hace lo siguiente:

    1. Crea un espacio de nombres y configura roles de RBAC para permitir la lectura de métricas de nodos.
    2. Despliega un DaemonSet que comprueba las métricas de kubelet del puerto de solo lectura no seguro.
  2. Implementa el archivo de manifiesto:

    kubectl create -f read-only-port-metrics.yaml
    
  3. Consulta los registros de DaemonSet:

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

    Si el resultado contiene alguna cadena server_type=readonly, significa que una aplicación está usando el puerto de solo lectura no seguro.

Comprobar el uso de puertos en el modo Estándar

Ejecuta el siguiente comando en al menos un nodo de cada grupo de nodos de tu clúster:

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

Sustituye NODE_NAME por el nombre del nodo.

Si el resultado contiene entradas con la cadena server_type="readonly", pueden darse los siguientes casos:

  • Las cargas de trabajo del nodo usan el puerto de solo lectura de kubelet no seguro.
  • Después de inhabilitar el puerto no seguro, el comando sigue devolviendo la cadena server_type="readonly". Esto se debe a que la métrica kubelet_http_requests_total representa el número acumulado de solicitudes HTTP recibidas por el servidor kubelet desde su último reinicio. Este número no se restablece cuando se inhabilita el puerto no seguro. Este número se restablece después de que GKE reinicie el servidor kubelet, por ejemplo, durante una actualización de nodos. Para obtener más información, consulta la referencia de métricas de Kubernetes.

Si la salida está vacía, ninguna carga de trabajo de ese nodo usa el puerto de solo lectura no seguro.

Identifica las cargas de trabajo que usan el puerto de solo lectura de kubelet no seguro.

Para identificar las cargas de trabajo que usan el puerto no seguro, consulta los archivos de configuración de la carga de trabajo, como ConfigMaps y Pods.

Ejecuta estos comandos:

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

Si la salida del comando no está vacía, usa la siguiente secuencia de comandos para identificar los nombres de los ConfigMaps o los pods que usan el puerto no seguro:

# 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

Una vez que haya identificado las cargas de trabajo pertinentes, migre para usar el puerto seguro 10250 siguiendo los pasos que se indican en la siguiente sección.

Migrar desde el puerto de solo lectura de kubelet no seguro

Por lo general, la migración de una aplicación al puerto seguro implica los siguientes pasos:

  1. Actualiza las URLs o los endpoints que hagan referencia al puerto de solo lectura no seguro para que usen el puerto de solo lectura seguro. Por ejemplo, cambia http://203.0.113.104:10255 por http://203.0.113.104:10250.

  2. Define el certificado de la autoridad de certificación (CA) del cliente HTTP como el certificado de la CA del clúster. Para encontrar este certificado, ejecuta el siguiente comando:

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

    Haz los cambios siguientes:

    • CLUSTER_NAME: el nombre de tu clúster.
    • LOCATION: la ubicación de tu clúster.

El puerto autenticado 10250 requiere que concedas los roles de RBAC adecuados al sujeto para acceder a los recursos específicos. Para obtener más información, consulta la sección sobre autorización de kubelet en la documentación de Kubernetes.

Si tu carga de trabajo usa el endpoint /pods en el puerto de solo lectura de kubelet inseguro, debes conceder el permiso nodes/proxy RBAC para acceder al endpoint en el puerto de kubelet seguro. nodes/proxy es un permiso potente que no puedes conceder en clústeres de Autopilot de GKE y que no deberías conceder en clústeres estándar de GKE. En su lugar, usa la API de Kubernetes con un fieldSelector para el nombre del nodo.

Si usas aplicaciones de terceros que dependen del puerto de solo lectura inseguro de kubelet, ponte en contacto con el proveedor de la aplicación para obtener instrucciones sobre cómo migrar al puerto seguro 10250.

Ejemplo de migración

Imaginemos un pod que consulta métricas del puerto de solo lectura de kubelet no seguro.

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

Esta aplicación hace lo siguiente:

  • Usa el recurso default ServiceAccount en el espacio de nombres default.
  • Ejecuta el comando curl en el endpoint /metrics del nodo.

Para actualizar este pod de forma que use el puerto seguro 10250, sigue estos pasos:

  1. Crea un ClusterRole con acceso para obtener métricas de nodos:

    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. Vincula el ClusterRole a la identidad de tu aplicación:

    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. Actualiza el comando curl para usar el endpoint del puerto seguro con los encabezados de autorización correspondientes:

    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'
    

Modificar reglas de cortafuegos de VPC

Si actualizas las cargas de trabajo para que usen el puerto 10250, crea reglas de cortafuegos para que los pods del clúster puedan acceder al puerto en tus intervalos de direcciones IP de nodo. Las reglas del cortafuegos deben hacer lo siguiente:

  • Permitir el tráfico entrante al puerto TCP 10250 en tus intervalos de direcciones IP de nodos desde intervalos de direcciones IP de pods internos
  • Deniega el tráfico entrante al puerto TCP 10250 en los intervalos de direcciones IP de tu nodo desde la red pública de Internet.

Puedes usar las siguientes reglas de cortafuegos predeterminadas de GKE como plantilla para los parámetros que quieras especificar en tus nuevas reglas:

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

Inhabilitar el puerto de solo lectura no seguro en clústeres de Autopilot

Puedes inhabilitar el puerto de solo lectura de kubelet no seguro en clústeres de Autopilot nuevos y actuales.

Para inhabilitar el puerto de solo lectura de kubelet no seguro en un clúster de Autopilot, usa la marca --no-autoprovisioning-enable-insecure-kubelet-readonly-port, como en el siguiente comando. Todos los nodos nuevos y actuales del clúster dejan de usar el puerto.

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

Haz los cambios siguientes:

  • CLUSTER_NAME: el nombre del clúster.
  • LOCATION: la ubicación de tu clúster.

También puedes usar la marca --no-autoprovisioning-enable-insecure-kubelet-readonly-port cuando crees un clúster con el comando gcloud container clusters create-auto.

Inhabilitar el puerto de solo lectura no seguro en clústeres estándar

Puedes inhabilitar el puerto de solo lectura de kubelet no seguro en todo el clúster Standard o en grupos de nodos concretos. Te recomendamos que inhabilite el puerto en todo el clúster.

Si usas el aprovisionamiento automático de nodos, los grupos de nodos aprovisionados automáticamente heredarán el ajuste de puerto que especifiques a nivel de clúster. También puedes especificar otro ajuste para los grupos de nodos aprovisionados automáticamente, pero te recomendamos que inhabilites el puerto en todos los nodos del clúster.

También puedes usar un archivo de configuración del sistema de nodos para inhabilitar de forma declarativa el puerto de solo lectura de kubelet no seguro. Si usas este archivo, no podrás usar los comandos de las secciones siguientes para controlar el ajuste de kubelet.

Inhabilitar el puerto de solo lectura no seguro en clústeres Standard

Para inhabilitar el puerto de solo lectura de kubelet no seguro en un clúster Standard, usa la marca --no-enable-insecure-kubelet-readonly-port como en el siguiente comando. Los nuevos grupos de nodos no usarán el puerto no seguro. GKE no actualiza automáticamente los grupos de nodos.

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

Haz los cambios siguientes:

  • CLUSTER_NAME: el nombre del clúster Standard.
  • LOCATION: la ubicación de tu clúster Standard.

También puedes usar la marca --no-autoprovisioning-enable-insecure-kubelet-readonly-port cuando crees un clúster con el comando gcloud container clusters create.

Inhabilitar el puerto de solo lectura no seguro en los grupos de nodos Standard

Te recomendamos que definas el puerto de solo lectura a nivel de clúster en todos los casos. Si has inhabilitado el puerto de solo lectura en un clúster que ya tenía grupos de nodos en ejecución, usa el siguiente comando para inhabilitar el puerto en esos grupos de nodos.

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

Haz los cambios siguientes:

  • NODE_POOL_NAME: el nombre de tu grupo de nodos.
  • CLUSTER_NAME: el nombre del clúster.
  • LOCATION: la ubicación del clúster.

Verificar que el puerto esté inhabilitado

Para verificar que el puerto de solo lectura de kubelet no seguro está inhabilitado, describe el recurso de GKE.

Comprobar el estado del puerto en clústeres de Autopilot

Ejecuta el siguiente comando:

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

Haz los cambios siguientes:

  • CLUSTER_NAME: el nombre de tu clúster de Autopilot.
  • LOCATION: la ubicación de tu clúster de Autopilot.

Si el puerto está inhabilitado, el resultado es el siguiente:

insecureKubeletReadonlyPortEnabled: false

Comprobar el estado del puerto en clústeres estándar

El estado del puerto está disponible en el campo nodePoolDefaults.nodeConfigDefaults.nodeKubeletConfig cuando describes tu clúster mediante la API de GKE.

En los clústeres estándar, también verá un campo nodeConfig que define un valor para el estado del puerto de solo lectura de kubelet. El campo nodeConfig está obsoleto y solo se aplica al grupo de nodos predeterminado que crea GKE al crear un clúster en modo estándar. El estado del puerto en el campo nodeConfig obsoleto no se aplica a otros grupos de nodos del clúster.

Ejecuta el siguiente comando:

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

Haz los cambios siguientes:

  • CLUSTER_NAME: el nombre de tu clúster Standard.
  • LOCATION: la ubicación de tu clúster Standard.

Si el puerto está inhabilitado, el resultado es el siguiente:

insecureKubeletReadonlyPortEnabled: false

Si la salida de este comando está en blanco, es posible que el puerto de solo lectura kubelet no seguro siga habilitado. Para inhabilitar el puerto, ejecuta el comando de la sección Inhabilitar el puerto de solo lectura no seguro en clústeres estándar.

Comprobar el estado del puerto en los grupos de nodos estándar

Ejecuta el siguiente comando:

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

Haz los cambios siguientes:

  • NODE_POOL_NAME: el nombre de tu grupo de nodos.
  • CLUSTER_NAME: el nombre del clúster.
  • LOCATION: la ubicación del clúster.

Si el puerto está inhabilitado, el resultado es el siguiente:

insecureKubeletReadonlyPortEnabled: false