Maîtrise des Variables d'Environnement dans Docker : Configuration vs Secrets
Déployez des applications Docker de manière sécurisée et flexible en maîtrisant les variables d'environnement. Ce guide complet clarifie la distinction cruciale entre l'utilisation des variables d'environnement pour la configuration générale de l'application et la gestion sécurisée des données sensibles comme les clés API et les mots de passe. Apprenez des méthodes pratiques pour transmettre des paramètres non sensibles, comprenez les risques graves de l'exposition des secrets via les variables d'environnement, et découvrez comment exploiter Docker Secrets et Compose pour une gestion robuste et chiffrée des secrets. Élevez vos connaissances Docker et protégez vos applications.
Maîtrise des Variables d'Environnement dans Docker : Configuration vs Secrets
Les variables d'environnement sont pratiques dans Docker car elles permettent à la même image de fonctionner en développement, en préproduction et en production avec des paramètres différents. Cette commodité devient risquée lorsque les équipes placent des mots de passe, des clés de signature et des jetons API dans le même panier que les niveaux de journalisation et les numéros de port.
Le modèle mental clair est simple : les variables d'environnement sont acceptables pour la configuration d'exécution non sensible. Les secrets doivent provenir d'un coffre-fort de secrets ou d'un fichier de secret monté, avec un accès limité et un plan de rotation.
Comprendre les Variables d'Environnement pour la Configuration
Les variables d'environnement sont une méthode simple et largement adoptée pour transmettre une configuration d'exécution aux applications, y compris celles qui s'exécutent dans des conteneurs Docker. Elles vous permettent de modifier le comportement d'une application sans reconstruire l'image Docker, rendant vos conteneurs plus flexibles et portables. C'est idéal pour les paramètres dynamiques non sensibles comme les numéros de port d'application, les indicateurs de débogage ou les URL de services tiers.
Méthodes pour Transmettre les Variables de Configuration
Docker propose plusieurs façons de définir et d'injecter des variables d'environnement dans vos conteneurs :
1. Instruction ENV dans le Dockerfile
L'instruction ENV définit une variable d'environnement par défaut qui sera disponible à l'intérieur du conteneur lors de son exécution. Cela convient aux variables qui sont peu susceptibles de changer ou qui fournissent des valeurs par défaut raisonnables pour votre application.
FROM alpine:latest
ENV APP_PORT=8080
ENV DEBUG_MODE=false
COPY ./app /app
WORKDIR /app
CMD ["/app/start.sh"]
Astuce : Bien que ENV définisse des valeurs par défaut, celles-ci peuvent être remplacées au moment de l'exécution.
2. Drapeau -e ou --env avec docker run
Lors du lancement d'un seul conteneur, vous pouvez utiliser le drapeau -e ou --env pour passer directement des variables d'environnement. C'est courant pour les tests ad hoc ou pour fournir des paramètres spécifiques qui diffèrent des valeurs par défaut du Dockerfile.
docker run -d -p 80:8080 --name my_app_instance \
-e APP_PORT=80 \
-e DEBUG_MODE=true \
my_app_image:latest
3. env_file dans Docker Compose
Pour gérer plusieurs variables d'environnement, en particulier sur plusieurs services définis dans un fichier docker-compose.yml, l'option env_file est très pratique. Elle vous permet de charger des variables à partir d'un ou plusieurs fichiers .env, ce qui rend votre docker-compose.yml plus propre.
docker-compose.yml :
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
env_file:
- ./config/app.env
./config/app.env :
APP_PORT=8080
DEBUG_MODE=false
API_ENDPOINT=https://api.example.com/v1
4. Clé environment dans Docker Compose
Alternativement, vous pouvez définir des variables d'environnement directement dans la section environment d'un service dans docker-compose.yml. Ceci est souvent préféré pour un petit nombre de variables ou pour des variables spécifiques à un seul service.
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
environment:
APP_PORT: 8080
DEBUG_MODE: false
Les Pièges de l'Utilisation des Variables d'Environnement pour les Secrets
Bien que les variables d'environnement soient excellentes pour la configuration, elles ne sont fondamentalement pas sécurisées pour la gestion des données sensibles (secrets) comme les mots de passe de base de données, les clés API ou les clés SSH privées. C'est une vulnérabilité de sécurité critique qui est souvent négligée, en particulier dans les environnements de développement.
Pourquoi les Variables d'Environnement sont Dangereuses pour les Secrets :
Visibilité via
docker inspect: Toute personne ayant accès à l'hôte Docker peut facilement voir les variables d'environnement d'un conteneur en cours d'exécution en utilisantdocker inspect <container_id>. Cela signifie que vos secrets sont clairement visibles en texte brut.# Exemple d'exposition d'un secret (NE FAITES PAS CELA EN PRODUCTION) docker run -d -e DB_PASSWORD=mysecretpassword --name insecure_app nginx:latest # N'importe qui peut voir le mot de passe docker inspect insecure_app | grep DB_PASSWORDEspionnage des Processus : À l'intérieur du conteneur, d'autres processus ou utilisateurs (si plusieurs utilisateurs existent) pourraient être en mesure de lire les variables d'environnement, surtout si l'application s'exécute en tant que root ou avec des privilèges élevés.
Journalisation et Historique : Les variables d'environnement peuvent se retrouver par inadvertance dans les journaux, l'historique du pipeline CI/CD ou l'historique du shell, entraînant une exposition accidentelle.
Couches d'Image : Si vous utilisez
ENVdans un Dockerfile avec un secret, ce secret est intégré dans une couche d'image et y reste, même si vous essayez de leunsetdans une couche ultérieure. Cela rend le secret récupérable à partir de l'image elle-même.Partage Accidentel : Les fichiers
.envoudocker-compose.ymlcontenant des secrets sont souvent commités dans des systèmes de contrôle de version ou partagés de manière inappropriée, entraînant une exposition généralisée.
Avertissement : Traiter les informations sensibles comme des variables d'environnement ordinaires est une erreur de sécurité courante. Supposez toujours que les variables d'environnement sont publiquement visibles sur l'hôte et dans le conteneur.
Gérer les Secrets de Manière Sécurisée dans Docker
Pour remédier aux lacunes de sécurité des variables d'environnement pour les données sensibles, Docker fournit des capacités dédiées de gestion des secrets, principalement via Docker Secrets (pour Docker Swarm) et des outils externes comme Docker Compose avec la fonctionnalité secrets (qui peut utiliser les secrets Docker Swarm ou simplement monter des fichiers).
Docker Secrets (Mode Docker Swarm)
Docker Secrets est une fonctionnalité intégrée au mode Docker Swarm qui offre un moyen sécurisé de transmettre et de stocker des données sensibles pour les services. Les secrets sont :
- Chiffrés au repos dans les journaux Raft du gestionnaire Swarm.
- Transmis de manière sécurisée aux tâches de service autorisées.
- Montés en tant que fichiers en mémoire dans le système de fichiers du conteneur, généralement à
/run/secrets/<nom_du_secret>, plutôt que d'être exposés comme variables d'environnement. - Accessibles uniquement par les services auxquels l'accès a été explicitement accordé.
Comment Utiliser Docker Secrets (Mode Swarm)
Initialiser Swarm (si ce n'est pas déjà fait) :
docker swarm initCréer un Secret : Les secrets sont créés à partir d'un fichier ou de l'entrée standard.
echo "my_secure_db_password" | docker secret create db_password_secret - echo "SG.your_api_key_here" | docker secret create sendgrid_api_key -Déployer un Service avec le Secret : Les services référencent les secrets par leur nom. Docker monte le secret dans le conteneur.
docker service create --name my-webapp \ --secret db_password_secret \ --secret sendgrid_api_key \ my_app_image:latestAccéder aux Secrets dans le Conteneur : Les applications lisent le secret à partir du chemin de fichier monté.
# Dans votre code d'application Python (ou similaire pour d'autres langages) with open('/run/secrets/db_password_secret', 'r') as f: db_password = f.read().strip() with open('/run/secrets/sendgrid_api_key', 'r') as f: sendgrid_key = f.read().strip()
Docker Compose et Secrets (pour un seul hôte ou Swarm)
Docker Compose version 3.1+ a introduit une section secrets, qui vous permet de définir et de référencer des secrets dans votre docker-compose.yml. Lors de l'exécution en mode Swarm, Compose utilise les secrets natifs de Docker Swarm. Lors de l'exécution sur un seul hôte sans mode Swarm, Compose prend toujours en charge les secrets en montant des fichiers de l'hôte dans le conteneur de manière sécurisée, bien que sans le chiffrement au repos fourni par Swarm.
Utilisation de secrets dans docker-compose.yml
Définir les Secrets : Vous pouvez définir des secrets soit en référençant un fichier externe, soit en le rendant externe (secret Swarm pré-créé).
# docker-compose.yml version: '3.8' services: webapp: image: my_app_image:latest ports: - "80:8080" secrets: - db_password - sendgrid_api_key secrets: db_password: file: ./secrets/db_password.txt # Chemin vers un fichier sur l'hôte contenant le mot de passe sendgrid_api_key: external: true # Fait référence à un secret Docker Swarm préexistant nommé 'sendgrid_api_key'Créer des Fichiers de Secret Locaux (si
fileest utilisé) :mkdir secrets echo "my_local_db_password" > ./secrets/db_password.txtDéployer avec Compose :
docker compose up -ddéploiera vos services, rendant les secrets disponibles à/run/secrets/<nom_du_secret>à l'intérieur des conteneurs.# À l'intérieur du conteneur, le contenu de ./secrets/db_password.txt sera à : # /run/secrets/db_password
Choisir le Bon Outil : Configuration vs Secrets
La décision d'utiliser une variable d'environnement pour la configuration ou une solution de gestion des secrets dédiée se résume à une question principale :
Les données sont-elles sensibles ?
- Si Oui (données sensibles) : Utilisez Docker Secrets (avec Swarm) ou un système de gestion des secrets similaire (par exemple, Kubernetes Secrets, HashiCorp Vault). Pour les configurations Compose sur un seul hôte, utilisez la section
secretspour monter les fichiers de manière sécurisée. - Si Non (configuration non sensible) : Utilisez les variables d'environnement (via
ENVdans le Dockerfile, le drapeau-e,env_file, ouenvironmentdans Compose).
| Fonctionnalité | Variables d'Environnement (pour la config) | Docker Secrets (pour les données sensibles) |
|---|---|---|
| Objectif | Configuration d'application non sensible | Données sensibles telles que mots de passe et clés API |
| Visibilité | Visible via docker inspect et souvent l'inspection des processus |
Monté en tant que fichiers ; non affiché comme valeurs d'environnement normales |
| Sécurité | Pas approprié pour les données sensibles | Gestion plus robuste ; les secrets Swarm sont chiffrés au repos, tandis que les secrets de fichiers Compose locaux dépendent de la protection du fichier hôte |
| Accès dans l'App | Lecture depuis os.environ ou similaire |
Lecture depuis le fichier /run/secrets/<nom_du_secret> |
| Géré par | Runtime Docker, Docker Compose | Docker Swarm, Docker Compose, ou un gestionnaire de secrets externe |
| Cas d'Utilisation | Numéros de port, indicateurs de débogage, URL non sensibles | Mots de passe de base de données, jetons API, clés privées |
Meilleures Pratiques pour les Deux
Pour la Configuration (Variables d'Environnement) :
- Fournissez des valeurs par défaut raisonnables dans votre Dockerfile en utilisant
ENV. Cela rend vos images exécutables immédiatement et documente clairement les variables attendues. - Externalisez la configuration dans la mesure du possible. Utilisez des fichiers
.envavecdocker composeou des services de configuration externes pour les déploiements plus importants. - Documentez toutes les options de configuration et leurs valeurs attendues, peut-être dans un
README.mdou la documentation de l'application. - Évitez de coder en dur les valeurs qui pourraient changer entre les environnements (développement, préproduction, production).
Pour les Secrets (Docker Secrets et au-delà) :
- Ne commitez jamais les secrets (par exemple, les fichiers
.envcontenant des secrets,db_password.txt) dans les systèmes de contrôle de version comme Git. - Effectuez une rotation régulière des secrets. Cela minimise la fenêtre d'exposition si un secret est compromis.
- Appliquez le principe du moindre privilège. Donnez uniquement aux services l'accès aux secrets dont ils ont absolument besoin.
- Évitez de journaliser les valeurs des secrets. Assurez-vous que la journalisation de votre application et de votre infrastructure n'imprime pas le contenu des secrets.
- Pour les déploiements à grande échelle et de niveau entreprise, envisagez des solutions de gestion des secrets dédiées comme HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault, qui offrent des fonctionnalités plus avancées telles que l'audit, la génération dynamique de secrets et l'intégration avec la gestion des identités et des accès (IAM).
Une Règle Pratique pour Décider Quoi Mettre Où
Avant d'ajouter une valeur à environment, demandez-vous ce qui se passerait si un collègue la collait dans un ticket de support. Si la réponse est "rien de grave", c'est probablement de la configuration. Si la réponse est "nous devrions faire une rotation des identifiants", c'est un secret.
Bonnes variables d'environnement :
APP_ENV=production
LOG_LEVEL=info
PUBLIC_BASE_URL=https://example.com
FEATURE_SIGNUP_ENABLED=false
REDIS_HOST=redis
Mauvaises variables d'environnement :
DATABASE_PASSWORD=...
STRIPE_SECRET_KEY=...
JWT_SIGNING_KEY=...
AWS_SECRET_ACCESS_KEY=...
PRIVATE_SSH_KEY=...
Il existe des zones grises. Un nom d'hôte de base de données est généralement une configuration. Une URL de base de données complète qui inclut le nom d'utilisateur et le mot de passe est un secret. Une clé d'analyse publique peut être sûre pour une application de navigateur, tandis qu'un jeton API privé pour le même fournisseur ne l'est pas. En cas de doute, traitez la valeur comme sensible jusqu'à preuve du contraire.
Les Fichiers .env de Compose Sont Faciles à Mal Comprendre
Docker Compose utilise .env de deux manières différentes que les gens confondent souvent.
Premièrement, Compose lit un fichier .env au niveau du projet pour la substitution de variables à l'intérieur de compose.yml :
services:
web:
image: "${APP_IMAGE}"
ports:
- "${HOST_PORT}:8080"
Deuxièmement, env_file transmet les variables dans le conteneur :
services:
web:
image: my-app
env_file:
- ./app.env
Ces fichiers peuvent se ressembler, mais ils servent des objectifs différents. Le premier aide Compose à rendre la configuration. Le second devient l'environnement d'exécution à l'intérieur du conteneur. Ne supposez pas qu'une valeur dans le fichier .env du projet apparaît automatiquement à l'intérieur du conteneur à moins que vous ne la transmettiez explicitement.
Pour le développement local, un fichier d'exemple vérifié est utile :
# .env.example
APP_ENV=development
LOG_LEVEL=debug
PUBLIC_BASE_URL=http://localhost:3000
Ensuite, gardez le vrai fichier .env hors de Git :
.env
*.env.local
secrets/
Le fichier d'exemple documente ce que l'application attend sans exposer les valeurs privées.
Lire les Secrets Basés sur des Fichiers dans une Application
De nombreuses applications s'attendent déjà à ce que les secrets soient dans des variables d'environnement. Passer aux secrets basés sur des fichiers est plus facile si vous prenez en charge les deux modèles pendant un certain temps.
Par exemple, un helper Node.js :
import fs from "node:fs";
function readSecret(name) {
const filePath = process.env[`${name}_FILE`];
if (filePath) {
return fs.readFileSync(filePath, "utf8").trim();
}
return process.env[name];
}
const databasePassword = readSecret("DATABASE_PASSWORD");
Ensuite, votre fichier Compose peut pointer vers un fichier de secret monté :
services:
web:
image: my-app
environment:
DATABASE_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Ce modèle fonctionne bien car l'application peut toujours fonctionner dans des environnements plus anciens pendant que vous déplacez la production vers des secrets montés en fichiers. De nombreuses images officielles prennent déjà en charge les variables se terminant par _FILE pour cette raison.
Ne Mettez Pas Non Plus les Secrets dans les Arguments de Construction
Les variables d'environnement ne sont pas le seul piège. Les arguments de construction peuvent également fuir si vous les utilisez pour récupérer des packages privés ou cloner des dépôts :
ARG NPM_TOKEN
RUN npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN
Même si le conteneur final n'affiche pas NPM_TOKEN, l'historique de construction et les couches intermédiaires peuvent encore exposer plus que ce que vous attendez. Avec BuildKit, utilisez des montages de secrets pour les secrets au moment de la construction :
# syntax=docker/dockerfile:1.7
FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN="$(cat /run/secrets/npm_token)" npm ci
Construisez-le comme ceci :
docker build \
--secret id=npm_token,src=.npm-token \
-t my-app .
Cela garde le jeton hors du Dockerfile et évite de l'intégrer dans une couche normale. Vous devez toujours protéger le fichier .npm-token local et le stockage de secrets CI.
Kubernetes, Gestionnaires de Secrets Cloud et Docker
Les secrets Docker sont utiles dans Swarm, et les secrets Compose sont utiles pour les configurations locales ou sur un seul hôte. Dans Kubernetes, vous utiliserez normalement les secrets Kubernetes, un opérateur de secrets externe ou une intégration de gestionnaire de secrets cloud. Sur AWS, les équipes utilisent souvent AWS Secrets Manager ou Systems Manager Parameter Store. Sur Azure, Azure Key Vault est courant. Sur Google Cloud, Secret Manager remplit le même rôle.
Le principe est le même sur toutes les plateformes :
- Stockez les valeurs sensibles dans un système conçu pour les secrets.
- Accordez à l'identité d'exécution l'accès uniquement aux secrets dont elle a besoin.
- Montez ou injectez les secrets au moment de l'exécution.
- Effectuez une rotation des secrets sans reconstruire l'image.
- Gardez les secrets hors du contrôle de source, des couches d'image, des journaux et des tableaux de bord.
Les secrets Kubernetes sont encodés par défaut, pas automatiquement chiffrés dans toutes les configurations de cluster. De nombreux clusters gérés prennent en charge le chiffrement au repos, mais vérifiez les paramètres réels du cluster au lieu de supposer. Pour les identifiants à haut risque, utilisez un gestionnaire de secrets cloud ou un outil dédié avec des journaux d'audit et une prise en charge de la rotation.
La Rotation Fait Partie de la Conception
Une stratégie de secret qui ne peut pas tourner est incomplète. Posez ces questions avant la production :
- Pouvons-nous changer le mot de passe de la base de données sans reconstruire l'image ?
- Deux identifiants valides peuvent-ils se chevaucher lors d'un déploiement ?
- L'application relit-elle les secrets, ou a-t-elle besoin d'un redémarrage ?
- Où les anciens identifiants sont-ils journalisés, mis en cache ou stockés ?
- Qui est averti lorsqu'un secret change ?
Pour les bases de données, la rotation implique souvent la création d'un deuxième identifiant, le déploiement de l'application avec le nouvel identifiant, la vérification du trafic, puis la révocation de l'ancien. Pour les clés API, cela dépend du fournisseur. Certains services autorisent plusieurs clés actives ; d'autres imposent un basculement. Concevez votre processus de déploiement en fonction de la dépendance la moins flexible.
Nettoyer une Exposition Accidentelle
Si un secret a déjà été commité dans Git ou intégré dans une image, supprimer la ligne ne suffit pas. Traitez-le comme exposé.
La réponse habituelle est :
- Révoquez ou faites une rotation de l'identifiant.
- Supprimez-le du code ou de l'image actuelle.
- Vérifiez les journaux CI, les registres d'images, les trackers de problèmes et les messages de chat pour les copies.
- Réécrivez l'historique Git uniquement si votre organisation est prête à gérer la coordination ; la rotation est toujours nécessaire.
- Ajoutez une analyse ou des vérifications de pré-commit pour réduire les erreurs répétées.
Les outils peuvent aider, mais ils ne remplacent pas les habitudes. Gardez les fichiers de secrets clairement nommés, ignorez-les dans Git et évitez d'imprimer les objets de configuration en bloc au démarrage.
Le Modèle de Travail
Utilisez les variables d'environnement pour les valeurs qui décrivent comment l'application doit s'exécuter dans cet environnement : ports, niveaux de journalisation, indicateurs de fonctionnalités, noms d'hôte de service et URL non sensibles. Utilisez les secrets pour les valeurs qui prouvent l'identité ou accordent l'accès : mots de passe, jetons, clés de signature, clés privées et identifiants de fournisseur.
L'image Docker propre est la même dans tous les environnements. Le développement, la préproduction et la production modifient le comportement au moment de l'exécution. La configuration peut voyager sous forme de variables d'environnement. Les secrets doivent provenir d'un coffre-fort de secrets ou d'un fichier de secret monté avec un accès limité. Cette séparation maintient les déploiements flexibles sans transformer chaque inspection de conteneur, ligne de journal ou couche d'image en une fuite d'identifiants.