Exercice 03 : Sécuriser un Dockerfile contre les vulnérabilités
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Éviter d'exécuter des conteneurs en
rootavecUSER 1001 - ✅ Éviter les secrets dans
ARG/ENVau build - ✅ Utiliser le multi-stage build pour réduire la surface d'attaque
- ✅ Utiliser
COPYspécifique au lieu deCOPY . . - ✅ Choisir une image de base minimale (alpine/distroless)
Durée estimée : 20 minutes
Difficulté : ⭐⭐⭐☆☆ (Intermédiaire)
Prérequis : Exercices 01 et 02 complétés, notions de Docker
📖 Contexte
Un Dockerfile mal écrit peut créer des vulnérabilités graves : exécution en root (compromet l'hôte si breakout), secrets exposés dans les layers (récupérables avec docker history), image volumineuse (plus de packages = plus de CVE). La sécurité d'un conteneur commence à la ligne 1 du Dockerfile.
📋 Énoncé
Analysez un Dockerfile volontairement mal écrit, identifiez les problèmes de sécurité, et réécrivez-le en appliquant les bonnes pratiques.
🧭 Déroulement de l'exercice
Tâche 1 : Analyser un Dockerfile vulnérable
Créez ce Dockerfile problématique et identifiez les 5 problèmes de sécurité :
FROM python:3.10
ARG API_KEY=sk-default-key
ENV SECRET_TOKEN=hardcoded_token_123
COPY . .
RUN pip install -r requirements.txt
RUN apt-get install -y curl wget vim
EXPOSE 8080
CMD ["python", "app.py"]Indice : Les 5 problèmes sont : (1) image de base trop lourde, (2)
ARG/ENVexposent des secrets dans les layers, (3)COPY . .copie potentiellement des fichiers sensibles (.env, .git), (4) installation de packages inutiles, (5) pas deUSER- tourne en root.
Vérification : Vous listez les 5 problèmes et comprenez le risque de chacun. docker history mon-image montre les ARG/ENV dans les layers.
Tâche 2 : Ajouter USER pour éviter root
Corrigez d'abord le problème le plus critique : l'exécution en root.
Indice :
`dockerfileFROM python:3.10-slim
>
# Créer un utilisateur non-privilégié
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
>
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
>
COPY src/ ./src/
>
# Changer le propriétaire et switcher l'utilisateur
RUN chown -R appuser:appgroup /app
USER appuser
>
CMD ["python", "src/app.py"]
`
Vérification : docker run mon-image whoami retourne appuser, pas root.
Tâche 3 : Éviter les secrets dans les layers
Remplacez les ARG/ENV contenant des secrets par des variables passées au runtime :
Indice :
`dockerfile# MAUVAIS : visible dans docker history et docker inspect
ARG API_KEY
ENV SECRET_TOKEN=$API_KEY
>
# BON : ne pas mettre de secrets dans le Dockerfile
# Les passer au docker run avec -e ou --env-file
`
>
Dans l'application :
`pythonimport os
api_key = os.environ.get("API_KEY") # Injecté au runtime
`
Vérification : docker history mon-image ne montre aucune valeur de secret. docker inspect mon-image ne contient pas de token.
Tâche 4 : Utiliser le multi-stage build
Créez un Dockerfile multi-stage pour une application Go ou Python qui sépare l'environnement de build de l'image finale :
Indice :
`dockerfile# Stage 1 : Build
FROM python:3.12 AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
>
# Stage 2 : Runtime (image minimale)
FROM python:3.12-slim AS runtime
WORKDIR /app
>
# Copier seulement les dépendances installées
COPY --from=builder /root/.local /root/.local
COPY src/ ./src/
>
RUN groupadd -r app && useradd -r -g app app
USER app
>
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "src/app.py"]
`
Vérification : L'image finale ne contient pas pip, les outils de compilation, ni les fichiers temporaires du build. Taille réduite.
Tâche 5 : Utiliser .dockerignore
Créez un .dockerignore pour éviter de copier des fichiers sensibles :
Indice :
`.git/
.env
.env.*
*.secret
__pycache__/
*.pyc
.pytest_cache/
node_modules/
.DS_Store
README.md
tests/
docs/
`
Vérification : Buildez l'image avec COPY . . et vérifiez que .env n'est pas dans le conteneur : docker run mon-image ls -la ne doit pas afficher .env.
🗂️ Mini-Projet
Réécrivez le Dockerfile vulnérable initial en Dockerfile sécurisé complet. Comparez les tailles avec docker images et les résultats de scan Trivy avant/après.
# Comparer les tailles
docker images | grep mon-app
# Scanner avant
trivy image mon-app:vulnerable
# Scanner après
trivy image mon-app:secureCheckpoints :
- Le conteneur ne tourne pas en root (
whoami=appuser) - Aucun secret dans
docker historynidocker inspect .dockerignoreexclut.envet.git- L'image multi-stage est plus petite que l'image originale
- Trivy trouve moins de vulnérabilités sur l'image sécurisée