Prácticas recomendadas para GKE RBAC


En este documento, se describen las prácticas recomendadas para planificar las políticas de control de acceso basado en roles (RBAC). Para aprender a implementar el RBAC en Google Kubernetes Engine (GKE), consulta Configura el control de acceso basado en roles.

RBAC es una función de seguridad principal en Kubernetes que te permite crear permisos detallados para administrar qué acciones pueden realizar los usuarios y las cargas de trabajo en los recursos de los clústeres. Como administrador de la plataforma, creas roles de RBAC y vinculas esos roles a los sujetos, que son usuarios autenticados, como cuentas de servicio o Grupos de Google.

Antes de comenzar

Antes de leer este documento, asegúrate de estar familiarizado con los siguientes conceptos:

Para obtener una lista de tareas de esta guía, consulta Lista de tareas resumida.

Cómo funciona RBAC

RBAC admite los siguientes tipos de roles y vinculaciones:

  • ClusterRole: un conjunto de permisos que se puede aplicar a cualquier espacio de nombres o a todo el clúster.
  • Role: Un conjunto de permisos que se limita a un solo espacio de nombres.
  • ClusterRoleBinding: Asigna un objeto ClusterRole a un usuario o grupo para todos los espacios de nombres del clúster.
  • RoleBinding: Asigna un objeto Role o ClusterRole a un usuario o grupo en un espacio de nombres específico.

Los permisos se definen como rules en un Role o un ClusterRole. Cada campo rules en un rol consta de un grupo de API, los recursos de API dentro de ese grupo y los verbos (acciones) permitidos en esos recursos. De forma opcional, puedes definir el alcance de los verbos a las instancias con nombre de recursos de API mediante el campo resourceNames. Para ver un ejemplo, consulta Restringe el acceso a instancias de recursos específicas.

Después de definir un rol, debes usar RoleBinding o ClusterRoleBinding para vincular el rol a un sujeto. Elige el tipo de vinculación según si deseas otorgar permisos en un solo espacio de nombres o en varios.

Diseño del rol de RBAC

Usa el principio de privilegio mínimo

Cuando asignes permisos en un rol de RBAC, usa el principio de privilegio mínimo y otorga los permisos mínimos necesarios para realizar una tarea. Con el uso del principio de privilegio mínimo, se reduce el potencial de elevación de privilegios si tu clúster se ve comprometido y reduce la probabilidad de que un acceso desmedido provoque un incidente de seguridad.

Cuando diseñes tus roles, considera con cuidado los riesgos de elevación de privilegios comunes, como los verbos escalate o bind, el acceso create para PersistentVolumes o el acceso create para solicitudes de firma de certificados. Para obtener una lista de riesgos, consulta RBAC de Kubernetes: Riesgos de elevación de privilegios.

Evita los roles y los grupos predeterminados

Kubernetes crea un conjunto de ClusterRoles y ClusterRoleBindings predeterminados que puedes usar para el descubrimiento de API y habilitar la funcionalidad de componentes administrados. Los permisos otorgados por estos roles predeterminados pueden ser extensos según el rol. Kubernetes también tiene un conjunto de usuarios y grupos de usuarios predeterminados, que se identifican con el prefijo system:. De forma predeterminada, Kubernetes y GKE vinculan automáticamente estos roles a los grupos predeterminados y a varios sujetos. Para obtener una lista completa de las vinculaciones y los roles predeterminados que crea Kubernetes, consulta Vinculaciones de roles y roles predeterminados.

En la siguiente tabla, se describen algunos roles, usuarios y grupos predeterminados. Te recomendamos que evites interactuar con estos roles, usuarios y grupos, a menos que las hayas evaluado con cuidado, ya que interactuar con estos recursos puede tener consecuencias no deseadas para la postura de seguridad de tu clúster.

Nombre Tipo Descripción
cluster-admin ClusterRole Otorga permiso a un sujeto para que realice cualquier acción en cualquier recurso del clúster.
system:anonymous Usuario

Kubernetes asigna este usuario a las solicitudes del servidor de la API que no proporcionan información de autenticación.

Vincular un rol a este usuario le otorga a cualquier usuario no autenticado los permisos que otorga ese rol.

system:unauthenticated Grupo

Kubernetes asigna este grupo a las solicitudes del servidor de la API que no proporcionan información de autenticación.

Cuando se vincula un rol a este grupo, se otorga a cualquier usuario no autenticado los permisos que otorga ese rol.

system:authenticated Grupo

GKE asigna este grupo a las solicitudes del servidor de la API realizadas por cualquier usuario que haya accedido con una Cuenta de Google, incluidas todas las cuentas de Gmail. En la práctica, esto no es muy diferente de system:unauthenticated porque cualquiera puede crear una Cuenta de Google.

Cuando se vincula un rol a este grupo, se otorga a cualquier usuario con una Cuenta de Google, incluidas todas las cuentas de Gmail, los permisos otorgados por ese rol.

system:masters Grupo

Kubernetes asigna el ClusterRole cluster-admin a este grupo de forma predeterminada para habilitar la funcionalidad del sistema.

Agregar tus propios sujetos a este grupo les da acceso a ellos para que realicen cualquier acción en cualquier recurso del clúster.

Si es posible, evita crear vinculaciones que involucren a los usuarios, roles y grupos predeterminados. Esto puede tener consecuencias no deseadas para la postura de seguridad de tu clúster. Por ejemplo:

  • Vincular el ClusterRole cluster-admin predeterminado al grupo system:unauthenticated otorga a los usuarios no autenticados acceso a todos los recursos del clúster (incluidos los Secrets). Estas vinculaciones con muchos privilegios se orientan activamente a ataques como campañas de software malicioso masivas.
  • Vincular un rol personalizado al grupo system:unauthenticated brinda a los usuarios no autenticados los permisos que otorga ese rol.

Cuando sea posible, usa los siguientes lineamientos:

  • No agregues tus propios sujetos al grupo system:masters.
  • No vincules el grupo system:unauthenticated a ningún rol de RBAC.
  • No vincules el grupo system:authenticated a ningún rol de RBAC.
  • No vincules el grupo system:anonymous a ningún rol de RBAC.
  • No vincules el ClusterRole cluster-admin a tus propios sujetos ni a ninguno de los usuarios y grupos predeterminados. Si tu aplicación requiere muchos permisos, determina los permisos exactos necesarios y crea un rol específico para ese propósito.
  • Evalúa los permisos otorgados por otros roles predeterminados antes de vincular sujetos.
  • Evalúa los roles vinculadas a los grupos predeterminados antes de modificar los miembros de esos grupos.

Detecta y evita el uso de roles y grupos predeterminados

Debes evaluar tus clústeres para identificar si vinculaste al usuario system:anonymous o a los grupos system:unauthenticated o system:authenticated mediante ClusterRoleBindings y RoleBindings.

ClusterRoleBindings
  1. Muestra una lista de los nombres de cualquier ClusterRoleBindings con el asunto system:anonymous, system:unauthenticated o system:authenticated:

    kubectl get clusterrolebindings -o json \
      | jq -r '["Name"], ["-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
    

    El resultado debe incluir solo los siguientes ClusterRoleBindings:

    Name
    ----
    "system:basic-user"
    "system:discovery"
    "system:public-info-viewer"
    

    Si el resultado contiene vinculaciones no predeterminadas adicionales, realiza lo siguiente para cada vinculación adicional. Si tu resultado no contiene vinculaciones no predeterminadas, omite los siguientes pasos.

  2. Enumera los permisos de la función asociada con la vinculación:

    kubectl get clusterrolebinding CLUSTER_ROLE_BINDING_NAME -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml'
    

    Reemplaza CLUSTER_ROLE_BINDING_NAME por el nombre del objeto ClusterRoleBinding no predeterminado.

    El resultado es similar al siguiente:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

    Si determinas que los permisos en el resultado son seguros para otorgar a los usuarios o grupos predeterminados, no es necesario que realices ninguna otra acción. Si determinas que los permisos otorgados por la vinculación no son seguros, continúa con el siguiente paso.

  3. Borra una vinculación no segura de tu clúster:

    kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
    

    Reemplaza CLUSTER_ROLE_BINDING_NAME por el nombre de la ClusterRoleBinding que deseas borrar.

RoleBindings
  1. Enumera el espacio de nombres y el nombre de cualquier RoleBinding con el asunto system:anonymous, system:unauthenticated o system:authenticated:

    kubectl get rolebindings -A -o json \
      | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
    

    Si tu clúster está configurado de forma correcta, el resultado debe estar en blanco. Si el resultado contiene vinculaciones no predeterminadas, realiza los siguientes pasos para cada vinculación adicional. Si el resultado está en blanco, omite los siguientes pasos.

    Si solo conoces el nombre del RoleBinding, puedes usar el siguiente comando para encontrar RoleBindings coincidentes en todos los espacios de nombres:

    kubectl get rolebindings -A -o json \
      | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(.metadata.name == "ROLE_BINDING_NAME") | [.metadata.namespace, .metadata.name]) | @tsv'
    

    Reemplaza ROLE_BINDING_NAME por el nombre de la RoleBinding no predeterminada.

  2. Enumera los permisos del rol asociado con la vinculación:

    kubectl get rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml --namespace ROLE_BINDING_NAMESPACE'
    

    Reemplaza lo siguiente:

    • ROLE_BINDING_NAME: El nombre del RoleBinding no predeterminado.
    • ROLE_BINDING_NAMESPACE: El espacio de nombres del RoleBinding no predeterminado.

    El resultado es similar al siguiente:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

    Si determinas que los permisos en el resultado son seguros para otorgar a los usuarios o grupos predeterminados, no es necesario que realices ninguna otra acción. Si determinas que los permisos otorgados por la vinculación no son seguros, continúa con el siguiente paso.

  3. Borra una vinculación no segura de tu clúster:

    kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
    

    Reemplaza lo siguiente:

    • ROLE_BINDING_NAME: El nombre del RoleBinding que se borrará.
    • ROLE_BINDING_NAMESPACE: Es el espacio de nombres del RoleBinding que se borrará.

Permisos de alcance al nivel del espacio de nombres

Usa las vinculaciones y los roles de la siguiente manera, según las necesidades de la carga de trabajo o el usuario:

  • Para otorgar acceso a los recursos en un espacio de nombres, usa un Role con una RoleBinding.
  • Para otorgar acceso a los recursos en más de un espacio de nombres, usa un ClusterRole con una RoleBinding para cada espacio de nombres.
  • Para otorgar acceso a los recursos en todos los espacios de nombres, usa un ClusterRole con una ClusterRoleBinding.

Otorga permisos en la menor cantidad de espacios de nombres posible.

No uses comodines

El carácter * es un comodín que se aplica a todo. Evita usar comodines en tus reglas. Especifica de forma explícita grupos de API, recursos y verbos en las reglas de RBAC. Por ejemplo, si especificas * en el campo verbs, se otorgarían los permisos get, list, watch, patch, update, deletecollection y delete en los recursos. En la siguiente tabla, se muestran ejemplos para evitar comodines en tus reglas:

Recomendado No recomendado
- rules:
    apiGroups: ["apps","extensions"]
    resources: ["deployments"]
    verbs: ["get","list","watch"]

Otorga los verbos get, list y watch específicamente a los grupos de API apps y extensions.

- rules:
    apiGroups: ["*"]
    resources: ["deployments"]
    verbs: ["get","list","watch"]

Otorga los verbos a deployments en cualquier grupo de API.

- rules:
    apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]

Otorga solo los verbos get, list y watch a las implementaciones en los grupos de API apps y extensions.

- rules:
    apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["*"]

Otorga todos los verbos, incluidos patch o delete.

Usa reglas independientes para otorgar acceso de privilegios mínimos a recursos específicos

Cuando planifiques tus reglas, prueba los siguientes pasos de alto nivel para lograr un diseño de reglas con menos privilegios más eficiente en cada rol:

  1. Crea reglas de RBAC separadas para cada verbo en cada recurso al que un sujeto necesite acceder.
  2. Después de redactar las reglas, analiza las reglas para verificar si varias reglas tienen la misma lista verbs. Combina esas reglas en una sola.
  3. Mantén todas las reglas restantes separadas entre sí.

Este enfoque da como resultado un diseño de reglas más organizado, en el que las reglas que otorgan los mismos verbos a varios recursos se combinan y las reglas que otorgan verbos diferentes a los recursos se separan.

Por ejemplo, si tu carga de trabajo necesita obtener permisos para el recurso deployments, pero necesita list y watch en los recursos daemonsets, debes usar reglas independientes cuando crees un rol. Cuando vincules el rol de RBAC a tu carga de trabajo, no podrá usar watch en deployments.

Otro ejemplo, si tu carga de trabajo necesita get y watch en el recurso pods y en el recurso daemonsets, puedes combinarlos en una sola regla, ya que la carga de trabajo necesita los mismos verbos en ambos recursos.

En la siguiente tabla, ambos diseños de reglas funcionan, pero las reglas de división restringen de manera más detallada el acceso a los recursos según tus necesidades:

Recomendado No recomendado
- rules:
    apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get"]
- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets"]
    verbs: ["list", "watch"]

Otorga acceso get para Deployments y acceso watch y list para DaemonSets. Los sujetos no pueden enumerar Deployments.

- rules:
    apiGroups: ["apps"]
    resources: ["deployments", "daemonsets"]
    verbs: ["get","list","watch"]

Otorga los verbos a Deployments y DaemonSets. Un sujeto que podría no necesitar acceso list en los objetos deployments seguiría recibiendo ese acceso.

- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets", "deployments"]
    verbs: ["list", "watch"]

Combina dos reglas porque el sujeto necesita los mismos verbos para los recursos daemonsets y deployments.

- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets"]
    verbs: ["list", "watch"]
- rules:
    apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["list", "watch"]

Estas reglas de división tendrían el mismo resultado que la regla combinada, pero crearían un desorden innecesario en el manifiesto de tu rol

Restringe el acceso a instancias de recursos específicas

El RBAC te permite usar el campo resourceNames en tus reglas para restringir el acceso a una instancia con nombre específica de un recurso. Por ejemplo, si escribes un rol de RBAC que necesita update el ConfigMap seccomp-high y nada más, puedes usar resourceNames para especificar solo ese ConfigMap. Usa resourceNames siempre que sea posible.

Recomendado No recomendado
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

Restringe el sujeto para actualizar solo el ConfigMap seccomp-high. El sujeto no puede actualizar ningún otro ConfigMap en el espacio de nombres.

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["update"]

El sujeto puede actualizar el ConfigMap seccomp-high y cualquier otro ConfigMap del espacio de nombres.

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["list"]
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

Otorga a list acceso a todos los ConfigMaps del espacio de nombres, incluido seccomp-high. Restringe el acceso update solo al ConfigMap seccomp-high. Las reglas se dividen porque no puedes otorgar list para los recursos con nombre.

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["update", "list"]

Otorga acceso update a todos los ConfigMaps, junto con acceso list.

No permitas que las cuentas de servicio modifiquen los recursos de RBAC

No vincules recursos Role o ClusterRole que tengan permisos bind, escalate, create, update o patch en el grupo de API rbac.authorization.k8s.io a cuentas de servicio en cualquier espacio de nombres. escalate y bind, en particular, pueden permitir que un atacante omita los mecanismos de prevención de derivación integrados en el RBAC.

Cuentas de servicio de Kubernetes

Crea una cuenta de servicio de Kubernetes para cada carga de trabajo.

Crea una cuenta de servicio de Kubernetes independiente para cada carga de trabajo. Vincula un Role o un ClusterRole con los privilegios mínimos a esa cuenta de servicio.

No uses la cuenta de servicio predeterminada

Kubernetes crea una cuenta de servicio llamada default en cada espacio de nombres. La cuenta de servicio default se asigna de forma automática a los Pods que no especifican una cuenta de servicio de forma explícita en el manifiesto. Evita vincular un Role o un ClusterRole a la cuenta de servicio default. Kubernetes puede asignar la cuenta de servicio default a un Pod que no necesita el acceso otorgado en esos roles.

No actives automáticamente los tokens de la cuenta de servicio

El campo automountServiceAccountToken en la especificación del pod le indica a Kubernetes que inserte un token de credencial para una cuenta de servicio de Kubernetes en el Pod. El Pod puede usar este token para realizar solicitudes autenticadas al servidor de la API de Kubernetes. El valor predeterminado de este campo es true

En todas las versiones de GKE, configura automountServiceAccountToken=false en la especificación del Pod si tus Pods no necesitan comunicarse con el servidor de la API.

Da preferencia a los tokens efímeros por los tokens basados en Secrets

De forma predeterminada, el proceso de kubelet en el nodo recupera un token de cuenta de servicio de corta duración de forma automática para cada Pod. Kubelet activa este token en el Pod como un volumen proyectado, a menos que establezcas el campo automountServiceAccountToken en false en la especificación del Pod. Todas las llamadas a la API de Kubernetes desde el Pod usan este token para autenticarse en el servidor de la API.

Si recuperas los tokens de una cuenta de servicio de forma manual, evita usar Secrets de Kubernetes para almacenar el token. Los tokens de cuentas de servicio basados en Secrets son credenciales heredadas que no vencen y no se rotan de forma automática. Si necesitas credenciales para cuentas de servicio, usa la API de TokenRequest para obtener tokens de corta duración que se roten de forma automática.

Revisa de forma continua los permisos de RBAC

Revisa tus roles de RBAC y accede con regularidad para identificar posibles rutas de elevación y reglas redundantes. Por ejemplo, imagina una situación en la que no borras una RoleBinding que vincula un Role con privilegios especiales a un usuario borrado. Si un atacante crea una cuenta de usuario en ese espacio de nombres con el mismo nombre que el usuario borrado, estaría vinculado a ese Role y heredaría el mismo acceso. Las revisiones periódicas minimizan este riesgo.

Resumen de la lista de tareas

¿Qué sigue?