Retour au cours

cyber / cybersecurite-fondamentale

Sécurité des conteneurs et du cloud

Leçon 211 exercice

Explication

Ce que vous allez apprendre

  • Construire une image Docker durcie : utilisateur non-root, multi-stage build, dépendances figées
  • Configurer un securityContext Kubernetes pour interdire l'exécution en root et limiter les capabilities
  • Scanner une image de conteneur avant déploiement avec un outil comme Trivy
  • Restreindre les communications réseau entre pods avec une NetworkPolicy Kubernetes
  • Appliquer le moindre privilège à l'échelle du cloud via des politiques IAM restrictives et des rôles temporaires

Dans quel contexte ?

Une équipe déploie son API dans un cluster Kubernetes en utilisant l'image Docker par défaut générée par son framework, sans y penser davantage. Un audit de sécurité révèle que le conteneur tourne en root, que l'image contient des paquets système avec des CVE critiques non corrigées, et qu'aucune NetworkPolicy n'empêche les pods de discuter librement avec la base de données. Chacun de ces trois problèmes, pris isolément, semble mineur ; combinés, ils offrent à un attaquant un chemin direct de l'application vers les données de production.

Retour à la défense, dans un contexte moderne

Après la parenthèse offensive du pentest, on revient à la défense, cette fois dans le contexte des infrastructures modernes : conteneurs (Docker, Kubernetes) et cloud. Ces technologies changent la donne car elles ajoutent des couches d'abstraction qui ont chacune leurs propres angles d'attaque.

Pourquoi une image Docker "root par défaut" est un problème

Piège fréquent

Par défaut, un conteneur peut s'exécuter avec les mêmes droits que root sur la machine hôte. Si un attaquant compromet l'application à l'intérieur du conteneur, il hérite potentiellement de ces droits élevés — et selon la configuration, peut même s'évader du conteneur pour atteindre l'hôte. Faire tourner le processus applicatif avec un utilisateur non privilégié applique simplement le principe du moindre privilège vu en leçon 4, transposé à ce nouveau contexte.

Le rôle du securityContext Kubernetes

Réglage securityContextCe qu'il empêche
runAsNonRoot: trueLe conteneur de démarrer avec l'utilisateur root
allowPrivilegeEscalation: falseL'escalade de privilège via un binaire setuid
readOnlyRootFilesystem: trueL'écriture de fichiers malveillants persistants dans le conteneur
capabilities.drop: ["ALL"]L'usage de capacités système Linux non nécessaires

C'est l'équivalent conteneurisé du durcissement systemd déjà vu en leçon 4.

Pourquoi scanner les images avant déploiement

Une image Docker est construite à partir de couches qui incluent souvent des bibliothèques système et des dépendances tierces, chacune pouvant contenir des CVE connues. Un scanner comme Trivy vérifie automatiquement ces composants avant que l'image ne soit déployée en production — c'est un contrôle preventif, pas une réaction après incident.

L'IAM cloud, le moindre privilège à l'échelle du cloud

Bonne pratique

Dans le cloud, chaque service et chaque utilisateur reçoit des permissions via des politiques IAM. Le même principe qu'en leçon 4 s'applique : des rôles temporaires plutôt que des identifiants permanents, des politiques aussi restrictives que possible, et le MFA systématique même pour les comptes de service quand c'est possible.

Commandes & code

Sécurité des conteneurs et du cloud

dockerfile
# VULNÉRABLE : image Docker mal durcie
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3 python3-pip
COPY . /app
RUN pip install -r /app/requirements.txt
CMD ["python3", "/app/main.py"]
# Problèmes : tourne en root par défaut, image "latest" non pingée, secrets potentiels copiés (COPY .)
dockerfile
# CORRECT : image durcie, multi-stage, utilisateur non-root, dépendances figées
FROM python:3.12-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

FROM python:3.12-slim
RUN groupadd -r technologik && useradd -r -g technologik technologik   # utilisateur dédié non-root
WORKDIR /app
COPY --from=builder /root/.local /home/technologik/.local
COPY --chown=technologik:technologik ./app ./app
USER technologik                                                        # ne PAS tourner en root
ENV PATH=/home/technologik/.local/bin:$PATH
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
dockerfile
# .dockerignore - éviter de copier des fichiers sensibles ou inutiles dans l'image
cat <<'EOF' > .dockerignore
.env
.git
__pycache__
*.pyc
tests/
.venv/
EOF
bash
# Scanner une image pour des vulnérabilités connues dans ses dépendances/paquets système
trivy image technologik-api:2.3.0

# Résultat typique : liste des CVE par paquet avec sévérité, ex:
# openssl 3.0.2  -> CVE-2023-0286 (HIGH) -> corrigé en 3.0.7, mettre à jour l'image de base
yaml
# Durcissement d'un déploiement Kubernetes (securityContext)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: technologik-api
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
        - name: api
          image: technologik-api:2.3.0
          securityContext:
            allowPrivilegeEscalation: false     # empêche l'escalade via setuid dans le conteneur
            readOnlyRootFilesystem: true          # système de fichiers en lecture seule sauf volumes explicites
            capabilities:
              drop: ["ALL"]                        # retire toutes les capabilities Linux par défaut
          resources:
            limits: { cpu: "500m", memory: "256Mi" }   # évite qu'un conteneur compromis épuise le nœud
yaml
# NetworkPolicy Kubernetes : équivalent de la segmentation réseau (voir leçon dédiée) au niveau du cluster
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-access-restricted
spec:
  podSelector:
    matchLabels: { app: postgres }
  policyTypes: ["Ingress"]
  ingress:
    - from:
        - podSelector: { matchLabels: { app: technologik-api } }   # seule l'API peut joindre la DB
      ports:
        - protocol: TCP
          port: 5432
text
# Sécurité cloud : principes IAM (Identity and Access Management)
1. Un rôle de service par application, jamais de clé d'accès personnelle partagée dans le code
2. Politiques IAM restrictives (least privilege) : lister explicitement les actions autorisées
3. Utiliser des identités temporaires (rôles assumables, STS) plutôt que des clés statiques long terme
4. Activer le MFA sur tous les comptes root/administrateur du cloud provider
5. Auditer régulièrement avec des outils dédiés (ex: AWS IAM Access Analyzer, Prowler, ScoutSuite)
bash
# Audit de configuration cloud avec Prowler (exemple AWS, en lab/compte de test)
prowler aws --services s3,iam,ec2 --severity critical,high

# Vérifier qu'aucun bucket S3 n'est accessible publiquement par erreur
aws s3api get-bucket-acl --bucket technologik-backups
aws s3api get-public-access-block --bucket technologik-backups

Résumé

  • Une image Docker durcie tourne en utilisateur non-root, avec dépendances figées et surface minimale.
  • securityContext Kubernetes (non-root, capabilities drop ALL, readOnlyRootFilesystem) réduit fortement l'impact d'une compromission.
  • Trivy et les scanners d'image détectent les CVE connues avant déploiement.
  • Le moindre privilège s'applique aussi à l'IAM cloud : rôles temporaires, politiques restrictives, MFA systématique.

Exercices pratiques

1 disponible
1

Mission : l'audit du cluster Kubernetes avant certification

Objectif : Diagnostiquer trois faiblesses combinées d'un déploiement conteneurisé et prioriser les corrections selon leur impact réel.

Contexte

Un audit de sécurité du cluster Kubernetes de Technologik révèle trois constats sur le déploiement technologik-api : l'image Docker tourne avec l'utilisateur root par défaut, trivy image technologik-api:2.3.0 remonte une CVE HIGH sur une version d'OpenSSL embarquée, et aucune NetworkPolicy ne restreint les pods qui peuvent joindre la base PostgreSQL.

Résoudre l’exercice →