Bonnes pratiques pour GKE RBAC


Ce document décrit les bonnes pratiques à suivre pour planifier vos règles de contrôle des accès basé sur les rôles (RBAC). Pour savoir comment mettre en œuvre le contrôle RBAC dans Google Kubernetes Engine (GKE), consultez la page Configurer le contrôle des accès basé sur les rôles.

RBAC est une fonctionnalité de sécurité de base de Kubernetes qui vous permet de créer des autorisations précises pour gérer les actions que les utilisateurs et les charges de travail peuvent effectuer sur les ressources de vos clusters. En tant qu'administrateur de plate-forme, vous créez des rôles RBAC et associez ces rôles à des sujets, qui sont des utilisateurs authentifiés, tels que des comptes de service ou des groupes Google Groupes.

Avant de commencer

Avant de lire ce document, assurez-vous de connaître les concepts suivants :

pour obtenir un récapitulatif des conseils figurant dans ce document, consultez la section Checklist.

Fonctionnement du contrôle RBAC

RBAC accepte les types de rôles et de liaisons suivants :

  • ClusterRole: ensemble d'autorisations pouvant être appliquées à n'importe quel espace de noms ou à l'ensemble du cluster.
  • Rôle: ensemble d'autorisations limitées à un seul espace de noms.
  • ClusterRoleBinding : attribuez un rôle ClusterRole à un utilisateur ou à un groupe pour tous les espaces de noms du cluster.
  • RoleBinding : attribuez un rôle Role ou ClusterRole à un utilisateur ou à un groupe au sein d'un espace de noms spécifique.

Vous définissez des autorisations sous forme de rules, définies dans un Role ou un ClusterRole. Chaque champ rules d'un rôle se compose d'un groupe d'API, de ressources d'API au sein de ce groupe d'API et des verbes (actions) autorisés sur ces ressources. Vous pouvez également cantonner les verbes aux instances nommées des ressources d'API à l'aide du champ resourceNames. Pour obtenir un exemple, consultez la section Restreindre l'accès à des instances de ressources spécifiques.

Après avoir défini un rôle, vous devez utiliser une liaison RoleBinding ou ClusterRoleBinding pour lier le rôle à un sujet. Choisissez le type de liaison selon que vous souhaitez accorder des autorisations dans un seul ou bien plusieurs espaces de noms.

Conception de rôle RBAC

Utiliser le principe du moindre privilège

Lorsque vous attribuez des autorisations dans un rôle RBAC, appliquez le principe du moindre privilège en n'accordant que les autorisations minimales nécessaires pour effectuer une tâche. Le principe du moindre privilège réduit le risque d'élévation des privilèges si votre cluster est compromis, et réduit la probabilité d'un incident de sécurité en cas d'accès excessifs.

Lorsque vous concevez vos rôles, gardez en tête les principaux risques liés à l'élévation des privilèges, tels que les verbes escalate ou bind, l'accès create pour les PersistentVolumes ou l'accès create pour les requêtes de signature de certificat. Pour obtenir la liste des risques, consultez la page Kubernetes RBAC – Risques liés à l'élévation des privilèges.

Éviter les rôles et les groupes par défaut

Kubernetes crée un ensemble d'objets ClusterRole et ClusterRoleBinding par défaut que vous pouvez utiliser pour la découverte des API et pour activer la fonctionnalité de composants gérés. Les autorisations accordées par ces rôles par défaut peuvent être étendues en fonction du rôle. Kubernetes dispose également d'un ensemble d'utilisateurs et de groupes d'utilisateurs par défaut, identifiés par le préfixe system:. Par défaut, Kubernetes et GKE associent automatiquement ces rôles aux groupes par défaut et à divers sujets. Pour obtenir la liste complète des rôles et des liaisons par défaut créés par Kubernetes, consultez la page Rôles et liaisons de rôles par défaut.

Le tableau suivant décrit certains rôles, utilisateurs et groupes par défaut. Nous recommandons d'éviter d'interagir avec ces rôles, utilisateurs et groupes, sauf si vous les avez évalués avec soin, car interagir avec ces ressources peut avoir des conséquences inattendues sur la stratégie de sécurité de votre cluster.

Nom Type Description
cluster-admin ClusterRole Accorde à un sujet l'autorisation d'effectuer n'importe quelle opération sur n'importe quelle ressource du cluster.
system:anonymous Utilisateur

Kubernetes attribue cet utilisateur aux requêtes de serveur d'API pour lesquelles aucune information d'authentification n'est fournie.

En associant un rôle à cet utilisateur, vous attribuez à tout utilisateur non authentifié les autorisations accordées par ce rôle.

system:unauthenticated Groupe

Kubernetes attribue ce groupe aux requêtes de serveur d'API pour lesquelles aucune information d'authentification n'est fournie.

En associant un rôle à ce groupe, vous attribuez à tout utilisateur non authentifié les autorisations accordées par ce rôle.

system:authenticated Groupe

GKE attribue ce groupe aux requêtes de serveur d'API effectuées par tout utilisateur connecté avec un compte Google, y compris tous les comptes Gmail. Dans la pratique, ce n'est pas très différent de system:unauthenticated, car tout le monde peut créer un compte Google.

En associant un rôle à ce groupe, vous attribuez à tout utilisateur disposant d'un compte Google, y compris de tous les comptes Gmail, les autorisations accordées par ce rôle.

system:masters Groupe

Par défaut, Kubernetes attribue le ClusterRole cluster-admin à ce groupe pour activer les fonctionnalités système.

L'ajout de vos propres sujets à ce groupe leur donne accès à toute opération sur n'importe quelle ressource de votre cluster.

Si possible, évitez de créer des liaisons qui impliquent les utilisateurs, rôles et groupes par défaut. Cela peut avoir des conséquences inattendues sur la stratégie de sécurité de votre cluster. Exemple :

  • En associant le ClusterRole cluster-admin par défaut au groupe system:unauthenticated, vous permettez à tous les utilisateurs non authentifiés d'accéder à toutes les ressources du cluster (y compris les secrets). Ces liaisons à privilèges élevés sont activement ciblées par des attaques telles que les campagnes massives de logiciels malveillants.
  • En associant un rôle personnalisé au groupe system:unauthenticated, vous attribuez aux utilisateurs non authentifiés les autorisations accordées par ce rôle.

Dans la mesure du possible, respectez les consignes suivantes :

  • N'ajoutez pas vos propres sujets au groupe system:masters.
  • N'associez pas le groupe system:unauthenticated à des rôles RBAC.
  • N'associez pas le groupe system:authenticated à des rôles RBAC.
  • N'associez pas l'utilisateur system:anonymous à des rôles RBAC.
  • N'associez pas le ClusterRole cluster-admin à vos propres sujets ou à l'un des utilisateurs et groupes par défaut. Si votre application nécessite de nombreuses autorisations, déterminez les autorisations exactes requises et créez un rôle spécifique à cet effet.
  • Évaluez les autorisations accordées par d'autres rôles par défaut avant de lier des sujets.
  • Évaluez les rôles associés aux groupes par défaut avant de modifier les membres de ces groupes.

Détecter et empêcher l'utilisation des rôles et des groupes par défaut

Vous devez évaluer vos clusters pour déterminer si vous avez lié l'utilisateur system:anonymous ou les groupes system:unauthenticated ou system:authenticated à l'aide de ClusterRoleBindings et de RoleBindings.

ClusterRoleBindings
  1. Répertoriez les noms de tous les ClusterRoleBindings ayant pour sujet system:anonymous, system:unauthenticated ou 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'
    

    Le résultat ne doit répertorier que les objets ClusterRoleBinding suivants :

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

    Si la sortie contient d'autres liaisons autres que celles par défaut, procédez comme suit pour chaque liaison supplémentaire. Si le résultat ne contient pas de liaisons autres que celles par défaut, ignorez les étapes suivantes.

  2. Listez les autorisations du rôle associé à la liaison :

    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'
    

    Remplacez CLUSTER_ROLE_BINDING_NAME par le nom de l'objet ClusterRoleBinding autre que celui par défaut.

    Le résultat ressemble à ce qui suit :

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

    Si vous déterminez que les autorisations dans la sortie peuvent être accordées en toute sécurité aux utilisateurs ou aux groupes par défaut, aucune autre action n'est requise. Si vous constatez que les autorisations accordées par la liaison ne sont pas sécurisées, passez à l'étape suivante.

  3. Supprimez une liaison non sécurisée de votre cluster :

    kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
    

    Remplacez CLUSTER_ROLE_BINDING_NAME par le nom de l'objet ClusterRoleBinding à supprimer.

RoleBindings
  1. Répertoriez l'espace de noms et le nom de tous les objets RoleBinding ayant pour sujet system:anonymous, system:unauthenticated ou 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 votre cluster est correctement configuré, la sortie doit être vide. Si la sortie contient d'autres liaisons autres que celles par défaut, procédez comme suit pour chaque liaison supplémentaire. Si le résultat est vide, ignorez les étapes suivantes.

    Si vous ne connaissez que le nom de l'objet RoleBinding, vous pouvez utiliser la commande suivante pour rechercher les liaisons de rôles correspondantes dans tous les espaces de noms :

    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'
    

    Remplacez ROLE_BINDING_NAME par le nom de l'objet RoleBinding autre que celui par défaut.

  2. Listez les autorisations du rôle associé à la liaison :

    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'
    

    Remplacez les éléments suivants :

    • ROLE_BINDING_NAME : nom de l'objet RoleBinding autre que celui par défaut.
    • ROLE_BINDING_NAMESPACE : espace de noms de l'objet RoleBinding autre que celui par défaut.

    Le résultat ressemble à ce qui suit :

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

    Si vous déterminez que les autorisations dans la sortie peuvent être accordées en toute sécurité aux utilisateurs ou aux groupes par défaut, aucune autre action n'est requise. Si vous constatez que les autorisations accordées par la liaison ne sont pas sécurisées, passez à l'étape suivante.

  3. Supprimez une liaison non sécurisée de votre cluster :

    kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
    

    Remplacez les éléments suivants :

    • ROLE_BINDING_NAME : nom de l'objet RoleBinding à supprimer.
    • ROLE_BINDING_NAMESPACE : espace de noms de l'objet RoleBinding à supprimer.

Limiter les autorisations au niveau de l'espace de noms

Utilisez les liaisons et les rôles comme suit, en fonction des besoins de votre charge de travail ou de l'utilisateur :

  • Pour accorder l'accès aux ressources d'un seul espace de noms, utilisez un objet Role avec un RoleBinding.
  • Pour accorder l'accès aux ressources de plusieurs espaces de noms, utilisez un objet ClusterRole avec un RoleBinding pour chaque espace de noms.
  • Pour accorder l'accès aux ressources de tous les espaces de noms du cluster, utilisez un objet ClusterRole avec un ClusterRoleBinding.

Accordez des autorisations pour le moins d'espaces de noms possible.

Ne pas utiliser de caractères génériques

Le caractère * est un caractère générique qui s'applique à tous les champs de vos règles. Évitez d'utiliser des caractères génériques dans vos règles. Spécifiez explicitement les groupes d'API, les ressources et les verbes dans les règles RBAC. Par exemple, si vous spécifiez * dans le champ verbs, vous attribuez les autorisations get, list, watch, patch, update, deletecollection. et delete sur les ressources concernées. Le tableau suivant montre des exemples décrivant le bon usage des caractères génériques dans vos règles :

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

Attribue les verbes get, list et watch spécifiquement aux groupes d'API apps et extensions.

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

Attribue les verbes à deployments dans n'importe quel groupe d'API.

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

N'attribue que les verbes get, list et watch aux déploiements des groupes d'API apps et extensions.

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

Attribue tous les verbes, y compris patch ou delete.

Utiliser des règles distinctes pour accorder l'accès selon le principe du moindre privilège à des ressources spécifiques

Lorsque vous planifiez vos règles, essayez d'appliquer la procédure générale suivante afin de concevoir plus efficacement des règles basées sur le principe du moindre privilège, dans chaque rôle :

  1. Rédigez des règles RBAC distinctes pour chaque verbe sur chaque ressource à laquelle un sujet doit accéder.
  2. Après avoir rédigé les règles, analysez-les pour vérifier si plusieurs d'entre elles ont la même liste verbs. Combinez alors ces règles en une seule.
  3. Veillez à conserver toutes les autres règles indépendamment les unes des autres.

Cette approche permet de structurer davantage la conception de règles : les règles qui attribuent les mêmes verbes à plusieurs ressources sont combinées et celles qui attribuent des verbes différents aux ressources sont individualisées.

Par exemple, si votre charge de travail nécessite des autorisations get pour la ressource deployments, mais aussi des autorisations list et watch sur les ressources daemonsets, vous devez utiliser des règles distinctes lors de la création d'un rôle. Lorsque vous liez le rôle RBAC à votre charge de travail, il ne pourra pas utiliser le verbe watch sur la ressource deployments.

Autre exemple : si votre charge de travail nécessite les autorisations get et watch à la fois sur la ressource pods et sur la ressource daemonsets, vous pouvez les combiner en une seule règle, car la charge de travail requiert les mêmes verbes sur les deux ressources.

Dans le tableau suivant, les deux conceptions de règles fonctionnent, mais les règles individualisées limitent plus précisément l'accès aux ressources en fonction de vos besoins :

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

Accorde l'accès get pour les objets Deployment et les accès watch et list pour les objets DaemonSet. Les sujets ne peuvent pas lister les objets Deployment.

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

Accorde les verbes à la fois aux objets Deployment et DaemonSet. Un sujet qui n'a pas forcément besoin d'un accès list sur les objets deployments va tout de même bénéficier de pareil accès.

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

Combine deux règles, car le sujet a besoin des mêmes verbes pour les ressources daemonsets et deployments.

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

Ces règles de division auront le même résultat que la règle combinée, mais créeront un encombrement inutile dans le fichier manifeste du rôle.

Limiter l'accès à des instances de ressources spécifiques

Le contrôle RBAC vous permet d'utiliser le champ resourceNames dans vos règles pour limiter l'accès à une instance nommée spécifique d'une ressource. Par exemple, si vous écrivez un rôle RBAC nécessitant de mettre à jour (update) l'objet ConfigMap seccomp-high et rien d'autre, vous pouvez utiliser resourceNames pour ne spécifier que ce ConfigMap. Utilisez autant que possible le champ resourceNames.

Recommandé Déconseillé
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

Limite le sujet à la seule mise à jour du ConfigMap seccomp-high. Le sujet ne peut mettre à jour aucun autre ConfigMap dans l'espace de noms.

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

L'objet peut mettre à jour le ConfigMap seccomp-high et tout autre ConfigMap dans l'espace de noms.

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

Accorde l'accès list à tous les ConfigMaps de l'espace de noms, y compris seccomp-high. Limite l'accès update au seul ConfigMap seccomp-high. Les règles sont individualisées, car vous ne pouvez pas accorder l'accès list sur des ressources nommées.

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

Accorde l'accès update sur tous les ConfigMaps, ainsi que l'accès list.

Ne pas autoriser les comptes de service à modifier des ressources RBAC

N'associez pas les ressources Role ou ClusterRole disposant des autorisations bind, escalate, create, update ou patch sur le groupe d'API rbac.authorization.k8s.io à des comptes de service situés dans n'importe quel espace de noms. Plus particulièrement, les autorisations escalate et bind peuvent permettre à un pirate informatique de contourner les mécanismes de prévention des élévations intégrés à RBAC.

Comptes de service Kubernetes

Créer un compte de service Kubernetes pour chaque charge de travail

Créez un compte de service Kubernetes distinct pour chaque charge de travail. Associez un objet Role ou ClusterRole, selon le principe du moindre privilège, à ce compte de service.

Ne pas utiliser le compte de service par défaut

Kubernetes crée dans chaque espace de noms un compte de service nommé default. Ce compte de service default est automatiquement attribué aux pods qui ne spécifient pas explicitement de compte de service dans le fichier manifeste. Évitez d'associer un objet Role ou ClusterRole au compte de service default. Kubernetes pourrait alors attribuer le compte de service default à un pod qui n'a pas besoin de l'accès accordé à ces rôles.

Ne pas installer automatiquement les jetons de compte de service

Le champ automountServiceAccountToken de la spécification de pod indique à Kubernetes d'injecter un jeton d'identifiant pour un compte de service Kubernetes dans le pod. Le pod peut utiliser ce jeton pour envoyer des requêtes authentifiées au serveur d'API Kubernetes. La valeur par défaut de ce champ est true.

Dans toutes les versions de GKE, définissez automountServiceAccountToken=false dans la spécification de pod si vos pods n'ont pas besoin de communiquer avec le serveur d'API.

Préférer les jetons éphémères aux jetons basés sur des secrets

Par défaut, le processus kubelet sur le nœud récupère un jeton de compte de service de courte durée alterné automatiquement pour chaque pod. Le kubelet installe ce jeton sur le pod en tant que volume projeté, sauf si vous définissez le champ automountServiceAccountToken sur false dans la spécification du pod. Tous les appels à l'API Kubernetes à partir du pod utilisent ce jeton pour s'authentifier auprès du serveur d'API.

Si vous récupérez manuellement les jetons du compte de service, évitez d'utiliser des secrets Kubernetes pour stocker le jeton. Les jetons de compte de service basés sur des secrets constituent des identifiants d'ancienne génération : ils n'expirent pas et ne sont pas alternés automatiquement. Si vous avez besoin d'identifiants pour des comptes de service, utilisez l'API TokenRequest pour obtenir des jetons de courte durée qui sont automatiquement alternés.

Examiner régulièrement les autorisations RBAC

Examinez régulièrement vos rôles RBAC et vos accès, afin d'identifier les chemins d'élévation potentiels et les règles redondantes. Par exemple, considérons une situation dans laquelle vous ne supprimez pas un objet RoleBinding qui lie un objet Role doté de droits spécifiques à un utilisateur supprimé. Si un pirate informatique crée un compte utilisateur dans cet espace de noms, en reprenant le nom de l'utilisateur supprimé, il est alors lié à cet objet Role et va hériter du même accès. Des examens périodiques permettent de réduire ce risque.

Checklist

Étape suivante