Secrets et Configs
Objectifs du chapitre
- Comprendre les secrets Docker
- Gérer les configurations sensibles
- Utiliser les configs pour les fichiers
- Appliquer les bonnes pratiques de sécurité
1 - Introduction aux Secrets
Le problème
Comment gérer les données sensibles sans les exposer ?
# ❌ NE JAMAIS FAIRE
services:
db:
environment:
- POSTGRES_PASSWORD=supersecret123
Problèmes :
- Visible dans
docker inspect - Dans l'historique Git
- Dans les logs
- Accessible à tous les utilisateurs
La solution : Docker Secrets
Les secrets Docker sont :
- Chiffrés au repos et en transit
- Accessibles uniquement aux services autorisés
- Montés en mémoire (tmpfs), pas sur disque
- Gérés par le cluster Swarm
┌─────────────────────────────────────────────────────────────┐
│ Manager Node (Leader) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Raft Store (chiffré) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ secret1 │ │ secret2 │ │ secret3 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────┬── ─────────────────────────────┘
│ TLS
▼
┌─────────────────────────────────────────────────────────────┐
│ Worker Node │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Container │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ /run/secrets/db_password (tmpfs, 0444) │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └───────────── ──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2 - Créer et gérer des secrets
Créer un secret
# Depuis une chaîne
echo "mysuperpassword" | docker secret create db_password -
# Depuis un fichier
docker secret create ssl_cert ./certificate.pem
docker secret create ssl_key ./private.key
# Avec un label
docker secret create --label env=production db_password ./password.txt
Lister les secrets
# Liste simple
docker secret ls
# Avec filtres
docker secret ls --filter label=env=production
# Résultat
ID NAME CREATED UPDATED
abc123def456... db_password 2 hours ago 2 hours ago
ghi789jkl012... ssl_cert 1 hour ago 1 hour ago
Inspecter un secret
# Métadonnées seulement (pas le contenu !)
docker secret inspect db_password
# Format spécifique
docker secret inspect --format '{{.Spec.Name}}' db_password
Important
Il est impossible de voir le contenu d'un secret une fois créé. C'est une mesure de sécurité.
Supprimer un secret
# Supprimer (le secret ne doit pas être utilisé)
docker secret rm db_password
# Forcer n'est pas possible si utilisé par un service
3 - Utiliser les secrets dans les services
En ligne de commande
# Créer un service avec un secret
docker service create --name api \
--secret db_password \
myapi:latest
# Le secret est accessible dans /run/secrets/db_password
# Avec un nom personnalisé
docker service create --name api \
--secret source=db_password,target=database_pwd,mode=0400 \
myapi:latest
# Multiple secrets
docker service create --name api \
--secret db_password \
--secret jwt_secret \
--secret ssl_cert \
myapi:latest
Dans une stack
version: "3.9"
services:
db:
image: postgres:15
secrets:
- db_password
environment:
# PostgreSQL supporte les fichiers de secret
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
api:
image: myapi:latest
secrets:
- source: db_password
target: database_password
uid: '1000'
gid: '1000'
mode: 0400
- jwt_secret
secrets:
db_password:
external: true # Secret créé manuellement
jwt_secret:
file: ./secrets/jwt.txt # Secret créé depuis un fichier
Lire un secret dans l'application
# Python
def get_secret(name):
try:
with open(f'/run/secrets/{name}', 'r') as f:
return f.read().strip()
except FileNotFoundError:
return os.environ.get(name.upper())
db_password = get_secret('db_password')
// Node.js
const fs = require('fs');
const path = require('path');
function getSecret(name) {
try {
return fs.readFileSync(
path.join('/run/secrets', name),
'utf8'
).trim();
} catch (e) {
return process.env[name.toUpperCase()];
}
}
const dbPassword = getSecret('db_password');
4 - Rotation des secrets
Stratégie de rotation
# 1. Créer le nouveau secret
echo "newpassword" | docker secret create db_password_v2 -
# 2. Mettre à jour le service
docker service update \
--secret-rm db_password \
--secret-add source=db_password_v2,target=db_password \
api
# 3. Supprimer l'ancien secret (une fois tous les services migrés)
docker secret rm db_password
Rotation dans une stack
# Version 1
secrets:
db_password:
file: ./secrets/password_v1.txt
# Version 2 - Ajouter un nouveau secret
secrets:
db_password:
file: ./secrets/password_v2.txt
# Re-déployer
docker stack deploy -c stack.yml myapp
5 - Configs Docker
Différence Secrets vs Configs
| Aspect | Secrets | Configs |
|---|---|---|
| Chiffrement | Oui (au repos et en transit) | Non |
| Usage | Données sensibles | Fichiers de configuration |
| Emplacement | /run/secrets/ | Personnalisable |
| Permissions | Restrictives | Personnalisables |
Créer une config
# Depuis un fichier
docker config create nginx_config ./nginx.conf
# Depuis stdin
cat nginx.conf | docker config create nginx_config -
# Avec label
docker config create --label app=web nginx_config ./nginx.conf
Utiliser une config
# En ligne de commande
docker service create --name web \
--config source=nginx_config,target=/etc/nginx/nginx.conf \
nginx:alpine
# Dans une stack
version: "3.9"
services:
web:
image: nginx:alpine
configs:
- source: nginx_config
target: /etc/nginx/nginx.conf
uid: '0'
gid: '0'
mode: 0644
configs:
nginx_config:
file: ./nginx.conf
# Config externe (créée manuellement)
another_config:
external: true
Gestion des configs
# Lister
docker config ls
# Inspecter (contenu visible !)
docker config inspect nginx_config
# Voir le contenu
docker config inspect --format '{{json .Spec.Data}}' nginx_config | base64 -d
# Supprimer
docker config rm nginx_config
6 - Mise à jour des configs
Rotation de config
# 1. Créer la nouvelle config
docker config create nginx_config_v2 ./nginx_new.conf
# 2. Mettre à jour le service
docker service update \
--config-rm nginx_config \
--config-add source=nginx_config_v2,target=/etc/nginx/nginx.conf \
web
# 3. Supprimer l'ancienne config
docker config rm nginx_config
Template de config
# La config peut être un template
configs:
app_config:
template_driver: golang
file: ./config.tmpl
# config.tmpl
server_name: {{ env "SERVER_NAME" }}
port: {{ env "PORT" }}
7 - Exemple complet
Application sécurisée
version: "3.9"
services:
nginx:
image: nginx:alpine
ports:
- "443:443"
configs:
- source: nginx_config
target: /etc/nginx/nginx.conf
secrets:
- source: ssl_cert
target: /etc/nginx/ssl/cert.pem
- source: ssl_key
target: /etc/nginx/ssl/key.pem
mode: 0400
networks:
- frontend
deploy:
replicas: 2
api:
image: myapi:latest
secrets:
- db_password
- jwt_secret
- api_key
configs:
- source: api_config
target: /app/config.json
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_USER=api
- DB_PASSWORD_FILE=/run/secrets/db_password
networks:
- frontend
- backend
deploy:
replicas: 3
db:
image: postgres:15
secrets:
- db_password
- db_root_password
environment:
- POSTGRES_USER=api
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
- POSTGRES_DB=myapp
volumes:
- db-data:/var/lib/postgresql/data
configs:
- source: pg_config
target: /etc/postgresql/postgresql.conf
networks:
- backend
deploy:
replicas: 1
placement:
constraints:
- node.labels.db == true
networks:
frontend:
driver: overlay
backend:
driver: overlay
internal: true
volumes:
db-data:
configs:
nginx_config:
file: ./configs/nginx.conf
api_config:
file: ./configs/api.json
pg_config:
file: ./configs/postgresql.conf
secrets:
ssl_cert:
file: ./secrets/cert.pem
ssl_key:
file: ./secrets/key.pem
db_password:
external: true
db_root_password:
external: true
jwt_secret:
external: true
api_key:
external: true
Scripts de déploiement
#!/bin/bash
# deploy.sh
# Créer les secrets externes
echo "Creating secrets..."
echo "$DB_PASSWORD" | docker secret create db_password - 2>/dev/null || true
echo "$DB_ROOT_PASSWORD" | docker secret create db_root_password - 2>/dev/null || true
echo "$JWT_SECRET" | docker secret create jwt_secret - 2>/dev/null || true
echo "$API_KEY" | docker secret create api_key - 2>/dev/null || true
# Déployer la stack
echo "Deploying stack..."
docker stack deploy -c docker-compose.yml myapp
echo "Done!"
8 - Bonnes pratiques
Secrets
# ✅ Utiliser des secrets pour
- Mots de passe de base de données
- Clés API
- Certificats SSL/TLS
- Tokens d'authentification
- Clés de chiffrement
# ❌ Ne pas utiliser pour
- Configuration non sensible
- Gros fichiers (> quelques Ko)
- Données fréquemment modifiées
Configs
# ✅ Utiliser des configs pour
- Fichiers de configuration (nginx.conf, etc.)
- Templates
- Fichiers statiques
- Scripts d'initialisation
# ❌ Ne pas utiliser pour
- Données sensibles (utiliser secrets)
- Fichiers volumineux (utiliser volumes)
Sécurité
# ✅ Permissions restrictives pour les secrets
secrets:
- source: db_password
mode: 0400 # Lecture seule par le propriétaire
# ✅ Ne pas logger les secrets
# Dans votre code, ne jamais faire :
# console.log(secret) ou print(secret)
# ✅ Rotation régulière
# Changez les secrets périodiquement
Résumé
| Commande | Description |
|---|---|
docker secret create | Créer un secret |
docker secret ls | Lister les secrets |
docker secret inspect | Inspecter un secret |
docker secret rm | Supprimer un secret |
docker config create | Créer une config |
docker config ls | Lister les configs |
docker config inspect | Inspecter une config |
docker config rm | Supprimer une config |
Points clés
- Les secrets sont chiffrés et accessibles via
/run/secrets/ - Les configs sont pour les fichiers non sensibles
- Utilisez
external: truepour les secrets créés manuellement - Planifiez la rotation des secrets
Exercices pratiques
- Créez un secret pour un mot de passe de base de données
- Déployez PostgreSQL en utilisant le secret
- Créez une config nginx et déployez un service web
- Pratiquez la rotation d'un secret