Aller au contenu principal

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

AspectSecretsConfigs
ChiffrementOui (au repos et en transit)Non
UsageDonnées sensiblesFichiers de configuration
Emplacement/run/secrets/Personnalisable
PermissionsRestrictivesPersonnalisables

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é

CommandeDescription
docker secret createCréer un secret
docker secret lsLister les secrets
docker secret inspectInspecter un secret
docker secret rmSupprimer un secret
docker config createCréer une config
docker config lsLister les configs
docker config inspectInspecter une config
docker config rmSupprimer 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: true pour les secrets créés manuellement
  • Planifiez la rotation des secrets

Exercices pratiques

  1. Créez un secret pour un mot de passe de base de données
  2. Déployez PostgreSQL en utilisant le secret
  3. Créez une config nginx et déployez un service web
  4. Pratiquez la rotation d'un secret

← Stacks et déploiement | Scaling et Load Balancing →