Aller au contenu

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 votre kubectl et votre kubeconfig.
* Option B (Secours - PC d'entreprise verrouillé) : Connectez-vous en SSH sur k8s-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 :

kubectl apply -f campus-limitrange.yaml
kubectl describe limitrange campus-default-limits

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 :

kubectl apply -f campus-quota.yaml
kubectl describe resourcequota campus-compute-quota

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 NetworkPolicies né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 :

kubectl apply -f postgres-networkpolicy.yaml
kubectl get 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 :

kubectl run rogue-pod --image=busybox --rm -it --restart=Never -- nc -zv -w 3 postgres 5432

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 :

kubectl apply -f deployment-backend.yaml
kubectl rollout status deployment/campus-backend

2. Vérifier l'identité système du processus dans le conteneur

Vérifiez l'UID sous lequel tourne le conteneur :

kubectl exec -it deployment/campus-backend -- id

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
Le résultat doit être 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
Le premier retourne yes, le second retourne no ! Votre délégation RBAC est parfaitement étanche.


Critères de Validation (Checklist)

  • Le couple LimitRange et ResourceQuota protè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 NetworkPolicy bloque tout trafic pirate vers le port 5432 de PostgreSQL.
  • Seul le microservice campus-backend est autorisé à communiquer avec la base.
  • Le conteneur s'exécute avec l'UID 10001 sans privilèges root (runAsNonRoot: true).
  • Vous savez auditer et déléguer des privilèges RBAC avec kubectl auth can-i.