Introduction : Au-delà de l'Isolation par Défaut
Docker a révolutionné le déploiement applicatif grâce à la conteneurisation. Cependant, s'il offre une isolation des processus, il est crucial de ne pas le considérer comme une solution de sécurité infaillible. La sécurité des conteneurs est une responsabilité partagée qui exige une attention particulière à chaque étape de leur cycle de vie.
Ce guide a pour objectif de vous fournir une checklist opérationnelle et complète pour réduire drastiquement la surface d'attaque de vos conteneurs, en suivant le cycle de vie standard : Build (construction), Ship (distribution/stockage) et Run (exécution).
Note : Ce tutoriel s'adresse aux développeurs, DevOps et administrateurs système souhaitant déployer des conteneurs robustes en environnement de production. Il s'appuie sur des bonnes pratiques reconnues (OWASP, CIS Docker Benchmark, NIST).
Section 1 : La Base - Création d'Images Minimalistes et Durcies (Dockerfile)
La sécurité d'un conteneur commence par la qualité de son image. Chaque binaire, librairie ou outil inutile est une porte d'entrée potentielle pour un attaquant. Le Dockerfile est votre premier outil de durcissement.
-
Choisir une image de base minimale : La taille de l'image est souvent corrélée à sa surface d'attaque. Évitez les images complètes comme
ubuntuoupython:latest. Privilégiez des bases ultra-légères commealpineou, pour une sécurité maximale, les images "distroless" de Google qui ne contiennent que votre application et ses dépendances directes, sans shell ni gestionnaire de paquets.
⚠️ Attention : Alpine utilisemusl libcau lieu deglibc, ce qui peut poser des problèmes de compatibilité avec certaines applications (ex: binaires natifs, drivers). Testez toujours en amont. -
Ne pas exécuter en
root: C'est la règle d'or. Un processus s'échappant d'un conteneur en tant querootaurait potentiellement les privilègesrootsur l'hôte. Créez toujours un utilisateur dédié avec des privilèges minimaux.
💡 Astuce : Utilisez# Dans votre Dockerfile FROM alpine:3.18 # Crée un groupe et un utilisateur non-root RUN addgroup -S appgroup && adduser -S appuser -G appgroup # ... copiez vos fichiers ... # Changez le propriétaire des fichiers de l'application RUN chown -R appuser:appgroup /app # Spécifie l'utilisateur non-root pour l'exécution USER appuser # Lancez votre application CMD ["./mon-application"]USER 1001pour éviter les conflits d'UID avec l'hôte lors du montage de volumes. -
Utiliser les "Multi-stage builds" : Cette technique est essentielle pour ne pas inclure les outils de compilation (compilateurs, SDK,
git, etc.) dans votre image finale. L'image de production ne doit contenir que le strict nécessaire à l'exécution.
💡 Alternative : Pour Node.js, utilisez# Phase 1: La construction (avec tous les outils) FROM golang:1.20-alpine AS builder WORKDIR /src COPY . . # Compile l'application RUN go build -o /app/main . # Phase 2: La production (image minimale) FROM gcr.io/distroless/static-debian11 WORKDIR /app # Copie uniquement le binaire compilé depuis la phase de construction COPY --from=builder /app/main . ENTRYPOINT ["/app/main"]node:alpineen build etnode:slimou une image personnalisée en production. -
Ignorer les fichiers inutiles : Utilisez un fichier
.dockerignorepour exclure les fichiers sensibles ou inutiles du contexte de build (ex:.git,.env,README.md,node_modulessi installé via COPY). Cela améliore aussi les performances de build.# .dockerignore .git .gitignore *.env *.log README.md Dockerfile node_modules npm-debug.log -
Minimiser les couches et regrouper les commandes : Chaque instruction
RUN,COPYouADDcrée une couche. Regroupez les commandes avec&& \pour réduire le nombre de couches et limiter l'exposition des données temporaires.RUN apk add --no-cache ca-certificates wget \ && wget -O /app/data.zip https://api.example.com/data \ && unzip /app/data.zip -d /app/data \ && rm /app/data.zip \ && apk del wget
Section 2 : L'Audit - Analyse de Vulnérabilités des Images
Une fois l'image construite, elle doit être scannée pour détecter des vulnérabilités connues (CVEs) dans ses dépendances. Cette étape est cruciale pour une sécurité proactive.
- Introduction à Trivy :
Trivyest un scanner de vulnérabilités open-source très populaire et simple d'utilisation. Il analyse les paquets du système d'exploitation (viaapk,apt, etc.) et les dépendances applicatives (ex:pip,npm,maven). Il supporte aussi les configurations (Kubernetes, Terraform) et les secrets cachés. -
Exemple pratique : Installez Trivy, puis lancez une analyse sur votre image fraîchement construite.
💡 Conseil : Utilisez# Commande pour scanner une image trivy image votre-app:1.0 # Exemple de sortie (simplifié) =============================================================== mon-app:1.0 (alpine 3.18.3) =============================================================== Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 1, CRITICAL: 0) ┌──────────┬────────────────┬──────────┬────────┬───────────────────┐ │ Library │ Vulnerability │ Severity │ Status │ Fixed Version │ ├──────────┼────────────────┼──────────┼────────┼───────────────────┤ │ busybox │ CVE-2022-30065 │ MEDIUM │ fixed │ 1.36.1-r1 │ │ libcrypto3│ CVE-2023-3817 │ HIGH │ fixed │ 3.1.2-r0 │ │ zlib │ CVE-2023-3982 │ LOW │ fixed │ 1.2.13-r1 │ └──────────┴────────────────┴──────────┴────────┴───────────────────┘trivy image --severity CRITICAL,HIGH --exit-code 1 votre-app:1.0pour échouer le scan en CI/CD si des vulnérabilités critiques sont présentes. -
Intégration CI/CD : Intégrez le scan dans votre pipeline (GitHub Actions, GitLab CI, Jenkins). Bloquez automatiquement le déploiement si des vulnérabilités critiques sont détectées.
# Exemple dans GitHub Actions - name: Scan Docker Image run: | trivy image --exit-code 1 --severity CRITICAL,HIGH --no-progress votre-app:latest -
Alternatives : Explorez aussi
grype(Anchore),clair(CoreOS), ouDocker Scout(officiel, intégré à Docker Hub).
Section 3 : L'Exécution - Configuration d'un Runtime Sécurisé
Construire une image sécurisée est inutile si elle est exécutée avec des privilèges excessifs. Le runtime est la dernière ligne de défense.
- Limiter les "Capabilities" Linux : Par défaut, Docker accorde un ensemble de privilèges (capabilities) au conteneur. Le principe du moindre privilège exige de tous les supprimer et de n'ajouter que ceux qui sont strictement nécessaires.
📌 Autres capabilities utiles :# Supprime tous les privilèges par défaut # N'ajoute que la capacité de se lier à un port privilégié (ex: 80) docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --rm -d votre-appSYS_TIME(horloge),CHOWN(changement propriétaire), mais à utiliser avec parcimonie. - Système de fichiers en lecture seule : Pour empêcher un attaquant qui aurait compromis l'application d'écrire des fichiers malveillants, lancez le conteneur en lecture seule.
💡 Note : Si votre app doit écrire des logs, montez un volume ou un bind mount en lecture/écriture uniquement pour# Le conteneur ne peut plus écrire sur son système de fichiers # On utilise --tmpfs pour les répertoires temporaires docker run --read-only --tmpfs /tmp --tmpfs /run --rm -d votre-app/var/log. - Gestion des secrets : Ne passez JAMAIS de mots de passe ou de clés API en clair via les variables d'environnement (
-e). Elles sont visibles viadocker inspect. Utilisez :- Docker Secrets (dans Swarm)
- Fichiers montés :
--mount type=bind,source=/run/secrets/mysecret,target=/etc/secrets/mysecret - Outils externes : HashiCorp Vault, AWS Secrets Manager, etc.
# Exemple de montage de secret docker run --mount type=bind,source=./app.env,target=/run/secrets/app.env,readonly ... - Sécurité réseau : Limitez les communications réseau inutiles.
# Désactive le réseau si inutile docker run --network none votre-app # Utilise un réseau personnalisé isolé docker network create isolated-net docker run --network isolated-net votre-app - Profils de sécurité avancés : Pour une sécurité renforcée, utilisez :
- AppArmor ou SELinux : pour contrôler les accès aux fichiers et ressources.
- Seccomp : pour bloquer des appels système dangereux (ex:
ptrace,reboot).# Utilisation d'un profil seccomp personnalisé docker run --security-opt seccomp=profil-securise.json votre-app
- Éviter les privilèges élevés : JAMAIS utiliser
--privilegeden production. Évitez aussi--pid=host,--ipc=host, ou-v /var/run/docker.sock:/var/run/docker.sock(accès root sur l'hôte).
Conclusion : Les Piliers de la Sécurité des Conteneurs
La sécurisation des conteneurs n'est pas une action unique mais un processus continu basé sur des principes de défense en profondeur. En résumé :
- Construisez petit : Utilisez des images de base minimalistes, des
multi-stage buildset un.dockerignore. - Ne soyez pas root : Appliquez le principe du moindre privilège à l'intérieur du conteneur (
USER) et à l'exécution (capabilities). - Scannez tout : Intégrez l'analyse de vulnérabilités (
Trivy,Grype) comme une étape obligatoire de votre CI/CD. - Exécutez avec des privilèges minimaux : Utilisez
--read-only,--cap-drop=ALL, des réseaux isolés et des secrets sécurisés. - Surveillez et mettez à jour : Les CVEs évoluent. Repassez régulièrement vos scans et mettez à jour vos images de base.
En appliquant ces techniques, vous réduisez drastiquement la probabilité qu'une vulnérabilité dans votre application ne se transforme en une compromission complète de votre infrastructure. La sécurité est un voyage, pas une destination.
📚 Pour aller plus loin :