Aller au contenu

Gestion sécurisée des secrets avec Docker

Mise à jour :

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.

Parfait, je reprends après ton introduction et je rédige directement le chapitre 1 du guide.

Pourquoi protéger ses secrets ?

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.

Exemples concrets de fuites

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.

Quels sont les risques concrets ?

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.

Très bien, voici le chapitre 2 du guide, avec un focus clair sur les zones à risque.

Où vos secrets peuvent fuiter ?

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.

Dans un Dockerfile mal écrit

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.

Dans l’image finale

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 :

Terminal window
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.

Dans les fichiers de build ou les scripts

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 :

Terminal window
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é.

Dans les logs du pipeline

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.

Dans les conteneurs mal sécurisés

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

Terminal window
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.

Comment éviter de divulguer ses secrets ?

À 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.

Bien sûr, poursuivons avec le chapitre 3 de notre guide.

BuildKit : la méthode propre pour le build

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

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

Terminal window
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

Utilisation des secrets avec --mount=type=secret

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 :

    Terminal window
    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

Terminal window
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.

Bien noté. Voici le chapitre 4 en version Markdown propre, sans emoticons, liens ni éléments non textuels. Il est prêt à être copié tel quel dans un fichier .md.

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).

Utiliser --mount type=bind

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 :

Terminal window
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…).

Utiliser --mount type=tmpfs

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 :

Terminal window
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 :

Terminal window
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.

Intégrer les secrets dans un pipeline GitLab CI

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

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

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.

Voici un chapitre complémentaire pour ceux qui souhaitent aller plus loin en intégrant le chiffrement des secrets avec des outils comme SOPS, tout en restant dans un workflow Docker et CI/CD.

Aller plus loin : chiffrer ses secrets avec SOPS

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.

Pourquoi chiffrer les secrets ?

  • 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.

Utiliser Mozilla SOPS

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 :
Terminal window
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 :
Terminal window
gpg --import ma_cle_privee.asc
sops -d secrets.json > decrypted.json
  1. Utilisation avec Docker BuildKit :
Terminal window
docker build --secret id=config,src=decrypted.json -t mon_image .
  1. Nettoyage du fichier déchiffré :
Terminal window
rm -f decrypted.json

Recommandations

  • 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

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

  • 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.

Utiliser exclusivement --secret ou --mount

  • 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.

Stocker les secrets dans des fichiers temporaires

  • 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 :

Terminal window
echo "$SECRET_KEY" > secret.txt
docker build --secret id=key,src=secret.txt .
rm -f secret.txt

Donner les permissions minimales

  • 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).

Nettoyer les fichiers après usage

  • 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.

Toujours vérifier l’image finale

  • Inspecter les images construites avec :

    Terminal window
    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.

Travaux Pratiques Docker : Passe à l’action !

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

Pourquoi ce contrôle ?

Cet contrôle va vous permettre de valider vos connaissances sur le sujet abordé dans le guide. Il comporte des QCM, des questions vrai/faux et des réponses ouvertes à un mot.

🕒 Le chronomètre commence dès que vous cliquez sur Démarrer le test. Vous devrez terminer l’examen avant la fin du temps imparti.

🎯 Pour réussir, vous devez obtenir au moins 80% de bonnes réponses.

💡 Je ne fournis pas directement les réponses aux questions. Cependant, si certaines sont complexes, des pistes d’explication pourront être proposées dans le guide ou après l’examen.

Bonne chance ! 🚀

Conclusion

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.