Exercice 03 : Implémenter RBAC Kubernetes : contrôle d'accès granulaire
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Créer des
RoleetClusterRoleavec permissions minimales - ✅ Lier des permissions à des
ServiceAccountavecRoleBinding - ✅ Vérifier les permissions avec
kubectl auth can-i - ✅ Mettre en place des
NetworkPolicypour restreindre le trafic inter-pods - ✅ Auditer les permissions dangereuses avec
kubectl-who-can
Durée estimée : 40 minutes
Difficulté : ⭐⭐⭐☆☆ (Avancé)
Prérequis : Cluster Kubernetes local (minikube ou kind), kubectl configuré
📖 Contexte
Par défaut, dans Kubernetes, tout pod peut appeler l'API Kubernetes. Si un attaquant compromet un pod, il peut potentiellement lire tous les secrets du namespace, lister les autres pods, voire créer de nouveaux pods avec des privilèges élevés.
RBAC (Role-Based Access Control) permet de contrôler précisément ce que chaque pod (via son ServiceAccount) peut faire sur l'API Kubernetes. Les NetworkPolicies contrôlent le trafic réseau entre les pods.
📋 Énoncé
Sécurisez un cluster avec RBAC et NetworkPolicies suivant le principe du moindre privilège.
🧭 Déroulement de l'exercice
Tâche 1 : Vérifier l'état RBAC par défaut
# Créer un namespace de travail
kubectl create namespace rbac-demo
# Par défaut, le ServiceAccount "default" a des droits trop larges
# Vérifier ce que le SA default peut faire
kubectl auth can-i list pods --namespace=rbac-demo --as=system:serviceaccount:rbac-demo:default
kubectl auth can-i get secrets --namespace=rbac-demo --as=system:serviceaccount:rbac-demo:default
kubectl auth can-i create pods --namespace=rbac-demo --as=system:serviceaccount:rbac-demo:defaultIndice : Sur minikube ou un cluster permissif, certaines de ces commandes retournent
yes. C'est précisément le problème : le SA default ne devrait pas pouvoir lister les pods ou lire les secrets.
Tâche 2 : Créer des ServiceAccounts dédiés
# Bonne pratique : un ServiceAccount par application
kubectl create serviceaccount api-server -n rbac-demo
kubectl create serviceaccount worker -n rbac-demo
kubectl create serviceaccount monitoring -n rbac-demo
# Vérifier qu'ils existent
kubectl get serviceaccounts -n rbac-demoTâche 3 : Définir des Roles avec permissions minimales
# roles.yaml
---
# Role pour l'API server : peut gérer ses propres configs (ConfigMaps)
# mais NE PEUT PAS lire les Secrets ou créer des Pods
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: api-server-role
namespace: rbac-demo
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
# Peut lire ses propres pods pour le health check
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
# Role pour le worker : peut créer des Jobs
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: worker-role
namespace: rbac-demo
rules:
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create", "get", "list", "watch", "delete"]
---
# ClusterRole pour le monitoring : lecture seule sur tout le cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-reader
rules:
- apiGroups: [""]
resources: ["pods", "nodes", "services", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list", "watch"]
# Pas d'accès aux Secrets !kubectl apply -f roles.yamlTâche 4 : Lier les Roles aux ServiceAccounts
# bindings.yaml
---
# Lier api-server-role au SA api-server
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: api-server-binding
namespace: rbac-demo
subjects:
- kind: ServiceAccount
name: api-server
namespace: rbac-demo
roleRef:
kind: Role
name: api-server-role
apiGroup: rbac.authorization.k8s.io
---
# Lier worker-role au SA worker
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: worker-binding
namespace: rbac-demo
subjects:
- kind: ServiceAccount
name: worker
namespace: rbac-demo
roleRef:
kind: Role
name: worker-role
apiGroup: rbac.authorization.k8s.io
---
# ClusterRoleBinding pour le monitoring (accès cluster-wide)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: monitoring-binding
subjects:
- kind: ServiceAccount
name: monitoring
namespace: rbac-demo
roleRef:
kind: ClusterRole
name: monitoring-reader
apiGroup: rbac.authorization.k8s.iokubectl apply -f bindings.yamlTâche 5 : Vérifier les permissions avec kubectl auth can-i
# api-server : PEUT lire les configmaps
kubectl auth can-i get configmaps -n rbac-demo \
--as=system:serviceaccount:rbac-demo:api-server
# → yes
# api-server : NE PEUT PAS lire les secrets
kubectl auth can-i get secrets -n rbac-demo \
--as=system:serviceaccount:rbac-demo:api-server
# → no
# api-server : NE PEUT PAS créer des pods
kubectl auth can-i create pods -n rbac-demo \
--as=system:serviceaccount:rbac-demo:api-server
# → no
# worker : PEUT créer des jobs
kubectl auth can-i create jobs -n rbac-demo \
--as=system:serviceaccount:rbac-demo:worker
# → yes
# monitoring : PEUT lire les pods partout
kubectl auth can-i list pods --all-namespaces \
--as=system:serviceaccount:rbac-demo:monitoring
# → yes
# monitoring : NE PEUT PAS lire les secrets
kubectl auth can-i get secrets -n rbac-demo \
--as=system:serviceaccount:rbac-demo:monitoring
# → noVérification : Toutes les commandes ci-dessus doivent retourner le résultat attendu.
Tâche 6 : NetworkPolicy - isoler le trafic réseau
# network-policies.yaml
---
# Règle par défaut : bloquer tout le trafic entrant et sortant dans le namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: rbac-demo
spec:
podSelector: {} # S'applique à tous les pods
policyTypes:
- Ingress
- Egress
---
# Autoriser l'api-server à recevoir du trafic sur le port 3000
# depuis le namespace rbac-demo uniquement
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-server
namespace: rbac-demo
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: rbac-demo
ports:
- port: 3000
protocol: TCP
egress:
# Autoriser les requêtes DNS
- ports:
- port: 53
protocol: UDP
# Autoriser la connexion à la base de données
- to:
- podSelector:
matchLabels:
app: database
ports:
- port: 5432kubectl apply -f network-policies.yaml✅ Vérification du résultat
kubectl get serviceaccounts -n rbac-demoliste 3 SAs (+ default)kubectl auth can-i get secrets -n rbac-demo --as=system:serviceaccount:rbac-demo:api-serverretournenokubectl auth can-i list pods --all-namespaces --as=system:serviceaccount:rbac-demo:monitoringretourneyeskubectl get networkpolicies -n rbac-demoliste les NetworkPolicies créées
💡 À retenir
Le principe du moindre privilège en RBAC Kubernetes :
ServiceAccount (identité du pod)
↓ via RoleBinding
Role (namespace) ou ClusterRole (cluster)
↓ contient
Rules : apiGroups + resources + verbsNe jamais donner : verbs: ["*"] ou resources: ["*"] - c'est l'équivalent d'un accès root.
✨ Solution Complète
# Role minimal pour lire les ConfigMaps
kind: Role
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
kind: RoleBinding
subjects:
- kind: ServiceAccount
name: mon-app
roleRef:
kind: Role
name: mon-role
apiGroup: rbac.authorization.k8s.io