14 - Variables d'environnement et secrets
Ce que tu vas apprendre
- La différence entre ENV et ARG dans un Dockerfile
- Les différentes facons de passer des variables dans Compose
- Le fichier .env et son chargement automatique
- Docker secrets pour les donnees sensibles
- Comment vérifier qu'un secret n'a pas fuite dans une image
Prerequisites
- Avoir suivi l'article sur les volumes
- Comprendre la construction d'images Docker
J'ai vu un mot de passe de base de donnees en production dans un docker inspect une fois. Le dev avait mis le mot de passe directement dans le Dockerfile avec ENV. L'image etait poussee sur un registry prive, accessible a toute l'équipe. Tout le monde pouvait voir le mot de passe. Ce genre d'erreur, ca ne devrait jamais arriver.
ENV dans le Dockerfile
ENV definit une variable d'environnement qui persiste dans l'image et dans les conteneurs qui en derivent :
dockerfileFROM node:20-alpine
ENV NODE_ENV=production
ENV PORT=3000
Ces variables sont visibles dans l'image finale. N'importe qui avec acces a l'image peut les voir avec docker inspect ou docker history. Ne mets jamais de secret dans un ENV.
ENV est fait pour la configuration non sensible : NODE_ENV, PORT, TZ, ce genre de choses.
ARG : le build-time only
ARG est différent. Il n'existe que pendant le build :
dockerfileFROM node:20-alpine
ARG APP_VERSION=1.0.0
RUN echo "Building version $APP_VERSION"
# APP_VERSION n'existe plus dans le conteneur final
Pour passer un ARG au build :
bashdocker build --build-arg APP_VERSION=2.3.1 .
Ou dans Compose :
yamlservices:
app:
build:
context: .
args:
APP_VERSION: 2.3.1
Attention : meme si ARG n'existe pas dans le conteneur final, il est visible dans l'historique de l'image (docker history). Ne mets pas de secrets dans ARG non plus. C'est pour des valeurs comme le numero de version ou le choix d'un registry npm.
Variables d'environnement dans Compose
Directement dans le fichier
yamlservices:
app:
environment:
NODE_ENV: production
DATABASE_URL: postgres://postgres:secret@db:5432/myapp
Simple et lisible, mais les secrets sont en clair dans le fichier. Acceptable en dev local, jamais en production.
Avec env_file
yamlservices:
app:
env_file:
- .env.app
Le fichier .env.app :
NODE_ENV=production
DATABASE_URL=postgres://postgres:secret@db:5432/myapp
Ca évité de polluer le compose.yaml avec 30 variables. Tu peux spécifier plusieurs fichiers, ils sont charges dans l'ordre.
Le fichier .env magique
Compose charge automatiquement le fichier .env a la racine du projet. Mais attention : il ne le passe pas aux conteneurs. Il sert a interpoler les variables dans le fichier compose.yaml :
# .env
POSTGRES_VERSION=16
APP_PORT=3000
yaml# compose.yaml
services:
db:
image: postgres:${POSTGRES_VERSION}-alpine
app:
ports:
- "${APP_PORT}:3000"
C'est utile pour paramétrer le fichier Compose sans le modifier. Sur paltemps.fr, j'utilise le .env pour le numero de version des images et les ports, et un env_file pour les variables passees aux conteneurs.
Pour passer les variables du .env aux conteneurs, il faut les déclarer explicitement :
yamlservices:
app:
environment:
- APP_PORT # la valeur vient du shell ou du .env
Docker secrets
Pour les vrais secrets en production, Docker propose un mecanisme dédié. Les secrets sont stockes sous forme de fichiers dans /run/secrets/ a l'intérieur du conteneur :
yamlservices:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
app:
build: .
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
environment: "API_KEY"
Le contenu du fichier ./secrets/db_password.txt est monte en lecture seule dans /run/secrets/db_password. L'image officielle PostgreSQL supporte nativement le suffixe _FILE sur ses variables d'environnement.
Pour les applications custom, tu dois lire le fichier toi-meme :
typescriptimport { readFileSync } from "fs";
function getSecret(name: string): string {
const secretPath = `/run/secrets/${name}`;
try {
return readFileSync(secretPath, "utf-8").trim();
} catch {
// Fallback sur la variable d'environnement en dev
const envValue = process.env[name.toUpperCase()];
if (!envValue) throw new Error(`Secret ${name} not found`);
return envValue;
}
}
const dbPassword = getSecret("db_password");
Ne jamais cuire les secrets dans l'image
Meme si tu supprimes un fichier de secret dans une couche ulterieure du Dockerfile, il reste accessible dans les couches précédentes :
dockerfile# MAUVAIS : le secret est dans l'historique de l'image
COPY .env /app/.env
RUN source /app/.env && npm run build
RUN rm /app/.env # trop tard, il est dans la couche precedente
Avec un build multi-stage, tu peux limiter les degats :
dockerfileFROM node:20-alpine AS builder
COPY .env /app/.env
RUN source /app/.env && npm run build
FROM node:20-alpine
COPY --from=builder /app/dist /app/dist
# le .env n'est pas dans l'image finale
Mais la bonne pratique, c'est de passer les secrets au build via --mount=type=secret :
dockerfileRUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
bashdocker build --secret id=npmrc,src=.npmrc .
Le secret est accessible pendant le build mais n'apparaît dans aucune couche de l'image.
Verifier les fuites de secrets
Quelques commandes pour vérifier qu'un secret n'a pas fuite :
bash# Voir toutes les variables d'environnement de l'image
docker inspect <image> --format '{{json .Config.Env}}'
# Voir l'historique des couches
docker history <image> --no-trunc
# Chercher un pattern dans l'image
docker run --rm <image> env | grep -i password
Des outils comme trivy ou dockle scannent automatiquement les images a la recherche de secrets exposes.
Résumé
- ENV : configuration non sensible, visible dans l'image
- ARG : valeurs de build uniquement, visibles dans l'historique
- env_file : fichier de variables charge par Compose
- .env : interpole les variables dans compose.yaml, pas passe aux conteneurs par défaut
- Docker secrets : fichiers montes en lecture seule dans /run/secrets/
--mount=type=secretprotégé les secrets pendant le build
Navigation : Precedent : 13 - Volumes | Suivant : 15 - Permissions
Sources
- Docker Compose environment variables par Docker
- Docker secrets in Compose par Docker
- Build secrets par Docker