RBAC Kubernetes refuse tout par défaut : une action non explicitement permise par une règle est rejetée par le serveur API. Ce modèle deny-by-default rassure à tort, parce qu’il ne protège que contre ce qui n’a jamais été autorisé — un rôle trop large, une fois accordé, ne génère aucune alerte et ne casse jamais rien. Il existe, silencieux, jusqu’à l’incident qui l’expose.
Role vs ClusterRole : la portée avant la permission
Le premier choix n’est pas quelles permissions accorder, mais à quelle portée : un Role s’applique à un seul namespace, un ClusterRole à l’ensemble du cluster (ou à des ressources non namespacées, comme les nœuds). Une erreur fréquente consiste à utiliser un ClusterRole par réflexe, même quand un Role namespacé suffirait, parce que la syntaxe est presque identique et que la différence de portée ne se voit qu’au moment où quelqu’un l’exploite.
# Role : accès en lecture seule aux Pods, uniquement
# dans le namespace "staging", jamais au-delà
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: staging
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
Le piège du wildcard
Un rôle qui utilise "*" sur les verbes ou les ressources élimine toute la valeur du modèle : verbs: ["*"] équivaut à accorder create, delete, patch en plus de la simple lecture qui était probablement l’intention initiale. Ce raccourci, souvent pris pour débloquer rapidement un incident, survit ensuite indéfiniment parce que rien ne force à revenir dessus une fois l’urgence passée.
# À éviter : wildcard sur les verbes, accorde bien
# plus que la lecture qui semblait suffire au départ
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["*"]
Le ServiceAccount par défaut, monté sans qu’on le demande
Chaque pod reçoit par défaut un token du ServiceAccount default du namespace, monté automatiquement, même quand le pod n’a besoin d’appeler l’API Kubernetes pour rien. Ce token hérite de tout ce que RBAC accorde à ce ServiceAccount : un ClusterRole trop large lié au default expose alors sa permission à chaque pod du namespace, sans qu’aucun de ces pods n’ait explicitement demandé cet accès.
# Désactive le montage automatique du token si le pod
# n'a structurellement aucun besoin d'appeler l'API
apiVersion: v1
kind: Pod
metadata:
name: worker
spec:
automountServiceAccountToken: false
Un ServiceAccount dédié par application, avec ses propres permissions minimales, plutôt que le default partagé par tout le namespace, réduit la surface exposée à ce que l’application concernée utilise réellement.
Auditer ce qui existe déjà
kubectl auth can-i --list, exécuté avec l’identité d’un ServiceAccount précis, révèle l’ensemble de ses permissions effectives, agrégées depuis tous les RoleBinding et ClusterRoleBinding qui le concernent. C’est le seul moyen fiable de répondre à « que peut réellement faire ce compte » sans reconstituer manuellement l’ensemble des liaisons de rôles qui s’appliquent à lui.
# Permissions effectives du ServiceAccount "ci-deployer"
# dans le namespace "production", agrégées de tous les bindings
kubectl auth can-i --list \
--as=system:serviceaccount:production:ci-deployer \
-n production
Un audit régulier de chaque ServiceAccount qui compte (ceux liés à un pipeline CI/CD, à un opérateur, à tout composant qui écrit dans le cluster) rattrape ce qu’un déploiement normal ne révèle jamais : les permissions en trop ne se manifestent que lors de leur exploitation.
À retenir
RBAC refuse tout par défaut, mais ce modèle ne protège que contre l’oubli d’accorder une permission, jamais contre l’excès d’en accorder trop. Choisir la portée la plus étroite possible (Role plutôt que ClusterRole par défaut), bannir les wildcards sur les verbes et les ressources, désactiver le montage automatique du token quand un pod n’appelle jamais l’API, et auditer régulièrement via kubectl auth can-i --list : ces quatre réflexes ferment le seul vrai angle mort du modèle deny-by-default, un travail qui compte particulièrement dès qu’un cluster héberge plusieurs équipes, sujet central d’une migration Kubernetes bien menée.