Aller au contenu principal

Gestion des Secrets

Guide pour la gestion des secrets et de la configuration sensible de MyTelevision API.

Vue d'ensemble

La gestion correcte des secrets est critique pour la securite. Ce guide couvre les bonnes pratiques pour le traitement des secrets dans tous les environnements.

Categories de secrets

CategorieExemplesRotation
AuthenticationJWT secrets, API keysTrimestrielle
DatabaseConnection strings, passwordsTrimestrielle
Services externesTMDb, Firebase, StripeAnnuelle
EncryptionDRM keys, storage keysAnnuelle
MonitoringAlert webhooks, tokensAnnuelle

Approches par environnement

Development

Utiliser des fichiers .env (gitignored) :

# .env (developpement local)
JWT_SECRET=dev-secret-min-32-characters-here
DATABASE_URL=postgresql://mytv:password@localhost:5432/mytv
REDIS_PASSWORD=redis-dev-password
attention

Ne jamais commiter les fichiers .env dans le controle de version.

Staging

Utiliser Docker secrets ou des variables d'environnement :

# docker-compose.staging.yml
services:
api:
environment:
- JWT_SECRET=${JWT_SECRET}
- DATABASE_URL=${DATABASE_URL}

Les secrets sont charges depuis les variables d'environnement CI/CD.

Production

Utiliser Docker secrets montes en fichiers :

# docker-compose.production.yml
services:
api:
secrets:
- jwt_secret
- db_password
- redis_password
- firebase_private_key
environment:
- JWT_SECRET_FILE=/run/secrets/jwt_secret
- DB_PASSWORD_FILE=/run/secrets/db_password

secrets:
jwt_secret:
external: true
db_password:
external: true
redis_password:
external: true
firebase_private_key:
external: true

Docker Secrets

Creer des secrets

# Creer des secrets depuis des valeurs
echo "your-jwt-secret-min-32-chars" | docker secret create jwt_secret -
echo "your-database-password" | docker secret create db_password -

# Creer un secret depuis un fichier
docker secret create firebase_private_key ./firebase-key.json

# Lister les secrets
docker secret ls

Utilisation dans le code

import { readFileSync, existsSync } from 'fs';

function getSecret(name: string, envVar: string): string {
// Verifier d'abord le secret base fichier (production)
const secretPath = `/run/secrets/${name}`;
if (existsSync(secretPath)) {
return readFileSync(secretPath, 'utf8').trim();
}

// Fallback sur la variable d'environnement (developpement)
const envValue = process.env[envVar];
if (envValue) {
return envValue;
}

// Verifier le pattern suffixe _FILE
const fileEnvVar = `${envVar}_FILE`;
const filePath = process.env[fileEnvVar];
if (filePath && existsSync(filePath)) {
return readFileSync(filePath, 'utf8').trim();
}

throw new Error(`Secret ${name} not found`);
}

// Utilisation
const jwtSecret = getSecret('jwt_secret', 'JWT_SECRET');
const dbPassword = getSecret('db_password', 'DB_PASSWORD');

Kubernetes Secrets

Creer des secrets

# Depuis des valeurs litterales
kubectl create secret generic mytv-api-secrets \
--from-literal=JWT_SECRET='your-jwt-secret' \
--from-literal=DB_PASSWORD='your-db-password' \
-n mytv-production

# Depuis un fichier
kubectl create secret generic firebase-credentials \
--from-file=firebase-key.json=./firebase-service-account.json \
-n mytv-production

Utilisation dans un Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: mytv-api
spec:
template:
spec:
containers:
- name: api
envFrom:
- secretRef:
name: mytv-api-secrets
volumeMounts:
- name: firebase-creds
mountPath: /etc/secrets/firebase
readOnly: true
volumes:
- name: firebase-creds
secret:
secretName: firebase-credentials

Sealed Secrets (GitOps)

Pour stocker des secrets chiffres dans Git :

# Installer le CLI kubeseal
brew install kubeseal

# Creer un sealed secret
kubectl create secret generic mytv-secrets \
--from-literal=JWT_SECRET='secret' \
--dry-run=client -o yaml | \
kubeseal --format yaml > sealed-secrets.yaml

# Appliquer le sealed secret
kubectl apply -f sealed-secrets.yaml

Rotation des secrets

Rotation du JWT Secret

  1. Generer un nouveau secret
  2. Ajouter le nouveau secret a la configuration (garder l'ancien temporairement)
  3. Deployer avec les deux secrets (valider les deux pendant la transition)
  4. Apres la periode de grace, supprimer l'ancien secret
  5. Deployer avec uniquement le nouveau secret
// Support de plusieurs JWT secrets pendant la rotation
const jwtSecrets = [
process.env.JWT_SECRET,
process.env.JWT_SECRET_OLD, // Supprimer apres la rotation
].filter(Boolean);

function verifyToken(token: string): JwtPayload {
for (const secret of jwtSecrets) {
try {
return jwt.verify(token, secret) as JwtPayload;
} catch (e) {
continue;
}
}
throw new UnauthorizedException('Invalid token');
}

Rotation du mot de passe database

  1. Creer un nouvel utilisateur database avec un nouveau mot de passe
  2. Mettre a jour l'application pour utiliser les nouvelles credentials
  3. Deployer et verifier
  4. Supprimer l'ancien utilisateur database
-- PostgreSQL
CREATE USER mytv_new WITH PASSWORD 'new-password';
GRANT ALL PRIVILEGES ON DATABASE mytv TO mytv_new;

-- Apres deploiement
DROP USER mytv_old;

Inventaire des secrets

SecretStockageRotation
JWT_SECRETDocker/K8s secretTrimestrielle
JWT_REFRESH_SECRETDocker/K8s secretTrimestrielle
DATABASE_URLDocker/K8s secretTrimestrielle
REDIS_PASSWORDDocker/K8s secretTrimestrielle
FIREBASE_PRIVATE_KEYDocker/K8s secretAnnuelle
TMDB_API_KEYDocker/K8s secretAnnuelle
STRIPE_SECRET_KEYDocker/K8s secretAnnuelle
R2_SECRET_ACCESS_KEYDocker/K8s secretAnnuelle
SMTP_PASSWORDDocker/K8s secretAnnuelle

Generation de secrets

Generer des secrets forts

# Generer un secret aleatoire de 32 bytes
openssl rand -hex 32

# Generer un secret base64
openssl rand -base64 32

# Generer un UUID
uuidgen

Complexite des mots de passe

TypeExigences
JWT Secrets32+ caracteres aleatoires
Database16+ chars, majuscules/minuscules, chiffres, symboles
API KeysGeneres par le fournisseur
Redis16+ caracteres aleatoires

Monitoring et audit des secrets

Logs d'acces aux secrets

Surveiller l'acces aux secrets :

# Kubernetes audit policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ''
resources: ['secrets']

Alertes

Configurer des alertes pour :

  • Acces aux secrets depuis des sources inattendues
  • Tentatives echouees de recuperation de secrets
  • Rappels de rotation des secrets
  • Certificats expires

Procedures d'urgence

Reponse a une compromission de secret

  1. Identifier - Determiner quels secrets sont compromis
  2. Revoquer - Faire la rotation des secrets compromis immediatement
  3. Deployer - Mettre a jour les applications avec les nouveaux secrets
  4. Investiguer - Analyser comment la compromission s'est produite
  5. Remedier - Corriger les vulnerabilites qui ont mene a la compromission
  6. Documenter - Creer un rapport d'incident

Commandes de rotation rapide

# Rotation immediate du JWT secret
NEW_SECRET=$(openssl rand -hex 32)
docker secret rm jwt_secret
echo "$NEW_SECRET" | docker secret create jwt_secret -
docker service update --secret-rm jwt_secret --secret-add jwt_secret mytv-api

# Ou pour Kubernetes
kubectl create secret generic mytv-api-secrets \
--from-literal=JWT_SECRET="$NEW_SECRET" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl rollout restart deployment/mytv-api -n mytv-production

Bonnes pratiques de securite

A faire

  • Utiliser la gestion de secrets (Docker secrets, K8s secrets)
  • Faire la rotation des secrets regulierement
  • Utiliser des secrets differents par environnement
  • Chiffrer les secrets au repos
  • Limiter l'acces aux secrets (principe du moindre privilege)
  • Auditer l'acces aux secrets
  • Utiliser des secrets forts, generes aleatoirement

A ne pas faire

  • Commiter des secrets dans le controle de version
  • Logger des secrets
  • Passer des secrets en arguments de ligne de commande
  • Partager des secrets par chat/email
  • Hardcoder des secrets dans le code
  • Utiliser les memes secrets entre environnements
  • Utiliser des secrets faibles ou previsibles