Lab 07 : Sécurité, Quotas de Ressources & Isolement Réseau (NetworkPolicy)¶
Objectifs Pédagogiques :¶
- Protéger le cluster contre la surconsommation et l'effet « Noisy Neighbor » avec le couple ResourceQuota et LimitRange.
- Cloisonner le trafic réseau selon le modèle Zéro-Trust avec les NetworkPolicies (politique Default Deny et filtrage strict vers PostgreSQL).
- Durcir les conteneurs selon les standards de sécurité NSA/CISA avec les SecurityContext (interdiction du compte root, système de fichiers en lecture seule).
- Auditer les privilèges et déléguer des accès avec les ServiceAccounts et le modèle RBAC (
kubectl auth can-i).
📌 Note Compatibilité OS :
Les commandes de cet atelier (kubectl apply,kubectl describe resourcequota,kubectl auth can-i,kubectl exec) sont rigoureusement identiques sous Windows (PowerShell), Linux (Ubuntu) et macOS (Terminal).💡 Modalités d'exécution (Au choix) :
* Option A (Recommandée - Confort DevOps Local) : Exécutez les commandes directement depuis votre machine locale avec votrekubectlet votrekubeconfig.
* Option B (Secours - PC d'entreprise verrouillé) : Connectez-vous en SSH surk8s-control: l'outillage et les manifests sont pré-installés dans~/labs/.
Contexte Métier¶
En environnement partagé ou de production réglementée (secteur bancaire, assurance, santé), laisser un conteneur consommer toute la mémoire d'un nœud physique ou permettre à n'importe quel conteneur compromis d'accéder directement au port 5432 de la base de données PostgreSQL constitue une faille critique de sécurité. Cet atelier vous guide pour verrouiller les ressources système, cloisonner le réseau et durcir les conteneurs au niveau du noyau Linux.
Exercice 1 : Gouvernance des Ressources avec LimitRange & ResourceQuota¶
En entreprise, un ResourceQuota seul crée un piège fréquent : dès qu'un quota de CPU/RAM est actif sur un namespace, tout pod créé sans spécification explicite de requests et limits est immédiatement rejeté par l'API Server ! C'est pourquoi on configure toujours un LimitRange en amont.
1. Configurer un LimitRange pour injecter des valeurs par défaut¶
Créez le fichier campus-limitrange.yaml qui attribue automatiquement des limites aux conteneurs qui n'en déclarent pas :
apiVersion: v1
kind: LimitRange
metadata:
name: campus-default-limits
spec:
limits:
- default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
type: Container
Appliquez le LimitRange :
2. Définir le plafond global avec un ResourceQuota¶
Créez le fichier campus-quota.yaml pour plafonner l'utilisation globale cumulée du namespace :
* Maximum 6 Pods au total
* Maximum 2 cœurs CPU (requests)
* Maximum 2 Go de RAM (requests)
* Maximum 4 cœurs CPU (limits)
* Maximum 4 Go de RAM (limits)
apiVersion: v1
kind: ResourceQuota
metadata:
name: campus-compute-quota
spec:
hard:
pods: "6"
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
Appliquez le ResourceQuota et visualisez la consommation en direct :
3. Test de rejet par l'Admission Controller¶
Tentez de déployer un pod sollicitant 5 cœurs CPU (dépassant le plafond autorisé de 4 cœurs) :
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pod-trop-gros
spec:
containers:
- name: sleeper
image: busybox
command: ["sleep", "3600"]
resources:
requests:
cpu: "5"
limits:
cpu: "5"
EOF
Observez l'erreur immédiate renvoyée par l'API Server :
Error from server (Forbidden): error when creating "STDIN": pods "pod-trop-gros" is forbidden: exceeded quota: campus-compute-quota, requested: limits.cpu=5,requests.cpu=5, used: ...
Le cluster a automatiquement protégé ses ressources physiques !
Exercice 2 : Cloisonnement Réseau Zéro-Trust avec NetworkPolicy¶
Par défaut dans Kubernetes, le réseau est entièrement plat : n'importe quel pod du cluster peut contacter le port 5432 de PostgreSQL, même un pod web compromis ou un script tiers.
1. Règle d'or Zéro-Trust : Isoler par défaut (Default Deny Ingress)¶
Créez le fichier default-deny-ingress.yaml qui coupe tout le trafic entrant non explicitement autorisé :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
⚠️ Note sur le plugin CNI : Les
NetworkPoliciesnécessitent un pilote réseau (CNI) compatible (Calico, Cilium, Weave, ou Kube-Router). Sur votre cluster AWS Kubeadm, Calico a été déployé au Lab 00 et applique nativement les NetworkPolicies.
2. Règle ciblée : Autoriser exclusivement le backend vers PostgreSQL¶
Nous voulons que seuls les pods munis du label app: campus-backend puissent atteindre le port 5432 du pod campus-postgres.
Créez le fichier postgres-networkpolicy.yaml :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-only-backend-to-postgres
spec:
podSelector:
matchLabels:
app: campus-postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: campus-backend
ports:
- protocol: TCP
port: 5432
Appliquez la NetworkPolicy :
3. Test d'intrusion : Tentative d'accès pirate non autorisée¶
Lancez un conteneur pirate sans le label app=campus-backend et tentez d'établir une connexion TCP vers PostgreSQL :
Résultat attendu : La commande tombe en timeout (Connection timed out). Le paquet réseau est rejeté au niveau du filtre noyau (iptables/eBPF) géré par Calico !
4. Validation de la connectivité légitime depuis le backend¶
Exécutez le test avec un conteneur de diagnostic portant l'étiquette autorisée app=campus-backend :
kubectl run test-auth --image=busybox --labels='app=campus-backend' --rm -i --restart=Never -- nc -zv -w 3 postgres 5432
Résultat attendu : postgres (10.x.x.x:5432) open. L'accès est accordé exclusivement au microservice habilité !
Vous pouvez également vérifier que l'API Spring Boot interroge la base avec succès :
kubectl run test-health --image=curlimages/curl --rm -i --restart=Never -- curl -s http://campus-backend-svc:8080/actuator/health
Exercice 3 : Durcissement des Conteneurs avec SecurityContext¶
Selon le guide de durcissement Kubernetes de la NSA et de la CISA, un conteneur de production ne doit jamais s'exécuter en tant qu'utilisateur root (UID 0), ne doit jamais pouvoir élever ses privilèges et doit idéalement monter son disque racine en lecture seule (Read-only root filesystem).
1. Intégrer les directives de sécurité au déploiement Backend¶
Mettez à jour votre fichier deployment-backend.yaml pour intégrer le bloc securityContext au niveau conteneur et pod :
apiVersion: apps/v1
kind: Deployment
metadata:
name: campus-backend
spec:
replicas: 2
selector:
matchLabels:
app: campus-backend
template:
metadata:
labels:
app: campus-backend
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
containers:
- name: campus-backend
image: regaieg/campus:backend-1.0.0
ports:
- containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- ALL
env:
- name: SPRING_DATASOURCE_URL
value: "jdbc:postgresql://postgres:5432/campus"
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: campus-db-credentials
key: POSTGRES_USER
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: campus-db-credentials
key: POSTGRES_PASSWORD
envFrom:
- configMapRef:
name: campus-config
Appliquez le déploiement durci :
2. Vérifier l'identité système du processus dans le conteneur¶
Vérifiez l'UID sous lequel tourne le conteneur :
Vous constatez : uid=10001 gid=10001 groups=10001. Le processus ne possède aucun droit d'administration sur le système hôte !
Exercice 4 : Audit RBAC & ServiceAccount Dédié¶
Par défaut, chaque pod reçoit un jeton d'authentification lié au compte de service default. Si ce jeton est compromis, un attaquant pourrait interroger l'API Kubernetes.
1. Tester les autorisations avec kubectl auth can-i¶
La commande kubectl auth can-i est l'outil indispensable de l'administrateur pour vérifier instantanément les droits d'un utilisateur ou d'un compte de service :
# Vérifier ce que vous avez le droit de faire
kubectl auth can-i create pods
kubectl auth can-i delete namespaces
kubectl auth can-i create clusterroles
# Vérifier les droits du compte de service par défaut
kubectl auth can-i list secrets --as=system:serviceaccount:default:default
no : le compte de service applicatif standard n'a pas le droit d'énumérer les secrets du cluster.
2. Créer un ServiceAccount restreint pour une tâche d'automatisation¶
Créez un compte de service dédié nommé campus-ci-runner et associez-lui un rôle en lecture seule :
kubectl create serviceaccount campus-ci-runner
kubectl create role pod-reader --verb=get,list,watch --resource=pods
kubectl create rolebinding read-pods --role=pod-reader --serviceaccount=default:campus-ci-runner
Validez les droits accordés :
kubectl auth can-i list pods --as=system:serviceaccount:default:campus-ci-runner
kubectl auth can-i delete pods --as=system:serviceaccount:default:campus-ci-runner
yes, le second retourne no ! Votre délégation RBAC est parfaitement étanche.
Critères de Validation (Checklist)¶
- Le couple
LimitRangeetResourceQuotaprotège le cluster contre l'épuisement des CPU/RAM. - La tentative de déploiement dépassant les quotas a été formellement rejetée par l'API Server.
- La
NetworkPolicybloque tout trafic pirate vers le port5432de PostgreSQL. - Seul le microservice
campus-backendest autorisé à communiquer avec la base. - Le conteneur s'exécute avec l'UID
10001sans privilèges root (runAsNonRoot: true). - Vous savez auditer et déléguer des privilèges RBAC avec
kubectl auth can-i.