Maîtriser les variables d'environnement dans Docker : Configuration vs Secrets
Docker a révolutionné la manière dont les applications sont construites, déployées et exécutées, offrant un environnement cohérent à travers les différentes étapes de développement. Un aspect fondamental de la gestion des applications conteneurisées est leur configuration, et les variables d'environnement en sont le mécanisme principal. Cependant, toutes les données ne sont pas égales ; certaines configurations sont inoffensives, tandis que d'autres informations, comme les clés d'API ou les informations d'identification de base de données, sont très sensibles. Confondre ces deux catégories peut entraîner des vulnérabilités de sécurité importantes.
Cet article explore la distinction cruciale entre l'utilisation des variables d'environnement pour la configuration générale et les méthodes appropriées et sécurisées pour gérer les données sensibles, communément appelées « secrets ». Nous examinerons les différentes manières de transmettre la configuration à vos conteneurs Docker, mettrons en évidence les risques inhérents à traiter les secrets comme des variables d'environnement ordinaires, et présenterons les solutions dédiées de Docker pour la gestion sécurisée des secrets. À la fin, vous aurez une compréhension claire de quand et comment utiliser chaque approche, garantissant que vos applications sont à la fois flexibles et sécurisées.
Comprendre les variables d'environnement pour la configuration
Les variables d'environnement sont une méthode simple et largement adoptée pour transmettre la configuration d'exécution aux applications, y compris celles s'exécutant 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 tels que les numéros de port de l'application, les indicateurs de débogage ou les URL de services tiers.
Méthodes pour transmettre les variables de configuration
Docker fournit 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. Ceci convient aux variables qui sont peu susceptibles de changer ou qui fournissent des valeurs par défaut sensées 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 écrasées lors 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 transmettre directement des variables d'environnement. Ceci est courant pour les tests ponctuels 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 \n -e APP_PORT=80 \n -e DEBUG_MODE=true \n 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, gardant 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 gérer des données sensibles (secrets) telles que les mots de passe de base de données, les clés d'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-elles 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.```bash
Exemple d'exposition d'un secret (NE PAS FAIRE CECI 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_PASSWORD
``` -
Espionnage de processus : À l'intérieur du conteneur, d'autres processus ou utilisateurs (s'il existe plusieurs utilisateurs) pourraient être en mesure de lire les variables d'environnement, surtout si l'application s'exécute en tant que root ou dispose de privilèges élevés.
-
Journalisation et historique : Les variables d'environnement peuvent se retrouver accidentellement dans les journaux, l'historique des pipelines 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 le désactiver (unset) dans 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 intégré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 normales est une erreur de sécurité courante. Supposez toujours que les variables d'environnement sont visibles publiquement sur l'hôte et à l'intérieur du conteneur.
Gestion sécurisée des secrets 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 exploiter les secrets Docker Swarm ou simplement monter des fichiers).
Docker Secrets (Mode Swarm Docker)
Docker Secrets est une fonctionnalité intégrée au mode Docker Swarm qui fournit 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 comme des fichiers en mémoire dans le système de fichiers du conteneur, généralement à l'emplacement
/run/secrets/<nom_du_secret>, au lieu d'être exposés comme des variables d'environnement. - Accessibles uniquement par les services auxquels un accès explicite a été accordé.
Comment utiliser Docker Secrets (Mode Swarm)
-
Initialiser Swarm (si ce n'est pas déjà fait) :
bash docker swarm init -
Créer un secret : Les secrets sont créés à partir d'un fichier ou de l'entrée standard.
bash 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.
bash docker service create --name my-webapp \n --secret db_password_secret \n --secret sendgrid_api_key \n my_app_image:latest -
Accéder aux secrets dans le conteneur : Les applications lisent le secret à partir du chemin du fichier monté.
```python
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 hôte unique 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. Lorsqu'il est exécuté en mode Swarm, Compose utilise les secrets natifs de Docker Swarm. Lorsqu'il est exécuté sur un seul hôte sans le mode Swarm, Compose prend toujours en charge les secrets en montant des fichiers de l'hôte vers 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 les secrets soit en référençant un fichier externe, soit en en faisant un secret externe (secret Swarm préexistant).
```yaml
docker-compose.yml
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
secrets:
- db_password
- sendgrid_api_keysecrets:
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 secrets locaux (si
fileest utilisé) :
bash mkdir secrets echo "my_local_db_password" > ./secrets/db_password.txt -
Déployer avec Compose :
docker compose up -ddéploiera vos services, rendant les secrets disponibles à l'emplacement/run/secrets/<nom_du_secret>à l'intérieur des conteneurs.```bash
À 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 dédiée de gestion des secrets 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 sur un seul hôte avec Compose, utilisez la section
secretspour monter les fichiers en toute sécurité. - Si non (configuration non sensible) : Utilisez les variables d'environnement (via
ENVdans Dockerfile, drapeau-e,env_fileouenvironmentdans Compose).
| Caractéristique | Variables d'environnement (pour la config) | Docker Secrets (pour les données sensibles) |
| :-------------------------- | :----------------------------------------- | :---------------------------------------------- |\n| Objectif | Configuration d'application non sensible | Données sensibles (mots de passe, clés d'API) |\n| Visibilité | Visible via docker inspect, ps -e | Monté comme des fichiers ; pas dans docker inspect |\n| Sécurité | Non sécurisé pour les données sensibles | Transmission et stockage chiffrés et sécurisés |\n| Accès dans l'application| Lecture depuis os.environ (ou similaire) | Lecture depuis le fichier /run/secrets/<secret_name> |\n| Géré par | Exécution Docker, Docker Compose | Docker Swarm, Docker Compose |\n| Cas d'utilisation | Numéros de port, indicateurs de débogage, URL non sensibles | Mots de passe de base de données, jetons d'API, clés privées |\n
Bonnes pratiques pour les deux
Pour la configuration (variables d'environnement) :
- Fournissez des valeurs par défaut sensées dans votre Dockerfile en utilisant
ENV. Cela rend vos images exécutables immédiatement et documente clairement les variables attendues. - Externalisez la configuration lorsque cela est 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 d'intégrer en dur des valeurs susceptibles de changer entre les environnements (développement, staging, production).
Pour les secrets (Docker Secrets et au-delà) :
- N'intégrez jamais de secrets (par exemple, des fichiers
.envcontenant des secrets,db_password.txt) dans des systèmes de contrôle de version comme Git. - Faites pivoter les secrets régulièrement. Cela minimise la fenêtre d'exposition si un secret est compromis.
- Accordez le moindre privilège. Ne donnez aux services que l'accès aux secrets dont ils ont absolument besoin.
- Évitez d'enregistrer les valeurs des secrets. Assurez-vous que la journalisation de votre application et de votre infrastructure n'affiche pas le contenu des secrets.
- Pour les déploiements à grande échelle de niveau entreprise, envisagez des solutions dédiées de gestion des secrets telles que HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault, qui offrent des fonctionnalités plus avancées comme l'audit, la génération de secrets dynamiques et l'intégration avec la gestion des identités et des accès (IAM).
Conclusion
Maîtriser les variables d'environnement dans Docker signifie plus que simplement savoir comment les transmettre ; cela signifie comprendre la différence fondamentale entre la configuration générique et les secrets sensibles. Bien que les variables d'environnement offrent une flexibilité inégalée pour la configuration des applications, elles sont intrinsèquement non sécurisées pour les données sensibles.
En tirant parti de Docker Secrets pour les informations sensibles au sein d'un environnement Swarm, ou en utilisant judicieusement la fonctionnalité secrets de Docker Compose pour les déploiements sur un seul hôte, vous pouvez améliorer considérablement la posture de sécurité de vos applications conteneurisées. Priorisez toujours la sécurité en utilisant le bon outil pour le travail, en adhérant aux meilleures pratiques et en vous assurant que vos données sensibles restent protégées contre toute exposition accidentelle. Cette approche disciplinée mènera à des déploiements Docker plus robustes, maintenables et sécurisés.