Aller au contenu
Conteneurs & Orchestration medium

Secrets Docker : Gérer les données sensibles en toute sécurité

18 min de lecture

logo docker

Un secret est toute donnée sensible que vous ne voulez pas exposer : mot de passe de base de données, clé API, certificat TLS, token d’authentification. Lorsque vous travaillez avec Docker, il est tentant de passer ces secrets via des variables d’environnement ou de les copier directement dans l’image. Erreur fatale ! Une fois dans l’image, ces secrets sont accessibles à quiconque la télécharge. Ce guide vous montre comment gérer vos secrets proprement, au build comme au runtime.

  • Pourquoi c’est critique : où vos secrets peuvent fuiter (images, logs, historique)
  • BuildKit secrets : injection sécurisée au moment du build avec --mount=type=secret
  • Docker Compose secrets : gestion déclarative des secrets au runtime
  • Injection au runtime : bind mounts, tmpfs et variables d’environnement
  • CI/CD : intégration avec GitLab CI, GitHub Actions
  • Bonnes pratiques : chiffrement avec SOPS, audit des images
  • Connaissance de base des Dockerfile et de la CLI Docker
  • Accès à un environnement Docker fonctionnel

Imaginez que vous construisiez une maison (votre image Docker). Vous avez besoin d’un code d’alarme temporaire pendant les travaux (le secret au build). Si vous gravez ce code dans le béton (l’image), tous les futurs propriétaires et visiteurs pourront le lire. La bonne approche : utiliser un code temporaire que vous effacez après les travaux, et installer un vrai système de sécurité pour les habitants (secrets au runtime).

Selon une étude GitGuardian, des milliers d’images sur Docker Hub contiennent encore des clés AWS valides, tokens d’API et identifiants de production.

RisqueConséquenceExemple réel
Piratage cloudMachines supprimées ou utilisées pour miner des cryptosClé AWS exposée → facture de 50 000€
Fuite de donnéesAccès non autorisé à vos bases de donnéesToken PostgreSQL → dump de la BDD clients
Compromission CI/CDAccès à tous vos pipelines et artefactsSecret GitLab → push de code malveillant
Attaque supply chainInfection de toutes les images dérivéesToken NPM → package malveillant publié

Comprendre où les secrets peuvent fuiter est la première étape pour les protéger.

VecteurExemplePourquoi c’est dangereux
DockerfileENV API_KEY=abc123Visible dans l’historique et l’image
Image finaleCOPY .env /app/Accessible à quiconque pull l’image
Logs CI/CDecho $SECRET dans un scriptLogs souvent accessibles à toute l’équipe
Conteneur runtimedocker exec envVariables d’environnement visibles
Git historyCommit d’un .env puis suppressiongit log révèle tout
# ❌ INTERDIT : Le secret reste dans l'historique de l'image
ENV DATABASE_PASSWORD=supersecret
# ❌ INTERDIT : Le fichier est copié dans une couche permanente
COPY config.env /app/config.env
# ❌ INTERDIT : ARG est visible dans docker history
ARG NPM_TOKEN
RUN npm install --registry=https://:${NPM_TOKEN}@npm.pkg.github.com

Vérification : Vous pouvez voir ces secrets avec docker history :

Fenêtre de terminal
docker history mon_image --no-trunc
# Affiche toutes les instructions, y compris les ARG et ENV

Même sans être dans l’image finale, un secret peut fuiter dans les logs :

Fenêtre de terminal
# ❌ DANGEREUX : Le secret apparaît dans les logs
echo "Connexion avec le token: $API_TOKEN"
# ✅ SÉCURISÉ : Ne jamais afficher de secret
echo "Connexion au service..."

BuildKit est le moteur de build moderne de Docker, activé par défaut depuis Docker 23.0. Sa fonctionnalité phare pour la sécurité : le montage temporaire de secrets avec --mount=type=secret.

Le secret est monté uniquement pendant l’exécution d’une instruction RUN, puis disparaît. Il n’est jamais stocké dans une couche de l’image.

Fonctionnement du secret mount BuildKit : le secret est injecté temporairement pendant le RUN puis supprimé

# syntax=docker/dockerfile:1
FROM python:3.12-slim
WORKDIR /app
# Le secret est monté dans /run/secrets/pip_conf pendant le RUN
RUN --mount=type=secret,id=pip_conf,target=/etc/pip.conf \
pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Exemple complet : accès à un registre NPM privé

Section intitulée « Exemple complet : accès à un registre NPM privé »
  1. Créer le fichier .npmrc local (ne pas le commiter !)

    Fenêtre de terminal
    echo "//npm.pkg.github.com/:_authToken=${NPM_TOKEN}" > .npmrc
    echo ".npmrc" >> .gitignore
  2. Écrire le Dockerfile avec mount secret

    # syntax=docker/dockerfile:1
    FROM node:20-alpine
    WORKDIR /app
    COPY package*.json ./
    # Le .npmrc est monté temporairement pour npm install
    RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm ci --only=production
    COPY . .
    CMD ["node", "server.js"]
  3. Builder l’image avec le secret

    Fenêtre de terminal
    docker build --secret id=npmrc,src=.npmrc -t mon-app .
  4. Vérifier que le secret n’est pas dans l’image

    Fenêtre de terminal
    # Le fichier ne doit pas exister
    docker run --rm mon-app cat /root/.npmrc
    # cat: /root/.npmrc: No such file or directory
    # L'historique ne doit pas contenir le token
    docker history mon-app --no-trunc | grep -i token
    # (aucun résultat)
OptionDescriptionExemple
idIdentifiant du secret (obligatoire)id=my_secret
targetChemin de montage dans le conteneurtarget=/etc/config.json
requiredÉchoue si le secret n’est pas fournirequired=true
modePermissions du fichier (octal)mode=0400
uid, gidPropriétaire du fichieruid=1000,gid=1000
# Exemple avec toutes les options
RUN --mount=type=secret,id=db_password,target=/run/secrets/db,required=true,mode=0400,uid=1000 \
/app/init-db.sh

Depuis BuildKit, vous pouvez aussi injecter un secret directement comme variable d’environnement (sans fichier) :

RUN --mount=type=secret,id=api_key,env=API_KEY \
curl -H "Authorization: Bearer $API_KEY" https://api.example.com/data

Docker Compose propose un mécanisme déclaratif pour injecter des secrets dans les conteneurs au moment de l’exécution. Les secrets sont montés en tant que fichiers dans /run/secrets/.

services:
app:
image: mon-app
secrets:
- db_password # Monté dans /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt # Source du secret
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_root_password
- db_password
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:latest
depends_on:
- db
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
db_root_password:
file: ./secrets/db_root_password.txt
volumes:
db_data:
SourceSyntaxeCas d’usage
Fichier localfile: ./secret.txtDéveloppement, fichiers chiffrés avec SOPS
Variable d’environnementenvironment: MY_VARCI/CD, secrets injectés par la plateforme
Secret externeexternal: trueDocker Swarm, secrets pré-créés
secrets:
# Depuis un fichier
from_file:
file: ./secrets/api_key.txt
# Depuis une variable d'environnement
from_env:
environment: API_KEY
# Secret externe (Docker Swarm)
from_swarm:
external: true

Docker Compose permet aussi de passer des secrets au moment du build :

services:
app:
build:
context: .
secrets:
- npm_token
secrets:
npm_token:
environment: NPM_TOKEN

En dehors de Docker Compose, plusieurs méthodes permettent d’injecter des secrets au lancement d’un conteneur.

MéthodeSécuritéPersistanceCas d’usage
Variables d’environnement⚠️ FaiblePendant la vie du conteneurDebug, développement local
Bind mount (readonly)✅ MoyenneFichier sur l’hôteProduction simple
tmpfs✅✅ ÉlevéeRAM uniquementSecrets éphémères, haute sécurité
Docker Swarm secrets✅✅ ÉlevéeChiffré at-restClusters Swarm
Vault/External✅✅✅ Très élevéeExterneEnterprise, compliance

Variables d’environnement (à éviter en production)

Section intitulée « Variables d’environnement (à éviter en production) »
Fenêtre de terminal
# ⚠️ Le secret est visible avec docker inspect
docker run -e DATABASE_PASSWORD=secret mon-app
# ⚠️ Aussi visible avec docker exec
docker exec mon-container env | grep PASSWORD

Quand c’est acceptable : développement local, tests, conteneurs éphémères jetables.

Fenêtre de terminal
docker run \
--mount type=bind,source="$(pwd)/secrets/config.json",target=/run/secrets/config.json,readonly \
mon-app

Avantages : Simple, le fichier reste sur l’hôte (pas dans l’image). Inconvénients : Le fichier existe en clair sur le disque de l’hôte.

Le montage tmpfs stocke les données uniquement en RAM. Aucune trace sur le disque.

Fenêtre de terminal
# Créer un tmpfs pour les secrets
docker run \
--mount type=tmpfs,destination=/run/secrets,tmpfs-size=1m,tmpfs-mode=0700 \
-e SECRET_DATA="$(cat secret.json)" \
--entrypoint /bin/sh mon-app \
-c 'echo "$SECRET_DATA" > /run/secrets/config.json && exec /app/start.sh'

GitLab permet de définir des variables masquées et de type fichier.

  1. Configurer la variable dans GitLab

    • Aller dans Settings > CI/CD > Variables
    • Ajouter une variable NPM_TOKEN
    • Cocher Mask variable et Protect variable
  2. Utiliser dans .gitlab-ci.yml

    build:
    stage: build
    image: docker:latest
    services:
    - docker:dind
    variables:
    DOCKER_BUILDKIT: "1"
    script:
    # Créer le fichier temporaire
    - echo "$NPM_TOKEN" > .npmrc
    # Builder avec le secret
    - docker build --secret id=npmrc,src=.npmrc -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    # Nettoyer
    - rm -f .npmrc
    # Pousser l'image
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
name: Build and Push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build with secrets
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
secrets: |
"npm_token=${{ secrets.NPM_TOKEN }}"

Même avec --secret, les fichiers de secrets doivent être protégés avant le build. SOPS (Secrets OPerationS) permet de chiffrer des fichiers YAML, JSON ou ENV tout en gardant les clés lisibles.

  1. Chiffrer le fichier de secrets

    Fenêtre de terminal
    # Avec une clé GPG
    sops -e -i secrets.json
    # Avec AWS KMS
    sops --kms arn:aws:kms:eu-west-1:123:key/abc -e -i secrets.json
  2. Commiter le fichier chiffré (les valeurs sont illisibles)

    {
    "api_key": "ENC[AES256_GCM,data:abc123...,type:str]",
    "db_password": "ENC[AES256_GCM,data:def456...,type:str]"
    }
  3. Déchiffrer dans le pipeline CI

    Fenêtre de terminal
    # Le runner a accès à la clé de déchiffrement
    sops -d secrets.json > decrypted.json
    docker build --secret id=config,src=decrypted.json -t mon-app .
    rm -f decrypted.json
❌ Ne jamais faire✅ Toujours faire
ENV SECRET=xxx dans Dockerfile--mount=type=secret avec BuildKit
COPY .env /app/Monter le secret au runtime
ARG pour les secretsSecrets Compose ou tmpfs
Commiter des secrets en clairChiffrer avec SOPS
echo $SECRET dans les logsMasquer et ne jamais afficher
Fenêtre de terminal
# Vérifier qu'aucun secret n'est dans l'historique
docker history mon-image --no-trunc | grep -iE 'password|secret|token|key'
# Vérifier le contenu de l'image
docker run --rm mon-image find / -name "*.env" -o -name "*secret*" 2>/dev/null
# Scanner avec Trivy
trivy image mon-image --scanners secret
# Scanner avec trufflehog
trufflehog docker --image mon-image
OutilUsageLien
TrivyScan d’images DockerGuide Trivy
trufflehogDétection dans Git et imagesGuide trufflehog
GitleaksPre-commit hookgitleaks.io
DockleAudit de bonnes pratiques Dockergoodwithtech/dockle
SymptômeCause probableSolution
secret not foundSecret non passé au buildVérifier --secret id=xxx,src=fichier
Secret visible dans docker historyUtilisation de ARG/ENVMigrer vers --mount=type=secret
Permission denied sur /run/secretsMode trop restrictifAjuster mode=0400 ou uid/gid
Secret vide dans le conteneurFichier source inexistantVérifier le chemin du src
_FILE ne fonctionne pasImage ne supporte pas la conventionLire le fichier dans un entrypoint custom
Fenêtre de terminal
# Doit afficher "buildkit" ou "1"
docker info | grep -i buildkit
# Forcer BuildKit si nécessaire
DOCKER_BUILDKIT=1 docker build ...
  1. Ne jamais utiliser ARG, ENV ou COPY pour les secrets dans un Dockerfile — ils persistent dans l’image.

  2. BuildKit --mount=type=secret est la méthode recommandée pour les secrets au build — le secret est temporaire et n’est jamais stocké.

  3. Docker Compose secrets offre une gestion déclarative au runtime — les secrets sont montés dans /run/secrets/.

  4. Préférer les fichiers aux variables d’environnement — les env vars fuient facilement via logs, proc, inspect.

  5. tmpfs pour les secrets ultra-sensibles — aucune trace sur le disque, uniquement en RAM.

  6. Chiffrer les fichiers de secrets avec SOPS avant de les commiter — permet de versionner sans exposer.

  7. Scanner régulièrement vos images avec Trivy ou trufflehog — la détection automatique complète la prévention.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
8 min.
70% requis

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