Deja de usar el puerto de solo lectura de kubelet no seguro en 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.

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 la CLI de gcloud. Si ya instalaste la CLI de gcloud, ejecuta gcloud components update para obtener la versión más reciente.

Verifica si las aplicaciones usan el puerto

Antes de inhabilitar el puerto, asegúrate de que las aplicaciones de tu clúster no accedan al puerto de solo lectura. Si inhabilitas el puerto mientras una carga de trabajo aún necesita acceso a un extremo, es posible que la carga de trabajo falle.

  1. Obtén una lista de nodos en el clúster:

    kubectl get nodes
    
  2. Para cada nodo, verifica las métricas del servidor HTTP de kubelet:

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

    Si una aplicación envía solicitudes al puerto de solo lectura, el resultado será similar al siguiente:

    kubelet_http_requests_total{long_running="false",method="GET",path="metrics",server_type="readonly"} 2549
    kubelet_http_requests_total{long_running="false",method="GET",path="metrics/probes",server_type="readonly"} 2546
    kubelet_http_requests_total{long_running="false",method="GET",path="pods",server_type="readonly"} 1
    

    Para detener este uso, consulta Evita que las aplicaciones usen el puerto de solo lectura.

    Si el resultado está en blanco, ve a Inhabilita el puerto de solo lectura en el clúster.

Evita que las aplicaciones usen el puerto de solo lectura

Si tienes una aplicación de terceros que usa el puerto de solo lectura, consulta la documentación del proveedor para obtener una opción de usar el puerto kubelet autenticado.

En la mayoría de los casos, debes realizar los siguientes cambios en tu aplicación para evitar que use el puerto de solo lectura:

  1. Actualiza la URL o los extremos que hacen referencia al puerto de solo lectura. Por ejemplo, cambiarías las referencias de extremos de nodo como http://192.0.2.167:10255 a https://192.0.2.167:10250.
  2. Configura el certificado de CA del cliente HTTP como el certificado de CA del clúster. Para obtener este certificado de CA, usa uno de los siguientes métodos:

    • Usa el objeto masterAuth.clusterCaCertificate de la API de GKE.
    • Desde un Pod, accede al certificado en /var/run/secrets/kubernetes.io/serviceaccount/ca.crt.
  3. Actualiza el mecanismo de autorización en el clúster, como las reglas de RBAC, para que la aplicación otorgue los permisos adecuados en el recurso nodes/* o en subrecursos específicos. Para obtener una lista de subrecursos, consulta Autorización de kubelet.

Después de actualizar tus aplicaciones, vuelve a verificar el uso de puertos de solo lectura para verificar que el resultado esté en blanco. Luego, inhabilita el puerto de solo lectura en el clúster.

Ejemplo de migración de aplicaciones

En el siguiente ejemplo, se modifica un Pod que consulta las métricas del puerto de solo lectura de kubelet. Estas solicitudes no están autenticadas, por lo que cualquier aplicación que pueda acceder a los extremos de nodos también puede enviar cualquier consulta.

  1. Guarda el siguiente manifiesto como example-readonly.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: kubelet-readonly-example
    spec:
      restartPolicy: Never
      containers:
      - name: kubelet-readonly-example
        image: gcr.io/cloud-builders/curl:latest
        command:
          - curl
          - http://$(NODE_ADDRESS):10255/metrics
        env:
        - name: NODE_ADDRESS
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
    

    Este Pod ejecuta un comando curl para obtener el extremo /metrics en el puerto de solo lectura de kubelet.

  2. Aplica el manifiesto

    kubectl create -f example-readonly.yaml
    
  3. Verifica los registros del Pod:

    kubectl logs -f kubelet-readonly-example
    

    El resultado contiene métricas de nodo y es similar al siguiente:

    # HELP apiserver_audit_event_total [ALPHA] Counter of audit events generated and sent to the audit backend.
    # TYPE apiserver_audit_event_total counter
    ...
    kubelet_runtime_operations_total{operation_type="container_status"} 93
    ...
    
  4. Crea un ClusterRole que otorgue acceso get a las métricas de nodo:

    1. Guarda el siguiente manifiesto como metrics-role.yaml:

      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRole
      metadata:
        name: curl-authenticated-role
      rules:
      - apiGroups:
        - ""
        resources:
        - nodes/metrics
        verbs:
        - get
      
    2. Aplica el manifiesto

      kubectl create -f metrics-role.yaml
      
  5. Vincula el ClusterRole a la identidad de la aplicación, como ServiceAccount:

    1. Guarda el siguiente manifiesto como metrics-binding.yaml:

      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
      

      En este ejemplo, se vincula la ClusterRole a la ServiceAccount default en el espacio de nombres default.

    2. Aplica el manifiesto

      kubectl create -f metrics-binding.yaml
      
  6. Modifica el manifiesto example-readonly.yaml de la siguiente manera:

    apiVersion: v1
    kind: Pod
    metadata:
      name: kubelet-authenticated-example
    spec:
      restartPolicy: Never
      containers:
      - name: kubelet-readonly-example
        image: gcr.io/cloud-builders/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'
    

    El comando que el Pod envía a la API de kubelet incluye lo siguiente:

    • El certificado de CA del clúster ubicado en /var/run/secrets/kubernetes.io/ca.crt para autenticar la solicitud.
    • El token del portador de ServiceAccount ubicado en /var/run/secrets/kubernetes.io/serviceaccount/token para autorizar la solicitud.
  7. Implementa el manifiesto actualizado:

    kubectl apply -f example-readonly.yaml
    

Inhabilita el puerto de solo lectura en el clúster

Puedes inhabilitar el puerto de solo lectura de kubelet no seguro en clústeres de Standard nuevos o existentes, y en clústeres de Autopilot nuevos. No puedes inhabilitar el puerto en clústeres de Autopilot existentes.

Se aplican los siguientes requisitos de versión:

  • Solo puedes inhabilitar el puerto a nivel del clúster en la versión 1.26.4-gke.500 de GKE y versiones posteriores, y en la versión 1.27 y posteriores.
  • Debes usar la versión 439 o una versión posterior de la CLI de gcloud.
  1. Asegúrate de tener la última versión de la CLI de gcloud. Para actualizar la CLI de gcloud, consulta Instala la CLI de gcloud.

  2. Inhabilita el puerto para todos los nodos del clúster:

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

    Reemplaza lo siguiente:

    • CLUSTER_NAME: el nombre del clúster
    • LOCATION_NAME: es la región o zona de Compute Engine del clúster, como us-central1.

    Este comando anula cualquier configuración a nivel de nodo para el puerto de solo lectura, incluidos los especificados en el archivo de configuración de kubelet.

    Para inhabilitar el puerto cuando creas un clúster nuevo, especifica la marca --no-enable-insecure-kubelet-readonly-port en tu comando create.

Verifica que el puerto esté inhabilitado

Para verificar que el puerto esté inhabilitado, usa el manifiesto de la sección Migración de ejemplo.

  1. Guarda el siguiente manifiesto como readonly-verify.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: kubelet-readonly-verify
    spec:
      restartPolicy: Never
      containers:
      - name: kubelet-readonly-verify
        image: gcr.io/cloud-builders/curl:latest
        command:
          - curl
          - http://$(NODE_ADDRESS):10255/metrics
        env:
        - name: NODE_ADDRESS
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
    
  2. Aplica el manifiesto

    kubectl create -f readonly-verify.yaml
    
  3. Verifica los registros del Pod:

    kubectl logs -f kubelet-readonly-verify
    

Si el puerto está inhabilitado, verás un error similar al siguiente:

curl: (7) Failed to connect to NODE_ADDRESS port 10255: Connection refused

Si el puerto aún está habilitado, verás un resultado de métricas del nodo similar al siguiente:

# HELP apiserver_audit_event_total [ALPHA] Counter of audit events generated and sent to the audit backend.
# TYPE apiserver_audit_event_total counter
...
kubelet_runtime_operations_total{operation_type="container_status"} 93
...

En este caso, asegúrate de que tu clúster cumpla con los requisitos de la versión y que uses la versión más reciente de la CLI de gcloud.

¿Qué sigue?