Skip to main content

Secrets and Configs


Chapter objectives

  • Understand Docker secrets
  • Manage sensitive configurations
  • Use configs for files
  • Apply security best practices

1 - Introduction to Secrets

The problem

How do you manage sensitive data without exposing it?

# ❌ NE JAMAIS FAIRE
services:
db:
environment:
- POSTGRES_PASSWORD=supersecret123

Problems:

  • Visible in docker inspect
  • In the Git history
  • In the logs
  • Accessible to all users

The solution: Docker Secrets

Docker secrets are:

  • Encrypted at rest and in transit
  • Accessible only to authorized services
  • Mounted in memory (tmpfs), not on disk
  • Managed by the Swarm cluster
┌─────────────────────────────────────────────────────────────┐
│ Manager Node (Leader) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Raft Store (chiffré) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ secret1 │ │ secret2 │ │ secret3 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────┘
│ TLS

┌─────────────────────────────────────────────────────────────┐
│ Worker Node │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Container │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ /run/secrets/db_password (tmpfs, 0444) │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

2 - Create and manage secrets

Create a 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

List 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

Inspect a 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

It is impossible to view the content of a secret once created. This is a security measure.

Remove a 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 - Use secrets in services

On the command line

# 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

In a 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

Read a secret in the 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 - Secret rotation

Rotation strategy

# 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 in a 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 - Docker Configs

Difference between Secrets and Configs

AspectSecretsConfigs
EncryptionYes (at rest and in transit)No
UseSensitive dataConfiguration files
Location/run/secrets/Customizable
PermissionsRestrictiveCustomizable

Create a 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

Use a 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

Config management

# 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 - Updating configs

Config rotation

# 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

Config template

# 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 - Complete example

Secure application

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

Deployment scripts

#!/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 - Best practices

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)

Security

# ✅ 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

Summary

CommandDescription
docker secret createCreate a secret
docker secret lsList secrets
docker secret inspectInspect a secret
docker secret rmRemove a secret
docker config createCreate a config
docker config lsList configs
docker config inspectInspect a config
docker config rmRemove a config
Key points
  • Secrets are encrypted and accessible via /run/secrets/
  • Configs are for non-sensitive files
  • Use external: true for manually created secrets
  • Plan for secret rotation

Practical exercises

  1. Create a secret for a database password
  2. Deploy PostgreSQL using the secret
  3. Create an nginx config and deploy a web service
  4. Practice rotating a secret

← Stacks and deployment | Scaling and Load Balancing →