TP17 : Sécurité Kubernetes (RBAC, Pod Security, NetworkPolicy, Kyverno, Falco)
Un cluster Kubernetes installé « par défaut » est permissif : tout Pod peut joindre tous les autres, un conteneur peut tourner en root ou en mode privileged, n’importe quelle image peut être déployée. Ce TP applique la défense en profondeur à une petite application volontairement mal configurée : identité et permissions (RBAC), durcissement des Pods (securityContext, Pod Security Standards), segmentation réseau (NetworkPolicy), règles d’admission (Policy as Code avec Kyverno), puis surveillance continue (Trivy Operator, Kubescape, Falco).
Fichiers de départ :
manifests.zip,policies.zip
1. Objectifs pédagogiques
À l’issue du TP, vous serez capable de :
- appliquer le moindre privilège avec ServiceAccount, Role, RoleBinding et le vérifier avec
kubectl auth can-i; - durcir un Pod (
runAsNonRoot,readOnlyRootFilesystem,capabilities.drop,seccompProfile) et appliquer les Pod Security Standards ; - écrire des NetworkPolicies default deny puis n’ouvrir que les flux nécessaires ;
- écrire et tester des ClusterPolicies Kyverno (Audit puis Enforce) ;
- analyser un cluster avec Trivy Operator et Kubescape, et détecter un comportement suspect avec Falco.
Les images de cette page sont des schémas explicatifs, pas des captures d’écran.
2. Environnement du TP
-
Le cluster K3s du TP14 (ou un K3s mono-nœud sur la VM de lab, ≥ 8 Go de RAM),
kubectlconfiguré. -
Helm installé (TP15) : il sert ici à installer Kyverno, Trivy Operator et Falco.
-
Déployer l’application de départ et constater qu’elle est acceptée sans aucune objection :
kubectl apply -f manifests/00-app.yaml kubectl -n tp17 get pods
3. Partie 1 : RBAC et ServiceAccount
-
Que peut faire le ServiceAccount
defaultdu namespace ?kubectl auth can-i --list --as=system:serviceaccount:tp17:default -n tp17 -
Compléter
manifests/10-rbac.yaml(lecture seule des ConfigMaps du namespace) et l’appliquer. -
Vérifier les permissions :
SA=system:serviceaccount:tp17:api-sa kubectl auth can-i get configmaps --as=$SA -n tp17 # yes kubectl auth can-i list secrets --as=$SA -n tp17 # no kubectl auth can-i get configmaps --as=$SA -n default # no (Role = un seul namespace) kubectl auth can-i create pods --as=$SA -n tp17 # no -
Associer
api-saau Deploymentapi(serviceAccountName). PourquoiautomountServiceAccountToken: false? -
Expliquer la différence Role / ClusterRole et RoleBinding / ClusterRoleBinding. Lister les ClusterRoleBindings qui donnent
cluster-admin(kubectl get clusterrolebindings -o wide | grep cluster-admin).
4. Partie 2 : securityContext
-
Durcir les deux Deployments :
api: image taggée, sansprivileged,runAsNonRoot: true,runAsUser: 65532,allowPrivilegeEscalation: false,readOnlyRootFilesystem: true,capabilities: { drop: ["ALL"] },seccompProfile: { type: RuntimeDefault }, requests/limits ;frontend: imagenginxinc/nginx-unprivileged:1.29(port 8080), mêmes options. Nginx a besoin d’écrire dans/tmp: monter un volumeemptyDir.
-
Vérifier dans le conteneur :
kubectl -n tp17 exec deploy/frontend -- id kubectl -n tp17 exec deploy/frontend -- touch /etc/test # Read-only file system
5. Partie 3 : Pod Security Standards
| Profil | Interdit notamment |
|---|---|
privileged |
Rien (aucune restriction). |
baseline |
privileged, hostNetwork, hostPath, capacités dangereuses ajoutées. |
restricted |
En plus : exécution en root, élévation de privilèges, capacités non retirées, seccomp absent. |
-
Appliquer le profil en mode avertissement pour voir ce qui casserait :
kubectl label ns tp17 pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/audit=restricted --overwrite kubectl -n tp17 rollout restart deploy -
Une fois les Deployments conformes, passer en enforce :
pod-security.kubernetes.io/enforce=restricted. -
Tenter de créer un Pod privilégié dans le namespace : il doit être refusé par l’API server.
6. Partie 4 : NetworkPolicies
-
Avant toute policy, vérifier que l’intrus joint l’API :
kubectl -n tp17 exec intruder -- wget -qO- -T 3 http://api. -
Appliquer la policy
default-deny-alldemanifests/20-networkpolicies.yaml: plus rien ne passe, même le DNS. -
Compléter : DNS sortant vers
kube-dns, puis frontend → api uniquement (port 8080). -
Vérifier : le frontend joint l’API, l’intrus non (timeout).
kubectl -n tp17 exec deploy/frontend -- wget -qO- -T 3 http://api # OK kubectl -n tp17 exec intruder -- wget -qO- -T 3 http://api # timeout
7. Partie 5 : Policy as Code avec Kyverno
-
Installer Kyverno :
helm repo add kyverno https://kyverno.github.io/kyverno/ && helm repo update helm install kyverno kyverno/kyverno -n kyverno --create-namespace kubectl -n kyverno get pods -
Appliquer la policy modèle
policies/disallow-latest-tag.yamlen mode Audit, puis consulter les rapports :kubectl get policyreports -A kubectl get policyreport -n tp17 -o yaml | grep -B2 -A6 fail -
Écrire les policies listées dans
policies/TODO-policies.md: pas de conteneur privilégié,runAsNonRoot, requests/limits obligatoires. -
Les tester hors cluster avec la CLI Kyverno (idéal en CI) :
docker run --rm -v "$PWD:/w" ghcr.io/kyverno/kyverno-cli:latest apply /w/policies/ --resource /w/manifests/00-app.yaml -
Passer les policies en Enforce (
validationFailureAction: Enforce, oufailureAction: Enforcepar règle dans les versions récentes) et tenter d’appliquer un Deployment non conforme : noter le message de refus. -
Comparer brièvement Kyverno et Pod Security Standards : que permet Kyverno que PSS ne permet pas (mutation, génération, vérification d’images, règles métier) ?
8. Partie 6 : Analyse continue et détection à l’exécution
-
Installer Trivy Operator et consulter les rapports de vulnérabilités et de configuration :
helm repo add aqua https://aquasecurity.github.io/helm-charts/ && helm repo update helm install trivy-operator aqua/trivy-operator -n trivy-system --create-namespace kubectl get vulnerabilityreports -n tp17 -o wide kubectl get configauditreports -n tp17 -o wide -
Lancer Kubescape sur le cluster et sur les manifests (référentiel NSA-CISA) et noter le score avant / après durcissement :
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash kubescape scan framework nsa manifests/ kubescape scan framework nsa --include-namespaces tp17 -
Installer Falco avec le pilote eBPF moderne :
helm repo add falcosecurity https://falcosecurity.github.io/charts && helm repo update helm install falco falcosecurity/falco -n falco --create-namespace \ --set driver.kind=modern_ebpf --set tty=trueSur K3s, le socket containerd est
/run/k3s/containerd/containerd.sock. Si les alertes n’affichent pas le nom des Pods, configurez ce chemin dans les collectors du chart (voirhelm show values falcosecurity/falco). -
Déclencher des événements suspects et observer les alertes :
kubectl -n tp17 exec -it intruder -- sh -c 'cat /etc/shadow; nc -l -p 4444 & sleep 1' kubectl -n falco logs -l app.kubernetes.io/name=falco -c falco | grep -E 'Notice|Warning|Critical'
Défi
- Écrire une policy Kyverno de mutation qui ajoute automatiquement
seccompProfile: RuntimeDefaultaux Pods qui n’en ont pas.- Installer OPA Gatekeeper dans un namespace de test, réécrire la règle « pas de
latest» en Rego (ConstraintTemplate+Constraint) et comparer la lisibilité avec Kyverno.
9. Livrables
Captures à déposer dans captures/ :
| Fichier | Contenu attendu |
|---|---|
rbac-can-i.png |
Résultats kubectl auth can-i (yes / no) |
pss-denied.png |
Pod privilégié refusé par le profil restricted |
netpol.png |
Frontend → API OK, intrus → API en timeout |
kyverno-enforce.png |
Deployment non conforme refusé avec le message de la policy |
kyverno-cli.png |
Résultat de kyverno apply en ligne de commande |
trivy-kubescape.png |
Rapports Trivy Operator et score Kubescape avant / après |
falco-alert.png |
Alerte Falco après la lecture de /etc/shadow |
Ainsi que les manifests durcis, les policies Kyverno et les NetworkPolicies.