Aller au contenu
Conteneurs & Orchestration medium

Gestion sécurisée des secrets avec Docker

18 min de lecture

logo docker

Lorsque l’on travaille avec Docker, il est tentant d’ajouter ses mots de passe, tokens d’API ou certificats directement dans une image pour simplifier le build ou l’exécution. Erreur fatale ! Une fois dans l’image, ces secrets sont accessibles à quiconque la télécharge. Heureusement, Docker BuildKit et quelques bonnes pratiques permettent de garder vos secrets à l’abri, sans les exposer dans vos conteneurs. Je vous montre comment faire, simplement.

Quand on parle de secrets, on pense souvent à des mots de passe ou des clés API, mais en réalité, cela englobe bien plus : certificats TLS, tokens OAuth, identifiants de bases de données, clés SSH, voire même des fichiers de configuration sensibles contenant des accès à des services tiers. Et tous ces éléments peuvent finir, par inadvertance, dans une image Docker.

À mon avis, beaucoup sous-estiment le danger. Il suffit pourtant d’un ENV mal placé ou d’un COPY trop généreux pour que le mal soit fait. L’image, une fois poussée sur un registre comme Docker Hub, peut être tirée par n’importe qui… avec les secrets inclus.

Prenons un exemple réel : selon une étude menée par GitGuardian, des milliers d’images hébergées sur Docker Hub contiennent encore des clés d’API valides, des jetons AWS ou des identifiants de production. C’est énorme.

Et si vous pensez que GitHub est pire, vous n’avez pas tort : entre fichiers .env oubliés et commits mal nettoyés, la fuite peut aussi venir du dépôt source. Mais une fois le secret dans une image Docker, il devient persistant, partageable et très difficile à effacer. D’ailleurs, j’en profite pour vous partarger un outil de recherche de secrets dans les dépôts GitHub qui réponds au nom de trufflehog.

Voici ce que vous risquez :

  • Piratage de vos services cloud : une clé AWS exposée = vos machines EC2 supprimées ou utilisées pour miner du Bitcoin.
  • Fuites de données : accès à vos bases de données, buckets S3 ou API internes.
  • Compromission de votre chaîne CI/CD : un secret exposé peut donner un accès à vos outils de build, artefacts ou autres conteneurs.

Et dans un contexte DevOps, où tout est automatisé et versionné, le moindre fichier mal géré peut avoir un effet boule de neige.

Même avec de bonnes intentions, il est très facile de laisser échapper un secret. Docker, comme tout outil puissant, ne pardonne pas les raccourcis. Voici les principaux endroits où vos secrets peuvent se retrouver exposés… sans même que vous vous en rendiez compte.

Certaines instructions Docker sont des pièges classiques :

  • ENV SECRET_KEY=123456 : cette valeur devient accessible dans l’image et dans l’historique du build.
  • ARG SECRET_KEY + RUN export SECRET_KEY : même problème, surtout si elle est réutilisée dans une commande.
  • COPY ./config.env /app/config.env : si le fichier contient des secrets, il est maintenant dans l’image.

Et le pire, c’est que même après suppression dans le code source, les couches Docker précédentes conservent les fichiers si on n’a pas rebuildé proprement l’image.

Même si les secrets ne sont pas visibles dans le Dockerfile, ils peuvent se glisser ailleurs :

  • via un script shell embarqué contenant des identifiants,
  • par un fichier de configuration oublié,
  • ou simplement une mauvaise manipulation lors du COPY.

Il suffit ensuite de faire :

Fenêtre de terminal
docker run -it mon_image sh

Et de chercher dans /app, /etc, ou /root pour tomber sur une pépite. Certains outils comme trivy ou dockle peuvent même détecter ces traces.

Les fichiers .sh, .env, config.yaml qu’on utilise dans les builds sont souvent remplis de secrets, parfois en dur, parfois en variables d’environnement. Une ligne comme celle-ci :

Fenêtre de terminal
export AWS_SECRET_KEY=abcdef

…peut finir loggée, transmise ou copiée sans que personne ne s’en aperçoive. Les scripts de build sont souvent les parents pauvres de la sécurité.

C’est une erreur que j’ai vue trop souvent : les secrets apparaissent dans les logs GitLab CI, Jenkins ou autre. Pourquoi ? Parce qu’une commande mal formée, un echo $SECRET, ou une erreur dans le script peut afficher en clair la valeur.

Et dans les pipelines non protégés, les logs sont souvent accessibles à tout le monde du projet.

Une fois en production, un conteneur peut être inspecté par un utilisateur mal intentionné :

Fenêtre de terminal
docker exec -it mon_container cat /app/.env

Si vous avez laissé le fichier traîner, c’est trop tard. Même chose si vous avez monté un volume contenant des secrets sans le restreindre.

Je sens votre tension monter, mais ne vous inquiétez pas, il existe des solutions pour protéger vos secrets. Docker BuildKit et quelques bonnes pratiques vous permettent de garder vos secrets à l’abri. Je vous montre ça tout de suite.

À ce stade, le constat est clair : les risques sont partout et les secrets doivent être traités comme des pièces ultra-sensibles. Heureusement, Docker propose des outils puissants, comme BuildKit, pour éviter tout ça. Je vous montre ça tout de suite. Alors comment ne pas les embarquer dans un container?

Et bien docker, comme Podman, ont commencé à y répondre, mais partiellement. D’ailleurs pour les kubernetiens tout cela doit leur sembler bien naturel puisque pris en charge. Mais je suis sûr qu’ils se sont posé la question pour le build des images.

BuildKit est un backend de construction avancé pour Docker, offrant des améliorations significatives en termes de performance, de gestion du cache et de fonctionnalitésDepuis la version 23.0 de Docker Engine, BuildKit est activé par défaut. Cependant, si vous utilisez une version antérieure, il est recommandé de l’activer manuellement pour bénéficier de ses avantages

Activation de BuildKit sur les versions antérieures

Section intitulée « Activation de BuildKit sur les versions antérieures »

Si votre environnement utilise une version de Docker Engine antérieure à la 23.0, BuildKit n’est pas activé par défautPour l’activer temporairement lors d’une construction, vous pouvez définir la variable d’environnement DOCKER_BUILDKIT à 1

Fenêtre de terminal
export DOCKER_BUILDKIT=1
docker build -t mon_image .

Pour une activation permanente, modifiez le fichier de configuration du démon Docker, généralement situé à /etc/docker/daemon.json, en y ajoutant

{
"features": {
"buildkit": true
}

Après cette modification, redémarrez le démon Docker pour appliquer les changements

L’une des fonctionnalités majeures de BuildKit est la gestion sécurisée des secrets lors du processus de build. Plutôt que d’incorporer directement des informations sensibles dans votre Dockerfile, BuildKit permet de les monter temporairement pendant la construction, garantissant qu’ils ne sont pas inclus dans l’image finale

Exemple pratique : Supposons que vous ayez un fichier config.json contenant des identifiants sensibles nécessaires pour installer des dépendances privées. Voici comment procéder

  1. Préparez votre fichier secret :Assurez-vous que config.json est situé dans le répertoire de build mais est ajouté à votre .dockerignore pour éviter qu’il ne soit copié accidentellement dans l’image

  2. Dans votre Dockerfile, montez le secret :

    1.2
    FROM node:14
    # Définir le répertoire de travail
    WORKDIR /app
    # Monter le secret et installer les dépendances
    RUN --mount=type=secret,id=config \
    npm install --config /run/secrets/config
    # Copier le reste de l application
    COPY . .
    CMD ["node", "app.js"]

Dans cet exemple, le fichier config.json est monté temporairement pendant l’instruction RUN et n’est pas conservé dans l’image finale

  1. Utilisez l’option --secret lors du build :

    Fenêtre de terminal
    docker build --secret id=config,src=config.json -t mon_image .

Vérification post-build : Après la construction, il est essentiel de s’assurer que le secret n’est pas présent dans l’image

Fenêtre de terminal
docker run --rm mon_image
ls /run/secret

Cette commande ne devrait retourner aucun fichier, confirmant que le secret n’est pas inclus dans l’image finale

L’utilisation de BuildKit et de ses fonctionnalités avancées, telles que la gestion des secrets, renforce la sécurité de vos builds Docker.

Monter un secret lors de l’exécution d’un conteneur

Section intitulée « Monter un secret lors de l’exécution d’un conteneur »

Même si on a bien sécurisé l’étape du build, il reste une autre question : comment injecter un secret à un conteneur au moment de son lancement, sans compromettre sa sécurité ? Deux techniques sont particulièrement efficaces : le montage de fichiers temporaires ou l’utilisation de la mémoire (tmpfs).

La méthode la plus classique, c’est de monter un fichier contenant le secret depuis l’hôte vers le conteneur. Cela permet, par exemple, de passer une configuration privée à une application. Mon guide sur les Volumes Docker vous expliquera comment faire.

Exemple :

Fenêtre de terminal
docker run \
--mount type=bind,source="$(pwd)/secrets.json",target=/run/secrets/config.json,readonly \
mon_image
  • Le fichier secrets.json reste sur le système hôte, mais est accessible dans le conteneur à l’emplacement /run/secrets/config.json.
  • L’option readonly permet de s’assurer que l’application dans le conteneur ne pourra pas modifier le secret.

À surveiller :

  • Le fichier reste présent en clair sur le disque de l’hôte.
  • Si le conteneur est compromis, un attaquant pourrait le lire.
  • Il faut donc bien gérer les permissions du fichier en amont (UID, GID, chmod 600…).

Pour aller plus loin en matière de sécurité, Docker permet de monter un système de fichiers temporaire en RAM dans le conteneur. Le gros avantage ? Aucune trace sur le disque.

Commande de base :

Fenêtre de terminal
docker run \
--mount type=tmpfs,destination=/run/secrets \
mon_image

Ce montage crée un répertoire vide, en mémoire uniquement. À vous d’y écrire manuellement vos fichiers secrets au moment de l’exécution (par script d’entrée, par exemple).

Exemple avec injection dynamique :

Si vous avez un script d’entrée :

#!/bin/sh
echo "$SECRET_CONFIG" > /run/secrets/config.json
exec node app.js

Et que vous lancez le conteneur avec :

Fenêtre de terminal
docker run \
--mount type=tmpfs,destination=/run/secrets \
-e SECRET_CONFIG="$(cat config.json)" \
mon_image
  • Le secret est injecté depuis une variable d’environnement, puis écrit en mémoire seulement.
  • À la fin du cycle de vie du conteneur, le fichier est perdu.

Idéal pour :

  • Les applications à usage unique.
  • Les environnements à forte exigence de sécurité.
  • Les secrets à durée de vie courte.

À mon avis, cette méthode est la plus élégante quand on veut une exécution sécurisée sans laisser de trace.

Dans un contexte DevOps, sécuriser les secrets dans les pipelines d’intégration continue est essentiel. Si un secret fuit dans les logs GitLab ou dans une image construite depuis un pipeline, il est trop tard. Heureusement, GitLab CI permet de gérer des variables protégées et masquées, y compris des fichiers.

Ajouter des variables de type “fichier” dans GitLab

Section intitulée « Ajouter des variables de type “fichier” dans GitLab »

Plutôt que de stocker des secrets directement en clair dans un fichier versionné, GitLab offre la possibilité d’ajouter des variables de type fichier depuis l’interface web.

Voici comment faire :

  1. Aller dans Settings > CI/CD > Variables de votre projet.
  2. Cliquer sur Add variable.
  3. Donner un nom à la variable, par exemple SECRET_PIPCONF.
  4. Coller le contenu du fichier dans le champ Value.
  5. Cocher “Mask variable” pour que sa valeur n’apparaisse jamais dans les logs.
  6. Cocher “Protect variable” si vous souhaitez que la variable ne soit utilisée que sur des branches protégées.
  7. Sélectionner le type “File”.

Cette méthode permet à GitLab de stocker le secret de façon sécurisée et de le rendre disponible dans le pipeline sous forme de fichier temporaire.

Récupérer le fichier et le passer à docker build

Section intitulée « Récupérer le fichier et le passer à docker build »

Une fois la variable ajoutée, vous pouvez l’utiliser dans votre fichier .gitlab-ci.yml pour injecter le secret dans votre build Docker avec BuildKit.

Exemple de job CI :

stages:
- build
build_image:
stage: build
image: docker:latest
services:
- docker:dind
variables:
DOCKER_BUILDKIT: 1
script:
- echo "$SECRET_PIPCONF" > ./pip.conf
- docker build --secret id=pipconfig,src=./pip.conf -t mon_image .
- rm -f ./pip.conf

Bonnes pratiques :

  • Supprimer le fichier après le build (rm -f ./pip.conf) pour éviter qu’il ne traîne sur le runner.
  • S’assurer que la variable est bien masquée pour éviter toute fuite dans les logs.
  • Utiliser des runners sécurisés, surtout si les secrets sont sensibles.

Cette approche permet de transmettre un secret au moment du build, sans qu’il soit enregistré dans l’image finale ni exposé dans les fichiers du dépôt.

Même si on utilise --secret et les bonnes pratiques de montage, il reste un moment critique : le stockage des fichiers secrets en amont. Ces fichiers doivent idéalement être chiffrés, pour ne jamais apparaître en clair sur le disque ou dans un dépôt Git.

  • Permet de versionner les fichiers de secrets dans Git sans les exposer.
  • Renforce la sécurité en cas de fuite du dépôt, d’une erreur de manipulation, ou d’un accès non autorisé au disque.
  • Intégration facile avec des outils CI pour déchiffrer à la volée.

SOPS est un outil open source développé par Mozilla, conçu pour chiffrer des fichiers YAML, JSON, ENV ou INI. Il est souvent utilisé avec des backends de gestion de clés comme GPG, AWS KMS, Azure Key Vault ou HashiCorp Vault.

Exemple d’utilisation avec GPG :

  1. Chiffrement du fichier :
Fenêtre de terminal
sops -e -i secrets.json

Le fichier est maintenant chiffré et peut être stocké dans le dépôt.

  1. Déchiffrement dans un pipeline CI :
Fenêtre de terminal
gpg --import ma_cle_privee.asc
sops -d secrets.json > decrypted.json
  1. Utilisation avec Docker BuildKit :
Fenêtre de terminal
docker build --secret id=config,src=decrypted.json -t mon_image .
  1. Nettoyage du fichier déchiffré :
Fenêtre de terminal
rm -f decrypted.json
  • Ne jamais stocker la clé privée GPG dans le dépôt.
  • Protéger les clés avec une passphrase.
  • Ajouter *.dec.* ou les fichiers déchiffrés dans .gitignore.
  • Documenter le processus pour que l’équipe puisse utiliser et maintenir les secrets sans erreurs.

Chiffrer ses secrets avec SOPS, c’est gagner en sécurité tout en gardant un workflow Git fluide. C’est un excellent complément aux mécanismes de docker build et run, surtout dans un environnement CI/CD distribué.

Rappel sur toutes les Bonnes pratiques et les pièges à éviter

Section intitulée « Rappel sur toutes les Bonnes pratiques et les pièges à éviter »

Gérer les secrets avec Docker demande de la rigueur. Un simple oubli peut exposer tout un système. Voici les règles que je m’efforce toujours de respecter et que je recommande vivement dans tous les projets.

Ne jamais utiliser ARG, ENV ou COPY pour les secrets

Section intitulée « Ne jamais utiliser ARG, ENV ou COPY pour les secrets »
  • ARG et ENV insèrent les secrets dans l’historique de l’image.
  • COPY ajoute directement les fichiers sensibles dans l’image.
  • Ces secrets deviennent alors accessibles à quiconque télécharge ou inspecte l’image.

Même si c’est rapide pour un test local, c’est une mauvaise habitude à bannir en production.

  • Ces mécanismes évitent que les secrets soient persistés dans l’image finale.
  • Avec BuildKit, --mount=type=secret permet une injection temporaire à l’étape RUN.
  • En exécution, --mount type=bind ou --mount type=tmpfs offrent un bon niveau de contrôle.
  • Que ce soit dans un pipeline ou un script local, le secret doit toujours être écrit dans un fichier temporaire, puis supprimé.
  • Ces fichiers ne doivent jamais être versionnés, ni visibles en clair sur le disque.

Exemple typique :

Fenêtre de terminal
echo "$SECRET_KEY" > secret.txt
docker build --secret id=key,src=secret.txt .
rm -f secret.txt
  • Restreindre l’accès aux fichiers de secrets : chmod 600, appartenance à un utilisateur spécifique.
  • Ne pas donner de droits root par défaut dans les conteneurs.
  • Si possible, monter les fichiers secrets en lecture seule (readonly).
  • Dans un pipeline CI, un script de build ou un conteneur, les secrets doivent être nettoyés immédiatement après utilisation.
  • Ne pas laisser de fichiers .bak, .tmp ou autres artefacts qui pourraient contenir des données sensibles.
  • Inspecter les images construites avec :

    Fenêtre de terminal
    docker history mon_image
    docker image inspect mon_image
  • Utiliser des outils comme trivy ou dockle pour scanner l’image à la recherche de secrets ou de mauvaises pratiques.

Théorie, c’est bien. Pratique, c’est mieux. Pour vraiment maîtriser Docker et les conteneurs, je mets à ta disposition une série de TP concrets prêts à l’emploi sur mon dépôt GitHub.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80%

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Gérer des secrets avec Docker demande de la discipline. Chaque étape du cycle – build, exécution, CI/CD – peut devenir une faille si elle est négligée. Même avec les bons outils comme BuildKit, rien ne remplace une vigilance constante. À mon avis, le vrai danger, c’est de croire que « ça ira pour cette fois ». Soyons prudents, systématiques et n’oublions jamais qu’un secret exposé… ne l’est jamais par accident.