← Back to list

Ton Dockerfile est mauvais. Ta production est encore pire.

Ton application est prête à être déployée — ou peut-être qu’elle est déjà en ligne — et tu penses être en sécurité ? Désolé, mais ta…

Benoit Lefebvre · 2025-10-24 15:29 · 0 claps · 5.3 min read
#french #production #caelus #containers #dockerfiles
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Ton Dockerfile est mauvais. Ta production est encore pire.

Ton application est prête à être déployée — ou peut-être qu’elle est déjà en ligne — et tu penses être en sécurité ? Désolé, mais ta production fonce droit dans un mur. 🧱

Cette fois, l’histoire est gratuite et accessible à tous ! Je serais ravi si tu passais dire bonjour sur ma page LinkedIn. 😳

Commençons par la façon dont vous écrivez votre Dockerfile, puis nous verrons quelles étapes ajouter pour améliorer la sécurité. Je vais vous montrer comment créer un Dockerfile NodeJS correct. Cependant, vous pouvez appliquer toutes les bonnes pratiques à n’importe quelle image que vous créez.

Par exemple, supposons que ce Dockerfile soit votre Dockerfile actuel (mieux vaut vous asseoir avant de le lire) :

FROM node
WORKDIR /app
COPY . .
RUN apt-get update && \
    apt-get install -y python3 g++ make git && \
    rm -rf /var/lib/apt/lists/*COPY package*.json ./
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]

La taille de l’image dépend des dépendances et des fichiers sources de votre projet, mais vous pouvez vous attendre à ce qu’elle fasse au moins 1,14 Go. 😯

Optimisation de votre Dockerfile actuel

a. Choisir une image de base minimale

Tout d’abord, vous devriez reconsidérer l’image FROM que vous avez choisie. La plupart des images disponibles sur Docker Hub proposent plusieurs tags. Par exemple, comparons quelques tags de l’image officielle NodeJS :

[embed]

Vous pouvez facilement comprendre ce que je veux vous montrer : la taille de votre image de base est la clé pour la performance et la sécurité.

Plus l’image de base est grande, plus votre surface d’attaque est importante. Comme elle inclut davantage de dépendances, elle apportera aussi plus de problèmes de sécurité.

Alors, laquelle devriez-vous choisir ? Cela dépend des besoins de votre application. Mais, la plupart du temps, votre image de base finale devrait être slim ou alpine. C’est pour cela que vous devriez utiliser les Multi-Stage Builds. 🧩

C’est ce qui explique pourquoi votre image actuelle fait 1,14 Go. Maintenant, divisons-la par 7 ! 🔥

b. Multi-Stages Build

Si vous avez encore besoin de ces dépendances pour compiler ou configurer l’image, vous devriez utiliser les multi-stage builds. De cette façon, vous pourrez utiliser l’image volumineuse (uniquement pendant le processus de build) et obtenir le résultat final dans l’image de base minimale.

Voici un exemple :

# BUILD WITH LARGE IMAGE (needing native build tools)
FROM node:bookworm AS build
WORKDIR /app
RUN apt-get update && \
    apt-get install -y python3 g++ make git && \
    rm -rf /var/lib/apt/lists/*COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# THEN USE FINAL MINIMAL BASE IMAGE
FROM node:alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]

Laissez-moi vous expliquer pourquoi cela change tout. L’image finale de votre application est maintenant plus petite et :

  • Sera téléchargée et décompressée plus rapidement (très important lorsque vous devez scaler en production).
  • Présentera moins de risques grâce à une surface d’attaque réduite.
  • Consommera moins, ce qui réduit les coûts liés au cloud computing. 🌳💶

c. Optimisez le cache de votre environnement de production

Ce n’est pas un secret, l’un des atouts des conteneurs est qu’ils gèrent le stockage partagé entre différentes images de conteneur.

Voici un schéma très simplifié pour expliquer ce que je veux dire. Chaque commande RUN ou COPY crée une nouvelle couche, identifiée par un checksum unique. Ainsi, si vous utilisez la même image de base et installez les mêmes dépendances, elles ne seront stockées qu’une seule fois et réutilisées.

Dans un contexte de production, votre application sera extraite et déployée plus rapidement.

Sécuriser votre image

a. Corriger les permissions

Vous avez probablement déjà entendu ce conseil : NE PAS UTILISER L’UTILISATEUR ROOT.

Lorsque votre conteneur est déployé, il a un accès direct au système d’exploitation hôte. Et si votre application permet au conteneur de s’exécuter en tant que root, vous laissez votre application (et ses failles de sécurité) fonctionner en root sur votre hôte.

# BUILD WITH LARGE IMAGE (needing native build tools)
FROM node:bookworm AS build
WORKDIR /app
RUN apt-get update && \
    apt-get install -y python3 g++ make git && \
    rm -rf /var/lib/apt/lists/*
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# THEN USE FINAL MINIMAL BASE IMAGE
FROM node:alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && \
    adduser -S node -u 1001 -G nodejs
COPY --chown=node:nodejs package*.json ./
RUN npm install --production
COPY --from=build --chown=node:nodejs /app/dist ./dist
# Switch to non-root user
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]

Ici, j’ai créé un nouveau groupe système ainsi qu’un nouvel utilisateur dans ce groupe. De cette façon, l’utilisateur par défaut n’a pas de permissions sur l’hôte. Ensuite, nous changeons la propriété des fichiers de l’image finale dans l’image finale.

b. Gestion des secrets

Tout d’abord, les secrets ne doivent pas être mis en clair directement dans le Dockerfile :

...
# THEN USE FINAL MINIMAL BASE IMAGE
FROM node:alpine AS production
ENV DB_USER=test #✖
ENV DB_PASSWORD=whatapassword #✖
WORKDIR /app
...

Quiconque lira ce Dockerfile dans le dépôt pourra obtenir toutes les informations précieuses qui devraient rester secrètes.

Évoluons donc vers ceci :

...
# THEN USE FINAL MINIMAL BASE IMAGE
FROM node:alpine AS production
ENV DB_USER
ENV DB_PASSWORD
WORKDIR /app
...

Et l’ajouter au moment de l’exécution :

docker run -e DB_USER=test -e DB_PASSWORD=whatapassword myapp

Très bien, c’est mieux, mais il reste encore des problèmes de sécurité à résoudre. Vous devez savoir que vos informations sensibles peuvent se retrouver dans les variables d’environnement, et donc être affichées dans les journaux via console.log(process.env). Vous pouvez également voir ces mots de passe avec la commande docker inspect myapp.

Alors, que faire ?

Il existe plusieurs façons de gérer correctement ces secrets :

  • Passer le secret en volume, en utilisant docker run -v /path/secret:/run/secret:ro. Cela ajoutera le secret sous forme de fichier directement à l’intérieur du conteneur.
  • Utiliser un gestionnaire de secrets externe (comme Vault ou un KMS spécifique).

c. Scanner les vulnérabilités

Il existe plusieurs outils pour analyser les vulnérabilités de votre image Docker :

  • Trivy (principalement gratuit, outil tout-en-un)
  • Grype (via Syft : crée un SBOM pour lister le contenu et les licences du conteneur) — gratuit et open source
  • Snyk (payant, avec une interface CLI)

Utilisons Trivy cette fois et voyons ce que nous pouvons en tirer ! 🥷 (Voici le guide parfait pour l’installer)

Vous pouvez lancer votre premier scan comme ceci :

docker pull your-nodejs:version
trivy image your-nodejs:version

Cela affichera une liste de toutes les vulnérabilités trouvées dans les images. Ensuite, vérifiez si des mises à jour sont disponibles pour ces vulnérabilités ou, à défaut, cherchez des alternatives.

De plus, vous pouvez également analyser le code source, les configurations (IaC, Kubernetes) et les secrets :

# scan a project directory
trivy fs .
# scan a git repo 
trivy repo https://github.com/aquasecurity/trivy-ci-test
# Kubernetes manifest
trivy config ./k8s-manifests/
# detect secrets in your repo or files
trivy fs --security-checks secret .

Vous devriez intégrer ce processus directement dans votre pipeline CI/CD afin de garder vos conteneurs et vos builds à jour. 💚

Enfin, vous pouvez également générer votre SBOM afin d’obtenir des fichiers relatifs à votre chaîne d’approvisionnement ou aux versions des paquets :

# CycloneDX
trivy image --format cyclonedx --output sbom.json myapp:latest
# SPDX
trivy image --format spdx-json --output sbom.spdx.json myapp:latest

C’est tout pour aujourd’hui ! On se retrouve la semaine prochaine pour un nouvel article technique ! 💚

Sources (et pour aller plus loin) :


메타데이터
post_id
0d9ac8dc5db3
slug
ton-dockerfile-est-mauvais-ta-production-est-encore-pire-0d9ac8dc5db3
url
https://medium.com/@benoitlefebvre99/ton-dockerfile-est-mauvais-ta-production-est-encore-pire-0d9ac8dc5db3
canonical_url
https://medium.com/@benoitlefebvre99/ton-dockerfile-est-mauvais-ta-production-est-encore-pire-0d9ac8dc5db3
author_url
https://medium.com/@benoitlefebvre99
status
ok
fetched_at
2026-07-16 05:39:09