Gestion sécurisée des secrets avec Docker
Mise à jour :
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 :
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 :
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é :
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ésDepuis 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éfautPour l’activer temporairement lors
d’une construction, vous pouvez définir la variable d’environnement
DOCKER_BUILDKIT
à 1
export DOCKER_BUILDKIT=1docker 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
-
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 -
Dans votre Dockerfile, montez le secret :
1.2 FROM node:14# Définir le répertoire de travailWORKDIR /app# Monter le secret et installer les dépendancesRUN --mount=type=secret,id=config \npm install --config /run/secrets/config# Copier le reste de l applicationCOPY . .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
-
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
docker run --rm mon_imagels /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 :
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 :
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/shecho "$SECRET_CONFIG" > /run/secrets/config.jsonexec node app.js
Et que vous lancez le conteneur avec :
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 :
- Aller dans Settings > CI/CD > Variables de votre projet.
- Cliquer sur Add variable.
- Donner un nom à la variable, par exemple
SECRET_PIPCONF
. - Coller le contenu du fichier dans le champ Value.
- Cocher “Mask variable” pour que sa valeur n’apparaisse jamais dans les logs.
- Cocher “Protect variable” si vous souhaitez que la variable ne soit utilisée que sur des branches protégées.
- 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 :
- Chiffrement du fichier :
sops -e -i secrets.json
Le fichier est maintenant chiffré et peut être stocké dans le dépôt.
- Déchiffrement dans un pipeline CI :
gpg --import ma_cle_privee.ascsops -d secrets.json > decrypted.json
- Utilisation avec Docker BuildKit :
docker build --secret id=config,src=decrypted.json -t mon_image .
- Nettoyage du fichier déchiffré :
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
etENV
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’étapeRUN
. - 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 :
echo "$SECRET_KEY" > secret.txtdocker 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_imagedocker image inspect mon_image -
Utiliser des outils comme
trivy
oudockle
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.