Inhabilita el puerto de solo lectura de kubelet en los clústeres de GKE


En esta página, se muestra 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 de kubelet que se ejecuta en los nodos entrega una API de solo lectura que usa el puerto no seguro 10255. Kubernetes no realiza ninguna verificación de autenticación ni de autorización en este puerto. Kubelet entrega los mismos extremos en el puerto 10250 autenticado y más seguro.

Inhabilita el puerto de solo lectura de kubelet y cambia las cargas de trabajo que usan el puerto 10255 para usar el puerto más seguro 10250 en su lugar.

Antes de comenzar

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

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si deseas usar Google Cloud CLI para esta tarea, instala y, luego, inicializa gcloud CLI. Si ya instalaste gcloud CLI, ejecuta gcloud components update para obtener la versión más reciente.

Requisitos

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

Verifica el uso de puertos no seguros y migra las aplicaciones

Antes de inhabilitar el puerto de solo lectura no seguro, migra cualquiera de las aplicaciones en ejecución que usen el puerto al puerto de solo lectura más seguro. Las cargas de trabajo que podrían necesitar migración incluyen canalizaciones de métricas personalizadas y cargas de trabajo que acceden a extremos de kubelet.

  • Para las cargas de trabajo que necesitan acceso a la información que entrega la API de 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 con la API de kubelet en el nodo, como enumerar Pods en el nodo, usa la API de Kubernetes.

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

En esta sección, se muestra cómo verificar si hay un uso inseguro de puertos en tu clúster.

Verifica el uso de puertos en el modo Autopilot

Para verificar el uso de puertos en un clúster de Autopilot, asegúrate de tener al menos una carga de trabajo que no sea un DaemonSet en ejecución en el clúster. Si realizas los siguientes pasos en un clúster de Autopilot vacío, es posible que los resultados no sean válidos.

  1. Guarda el siguiente manifiesto como read-only-port-metrics.yaml:

    apiVersion: v1
    kind: Namespace
    metadata:
      name: node-metrics-printer-namespace
    ---
    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
    subjects:
    - kind: ServiceAccount
      name: node-metrics-printer-sa
      namespace: node-metrics-printer-namespace
    ---
    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:
          serviceAccountName: node-metrics-printer-sa
          containers:
          - name: metrics-printer
            image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest
            command: ["sh", "-c"]
            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:
            - 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 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. Implementa un DaemonSet que verifica las métricas de kubelet en busca del puerto de solo lectura no seguro.
  2. Implementa el manifiesto:

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

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

    Si el resultado tiene algún resultado que contenga la cadena server_type=readonly, una aplicación está usando el puerto de solo lectura no seguro.

Verifica 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

Reemplaza NODE_NAME por el nombre del conjunto de datos.

Si el resultado contiene entradas con la cadena server_type="readonly", pueden ocurrir las siguientes situaciones:

  • Las cargas de trabajo en el nodo usan el puerto de solo lectura no seguro de kubelet.
  • Después de inhabilitar el puerto no seguro, el comando aún muestra la cadena server_type="readonly". Esto se debe a que la métrica kubelet_http_requests_total representa la cantidad acumulada de solicitudes HTTP que recibió el servidor de 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 reinicia el servidor de kubelet, por ejemplo, durante una actualización de nodo. Para obtener más información, consulta la Referencia de métricas de Kubernetes.

Si el resultado está vacío, ninguna carga de trabajo en ese nodo usa el puerto de solo lectura no seguro.

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

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

Ejecute los siguientes comandos:

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

Si el resultado del comando no está vacío, usa la siguiente secuencia de comandos para identificar los nombres de los ConfigMaps o 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 hayas identificado las cargas de trabajo relevantes, mígralas para que usen el puerto seguro 10250. Para ello, completa los pasos que se indican en la siguiente sección.

Migra desde el puerto de solo lectura no seguro de kubelet

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

  1. Actualiza las URLs o los extremos que hacen referencia al puerto de solo lectura no seguro para usar el puerto de solo lectura seguro. Por ejemplo, cambia http://203.0.113.104:10255 a http://203.0.113.104:10250.

  2. Configura el certificado de la autoridad certificadora (AC) del cliente HTTP como el certificado de AC del clúster. Para encontrar este certificado, ejecuta el siguiente comando:

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

    Reemplaza lo siguiente:

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

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

Si tu carga de trabajo usa el extremo /pods en el puerto de solo lectura no seguro de kubelet, debes otorgar el permiso de RBAC nodes/proxy para acceder al extremo en el puerto seguro de kubelet. nodes/proxy es un permiso potente que no puedes otorgar en los clústeres de GKE Autopilot y que no deberías otorgar en los clústeres de GKE Standard. 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 no seguro de kubelet, consulta al proveedor de la aplicación para obtener instrucciones para migrar al puerto seguro 10250.

Ejemplo de migración

Considera un Pod que consulta métricas desde el puerto de solo lectura no seguro de 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

Esta aplicación hace lo siguiente:

  • Usa la cuenta de servicio default en el espacio de nombres default.
  • Ejecuta el comando curl en el extremo /metrics del nodo.

Para actualizar este Pod y que use el puerto seguro 10250, sigue estos pasos:

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

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: curl-authenticated-role
    rules:
    - 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
    subjects:
    - kind: ServiceAccount
      name: default
      namespace: default
    
  3. Actualiza el comando curl para usar el extremo de 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
        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'
    

Modifica las reglas de firewall de VPC

Si actualizas las cargas de trabajo para usar el puerto 10250, crea reglas de firewall para que los Pods del clúster puedan alcanzar el puerto en los rangos de direcciones IP de tu nodo. Las reglas del firewall deben hacer lo siguiente:

  • Permite el tráfico entrante al puerto TCP 10250 en los rangos de direcciones IP de tu nodo desde los rangos de direcciones IP internos del Pod.
  • Rechaza el tráfico entrante al puerto TCP 10250 en los rangos de direcciones IP de tu nodo desde Internet pública.

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

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

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

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

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

Para inhabilitar el puerto de solo lectura de kubelet no seguro cuando creas un clúster de Autopilot nuevo, usa la marca --no-autoprovisioning-enable-insecure-kubelet-readonly-port, como en el siguiente comando:

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

Reemplaza lo siguiente:

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

Inhabilita el puerto de solo lectura no seguro en los clústeres de Autopilot existentes

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

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

Reemplaza lo siguiente:

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

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

Puedes inhabilitar el puerto de solo lectura no seguro de kubelet para clústeres estándar completos o para grupos de nodos individuales. Te recomendamos que inhabilites el puerto para todo el clúster.

Si usas el aprovisionamiento automático de nodos, los grupos de nodos aprovisionados automáticamente heredan el parámetro de configuración de puerto que especificas a nivel del clúster. De manera opcional, puedes especificar un parámetro de configuración diferente 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 no seguro de kubelet. Si usas este archivo, no podrás usar los comandos de las siguientes secciones para controlar la configuración de kubelet.

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

Para inhabilitar el puerto de solo lectura de kubelet no seguro en un clúster estándar nuevo, usa la marca --no-enable-insecure-kubelet-readonly-port como en el siguiente comando:

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

Reemplaza lo siguiente:

  • CLUSTER_NAME: es el nombre del clúster estándar nuevo.
  • LOCATION: es la ubicación del nombre del clúster estándar nuevo.

De forma opcional, puedes agregar la marca --no-autoprovisioning-enable-insecure-kubelet-readonly-port para controlar por separado la configuración de aprovisionamiento automático de nodos, pero no recomendamos este enfoque. Esta marca inicia una actualización progresiva de tus grupos de nodos aprovisionados de forma automática, lo que puede causar interrupciones en tus cargas de trabajo en ejecución.

Inhabilita el puerto de solo lectura no seguro en los clústeres estándar existentes

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

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

Reemplaza lo siguiente:

  • CLUSTER_NAME: Es el nombre de tu clúster estándar existente.
  • LOCATION: Es la ubicación de tu clúster estándar existente.

Inhabilita el puerto de solo lectura no seguro en los grupos de nodos estándar

Te recomendamos que configures el puerto de solo lectura a nivel del clúster en todos los casos. Si inhabilitaste el puerto de solo lectura en un clúster existente 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

Reemplaza lo siguiente:

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

Verifica que el puerto esté inhabilitado

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

Verifica el estado de los puertos en los clústeres de Autopilot

Ejecuta el siguiente comando:

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

Reemplaza lo siguiente:

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

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

insecureKubeletReadonlyPortEnabled: false

Verifica el estado de los puertos en los clústeres estándar

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

En los clústeres estándar, también verás un campo nodeConfig que establece 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 GKE crea cuando creas un clúster nuevo 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)"

Reemplaza lo siguiente:

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

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

insecureKubeletReadonlyPortEnabled: false

Verifica el estado de los puertos 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)"

Reemplaza lo siguiente:

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

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

insecureKubeletReadonlyPortEnabled: false