39 KiB
AUDIT TECHNIQUE EXHAUSTIF - VEZA STREAM SERVER
Module: veza-stream-server (Rust)
Date: 2025-01-27
Version du module: 0.2.0
Auditeur: AI Technical Auditor
Méthodologie: Audit ultra-exhaustif, priorisé, actionnable
PHASE A — CARTOGRAPHIE DU MODULE
1. But du module
Rôle exact dans Veza : Serveur de streaming audio haute performance avec :
- Streaming HTTP Range Requests (partial content)
- WebSocket pour streaming temps réel synchronisé multi-client
- Transcodage audio (FFmpeg pipeline)
- Génération HLS (HTTP Live Streaming)
- Synchronisation multi-client (<10ms de précision)
- Cache audio et compression adaptative
- Analytics et monitoring
Position dans l'architecture Veza :
- Backend Go (veza-backend-api) : API REST, gestion utilisateurs, métadonnées
- Chat Server Rust : WebSocket chat
- Stream Server Rust (ce module) : Streaming audio, transcodage, HLS
- Frontend React : Interface utilisateur
2. Entrées / Sorties
APIs exposées
HTTP REST (Port 3002/8082)
GET /: Health check basiqueGET /health: Health check détailléGET /healthz: Liveness probe (K8s)GET /readyz: Readiness probe (K8s)GET /metrics: Métriques PrometheusGET /stream/:filename?expires=&sig=: Streaming audio avec signature HMACPOST /internal/jobs/transcode: Transcodage interne (non authentifié ⚠️)POST /v1/stream/transcode: Transcodage HLSGET /v1/stream/job/:id: Statut job transcodageGET /api/streams/jobs/:id/status: Statut détaillé jobGET /v1/stream/hls/:job_id/index.m3u8: Manifest HLS masterGET /v1/stream/hls/:job_id/:segment: Segment HLS (.ts)GET /hls/:track_id/master.m3u8: Master playlist HLSGET /hls/:track_id/:quality/playlist.m3u8: Playlist qualité spécifiqueGET /hls/:track_id/:quality/:segment: Segment HLS
WebSocket (Port 3002/8082)
GET /ws?token=&user_id=: Connexion WebSocket pour streaming temps réel- Commandes : Play, Pause, Stop, Seek, Subscribe, GetStatus, Ping
- Événements : PlaybackStarted, PlaybackPaused, PlaybackFinished, Error
gRPC (si activé)
- Service
StreamService(proto/stream/stream.proto) - Service
AuthService(proto/common/auth.proto)
Formats de données
JSON :
- Requêtes/Réponses REST : JSON standard
- WebSocket messages : JSON avec
typediscriminator - Health checks : JSON structuré
Protobuf :
- gRPC services : Messages définis dans
proto/
Binaire :
- Audio streams : MP3, WAV, FLAC, AAC, Opus
- HLS segments : .ts (MPEG-TS)
- HLS playlists : .m3u8 (text)
3. Dépendances internes
Modules Veza partagés
veza-common: Types communs, utilitaires partagés- Path :
../veza-common(workspace local)
- Path :
Types communs attendus
- UUID pour IDs (utilisateurs, tracks, streams)
- Formats de dates/timestamps (chrono)
- Structures d'erreurs standardisées
4. Dépendances externes
Base de données
- PostgreSQL (via sqlx 0.7)
- Tables :
stream_jobs,stream_segments - Références :
tracks(table externe, backend Go) - Pool : 10 max, 1 min, timeout 30s
- Tables :
Cache
- Redis (optionnel, via redis 0.25)
- URL :
REDIS_URL(env) - Pool : configurable
- URL :
Message Queue
- RabbitMQ (via lapin 2.3)
- URL :
RABBITMQ_URL(env, défaut:amqp://guest:guest@localhost:5672/) - Mode dégradé : Le serveur démarre même si RabbitMQ est down
- URL :
Stockage fichiers
- Filesystem local :
AUDIO_DIR(défaut:./audio) - S3/MinIO : ❌ Absent (stockage local uniquement)
Outils externes
- FFmpeg : Transcodage audio (optionnel, chemin via
FFMPEG_PATH)- Formats : MP3, AAC, FLAC, Opus, HLS
Services externes
- Backend API :
BACKEND_URL(défaut:http://backend-api:8080)- Utilisé pour : Validation auth, métadonnées tracks
5. Exécution
Commandes de build/run
# Build
cargo build --release
# Run (dev)
cargo run
# Run (production)
./target/release/stream_server
# Tests
cargo test
# Clippy (lint)
cargo clippy -- -D warnings
# Benchmarks
cargo bench
Configuration (variables d'environnement)
Obligatoires :
SECRET_KEY: Clé HMAC (min 32 chars) pour signatures URLsDATABASE_URL: PostgreSQL connection stringJWT_SECRET: Secret JWT (min 32 chars)
Optionnelles (avec defaults) :
STREAM_PORT/PORT: Port serveur (défaut: 3002)AUDIO_DIR: Dossier fichiers audio (défaut:./audio)ALLOWED_ORIGINS: CORS origins (défaut:*⚠️)MAX_FILE_SIZE: Taille max fichier (défaut: 100MB)MAX_RANGE_SIZE: Taille max range request (défaut: 10MB)RABBITMQ_URL: URL RabbitMQ (défaut:amqp://guest:guest@localhost:5672/)REDIS_URL: URL Redis (optionnel)RUST_LOG: Niveau logging (défaut:stream_server=info)
Fichiers de config :
.env: Variables d'environnement (optionnel, chargé via dotenv)env.example: Template de configuration
Docker
# Build
docker build -t veza-stream-server .
# Run (compose)
docker-compose up
# Run (production compose)
docker-compose -f docker-compose.production.yml up
Dockerfile :
- Multi-stage build (builder + runtime)
- Base :
rust:alpine→alpine:latest - User non-root :
app(UID 1001) - Healthcheck :
wget http://localhost:8082/health - Port exposé : 8082
Kubernetes
Manifests : k8s/production/
stream-server-deployment.yaml: Deploymentconfigmap.yaml: Configurationsecrets.yaml: Secrets (DB, JWT, TLS)
6. Points d'intégration avec autres services
Contrats d'API attendus
Backend Go (veza-backend-api)
- Auth : Validation JWT via
JWT_SECRETpartagé- Audience :
veza-services - Issuer :
veza-platform
- Audience :
- Métadonnées : Table
tracks(UUID, metadata)- Référence FK :
stream_jobs.track_id → tracks.id
- Référence FK :
- Format : UUID v4 pour tous les IDs
Frontend React
- CORS :
ALLOWED_ORIGINS(⚠️*par défaut en dev) - WebSocket :
ws://host:port/ws?token=<JWT>&user_id=<UUID> - Streaming : URLs signées avec
expiresetsig(HMAC-SHA256)
Auth (JWT/SSO)
JWT Validation :
- Header :
Authorization: Bearer <token> - Query param :
?token=<JWT>(WebSocket) - Validation :
iss=veza-platform,aud=veza-services,alg=HS256 - Expiration : Configurable (défaut: 3600s)
- Révocation : Session-based (HashMap en mémoire ⚠️)
Permissions :
StreamAudio: Accès streaming basiqueStreamHighQuality: Streaming haute qualitéStreamUnlimited: Streaming illimitéUploadAudio: Upload fichiersSystemAdmin: Admin système
Rôles :
Admin,Moderator,User,Premium,Artist,Guest
Headers HTTP
CORS :
Access-Control-Allow-Origin:ALLOWED_ORIGINS(⚠️*par défaut)Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONSAccess-Control-Allow-Headers: Content-Type, Authorization, Accept
Sécurité :
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockContent-Security-Policy: default-src 'none'; media-src 'self'Referrer-Policy: strict-origin-when-cross-origin
Schéma DB / UUID / Conventions
UUID :
- Format : UUID v4 (via
uuidcrate) - IDs utilisateurs :
String(UUID en string) - IDs tracks :
UUID(PostgreSQL) - IDs streams :
UUID(PostgreSQL)
Tables PostgreSQL :
stream_jobs: Jobs transcodage (FK →tracks.id)stream_segments: Segments HLS (FK →tracks.id)tracks: ❌ Table externe (backend Go, doit exister)
Conventions :
- Timestamps :
TIMESTAMPTZ(PostgreSQL) - Durées :
FLOAT(secondes) ouINTEGER(millisecondes) - Status :
TEXTavec CHECK constraints
PHASE B — SANTÉ TECHNIQUE
Build Status
Compilation
État : ✅ Compile avec warnings
Commandes de validation :
cargo check # ✅ Passe (avec warnings)
cargo build --release # ✅ Passe
Warnings détectés :
- 174 occurrences de
unwrap()/expect()dans 39 fichiers - Dépendances obsolètes (axum 0.7 vs 0.8, sqlx 0.7 vs 0.8, etc.)
- Clippy warnings non traités (109 warnings selon audit précédent)
Erreurs de types / Linters
Clippy :
cargo clippy -- -D warnings
- ⚠️ 109 warnings (non bloquants mais qualité dégradée)
- Patterns détectés :
unwrap(),expect(), allocations inutiles, clones
Rustfmt :
cargo fmt --check
- ✅ Formatage cohérent (sauf fichiers non formatés)
Tests
Couverture :
- Tests unitaires : ~25% (estimé)
- Tests d'intégration : ❌ Absents
- Tests E2E : ❌ Absents
Fichiers de tests :
src/lib.rs: 1 test basique (structure)src/config/mod.rs: 3 tests (config, dotenv, default)src/error.rs: 6 tests (conversions erreurs)src/structured_logging.rs: 5 tests (logging)src/utils/mod.rs: 3 tests (validation, signature)src/middleware/security.rs: 3 tests (patterns dangereux)src/database/pool.rs: 2 tests (ignorés, nécessitent DB)
Tests manquants :
- ❌ Tests WebSocket (connexion, messages, déconnexion)
- ❌ Tests streaming HTTP Range
- ❌ Tests transcodage FFmpeg
- ❌ Tests HLS generation
- ❌ Tests synchronisation multi-client
- ❌ Tests auth JWT
- ❌ Tests rate limiting
- ❌ Tests cache
- ❌ Tests analytics
Gestion des erreurs
Patterns utilisés :
- ✅
Result<T, AppError>pour erreurs applicatives - ✅
thiserrorpour définitions d'erreurs - ✅
anyhowpour erreurs contextuelles - ⚠️ 174
unwrap()/expect(): Risque de panics en production
Fichiers avec le plus d'unwrap() :
src/core/sync.rs: 16 occurrencessrc/analytics/mod.rs: 11 occurrencessrc/auth/mod.rs: 12 occurrencessrc/middleware/logging.rs: 6 occurrencessrc/routes/api.rs: 3 occurrences
Erreurs HTTP :
- ✅ Mapping
AppError→StatusCode(viaIntoResponse) - ✅ Messages d'erreur structurés (JSON)
- ⚠️ Certaines erreurs retournent 500 au lieu de codes appropriés
Cohérence des conventions
Naming :
- ✅ Snake_case pour fonctions/variables
- ✅ PascalCase pour types
- ✅ Modules organisés par domaine
Structure dossiers :
src/
├── analytics/ # Analytics engine
├── audio/ # Audio processing
├── auth/ # Authentication
├── cache/ # File cache
├── codecs/ # Audio codecs
├── config/ # Configuration
├── core/ # Core streaming logic
├── database/ # DB pool
├── middleware/ # HTTP middlewares
├── monitoring/ # Metrics, health
├── routes/ # HTTP routes
├── streaming/ # Streaming protocols
├── transcoding/ # FFmpeg transcoding
└── utils/ # Utilities
Séparation couches :
- ✅ Routes → Handlers → Services → DB
- ✅ Middlewares séparés (auth, rate limit, security, logging)
- ⚠️ Certains handlers font trop de logique métier
PHASE C — SÉCURITÉ
Top 10 Risques Sécurité
1. MOD-P0-001 : CORS permissif par défaut (*)
Impact : Attaques CSRF, accès non autorisé depuis n'importe quelle origine
Preuve :
allowed_origins: env::var("ALLOWED_ORIGINS")
.unwrap_or_else(|_| "*".to_string())
.split(',')
.map(|s| s.trim().to_string())
.collect(),
let cors = if allowed_origins_str.trim() == "*" {
// Mode développement: autoriser toutes les origines
CorsLayer::new()
.allow_origin(Any)
Cause racine : Configuration par défaut trop permissive pour faciliter le développement
Fix minimal :
// Dans src/config/mod.rs
allowed_origins: env::var("ALLOWED_ORIGINS")
.unwrap_or_else(|_| {
if cfg!(debug_assertions) {
"http://localhost:5176,http://localhost:3000".to_string()
} else {
panic!("ALLOWED_ORIGINS must be set in production")
}
})
Plan de validation :
- Test :
curl -H "Origin: https://evil.com" http://localhost:8082/health - Vérifier : Header
Access-Control-Allow-Originne doit pas être*en prod - Test : Connexion WebSocket depuis origine non autorisée doit échouer
Effet de bord : Devs doivent configurer ALLOWED_ORIGINS en local
Effort : S (1h)
2. MOD-P0-002 : Endpoint transcodage interne non authentifié
Impact : N'importe qui peut déclencher des jobs de transcodage (DoS, consommation CPU)
Preuve :
.route("/internal/jobs/transcode", post(internal_transcode_handler))
async fn internal_transcode_handler(
State(state): State<AppState>,
Json(payload): Json<serde_json::Value>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
// Aucune vérification d'authentification
Cause racine : Endpoint prévu pour usage interne mais exposé publiquement
Fix minimal :
// Ajouter middleware auth ou vérification IP
.route(
"/internal/jobs/transcode",
post(internal_transcode_handler)
.layer(axum::middleware::from_fn_with_state(
state.clone(),
auth_middleware,
))
)
Plan de validation :
- Test :
curl -X POST http://localhost:8082/internal/jobs/transcode -d '{}' - Vérifier : 401 Unauthorized sans token
- Test : Avec token valide, doit fonctionner
Effet de bord : Services internes doivent s'authentifier
Effort : S (1h)
3. MOD-P0-003 : WebSocket sans authentification obligatoire
Impact : Connexions WebSocket anonymes possibles, accès streaming non contrôlé
Preuve :
// Si un token est fourni, on le valide (pour l'instant, on accepte la connexion)
// En production, on validerait le token avec AuthManager
let user_id = params.user_id.or_else(|| {
// Si un token est fourni, on pourrait extraire user_id du token
// Pour l'instant, on utilise le user_id fourni dans les params
None
});
Cause racine : Authentification WebSocket non implémentée (TODO)
Fix minimal :
// Dans websocket_handler
let token = params.token.ok_or_else(|| {
tracing::warn!("WebSocket connection without token rejected");
return StatusCode::UNAUTHORIZED;
})?;
let validation_result = auth_manager.validate_token(&token).await;
if !validation_result.valid {
return StatusCode::UNAUTHORIZED.into_response();
}
Plan de validation :
- Test :
wscat -c "ws://localhost:8082/ws"(sans token) - Vérifier : Connexion rejetée
- Test : Avec token valide, connexion acceptée
Effet de bord : Clients doivent fournir JWT pour WebSocket
Effort : M (4h)
4. MOD-P0-004 : Secrets en clair dans k8s/secrets.yaml
Impact : Secrets commités dans Git (même si base64, facilement décodables)
Preuve :
data:
url: cG9zdGdyZXNxbDovL3ZlemE6c2VjdXJlX3Bhc3NAcG9zdGdyZXM6NTQzMi92ZXphX3Byb2Q=
Cause racine : Secrets de test commités par erreur
Fix minimal :
- Supprimer
k8s/production/secrets.yamldu repo - Ajouter à
.gitignore:**/secrets.yaml - Documenter : Utiliser Sealed Secrets ou Vault
Plan de validation :
- Vérifier :
git log --all --full-history -- k8s/production/secrets.yaml - Vérifier : Fichier absent du repo
- Test : Secrets créés via
kubectl create secret
Effet de bord : Devs doivent créer secrets manuellement
Effort : S (30min)
5. MOD-P1-005 : Rate limiting non implémenté (stub)
Impact : Pas de protection contre DoS, requêtes illimitées
Preuve :
async fn check_rate_limit(state: &AppState, client_ip: &str) -> bool {
// Implémentation basique du rate limiting
// Dans une vraie application, on utiliserait un store externe comme Redis
let max_requests_per_minute = state.config.security.rate_limit_requests_per_minute;
let _now = Instant::now();
// Pour cette implémentation basique, on permet toutes les requêtes
// En production, il faudrait implémenter un vrai système de rate limiting
tracing::debug!(
client_ip = %client_ip,
limit = max_requests_per_minute,
"Vérification du rate limit"
);
true // ⚠️ TOUJOURS TRUE
}
Cause racine : Implémentation placeholder non complétée
Fix minimal :
// Utiliser governor ou implémenter avec Redis
use governor::{Quota, RateLimiter};
let limiter = RateLimiter::keyed(Quota::per_minute(nonzero!(max_requests_per_minute)));
limiter.check_key(&client_ip).is_ok()
Plan de validation :
- Test : 1000 requêtes rapides depuis même IP
- Vérifier : Après 60 req/min, 429 Too Many Requests
- Test : Différentes IPs, limites indépendantes
Effet de bord : Redis requis si implémentation distribuée
Effort : M (4h)
6. MOD-P1-006 : Révocation JWT en mémoire (non persistant)
Impact : Tokens révoqués revalidés après redémarrage serveur
Preuve :
revoked_tokens: Arc<tokio::sync::RwLock<HashMap<String, u64>>>, // session_id -> revocation_time
Cause racine : Stockage en mémoire pour simplicité
Fix minimal :
- Option 1 : Redis pour révocation (TTL = expiration token)
- Option 2 : DB table
revoked_tokens(session_id, revoked_at, expires_at)
Plan de validation :
- Test : Révoquer token, vérifier rejeté
- Test : Redémarrer serveur, token toujours rejeté (si Redis/DB)
- Test : Nettoyage automatique tokens expirés
Effet de bord : Redis/DB requis pour révocation persistante
Effort : M (4h)
7. MOD-P1-007 : Validation signature HMAC avec timing attack possible
Impact : Attaque par timing pour deviner signature (faible mais présent)
Preuve :
use subtle::ConstantTimeEq;
expected_sig.as_bytes().ct_eq(sig.as_bytes()).into()
✅ DÉJÀ CORRIGÉ : Utilise subtle::ConstantTimeEq (comparaison temps constant)
Note : Pas de problème, mais vérifier que tous les usages utilisent ct_eq
8. MOD-P1-008 : Path traversal possible si validation bypass
Impact : Accès fichiers hors AUDIO_DIR (si validation contournée)
Preuve :
pub fn build_safe_path(config: &Config, filename: &str) -> Result<PathBuf> {
let audio_dir = Path::new(&config.audio_dir);
let file_path = audio_dir.join(filename);
// Utiliser un check basique au lieu de canonicalize qui nécessite que le fichier existe
// Ceci est nécessaire pour les tests où audio_dir peut ne pas exister physiquement
// Dans un environnement de prod, le dossier existe.
// Pour la sécurité, on vérifie juste qu'il n'y a pas de composants ".." après normalisation
// Note: std::fs::canonicalize requires file to exist.
// We can try to simplify path manually or just rely on validate_filename having rejected ".."
// Pour une sécurité maximale tout en permettant les tests:
let normalized_path = file_path; // validate_filename a déjà filtré ".."
Ok(normalized_path)
}
Cause racine : canonicalize() nécessite fichier existant, donc contourné
Fix minimal :
// Vérifier que le chemin résolu est bien dans audio_dir
let canonical_audio_dir = std::fs::canonicalize(&config.audio_dir)?;
let canonical_file_path = std::fs::canonicalize(&file_path)?;
if !canonical_file_path.starts_with(&canonical_audio_dir) {
return Err(AppError::Forbidden);
}
Plan de validation :
- Test :
GET /stream/../../../etc/passwd?expires=...&sig=... - Vérifier : 403 Forbidden ou 400 Bad Request
- Test : Fichier valide dans
AUDIO_DIR, doit fonctionner
Effet de bord : AUDIO_DIR doit exister (pas de problème en prod)
Effort : S (2h)
9. MOD-P2-009 : Headers sécurité manquants (HSTS, CSP strict)
Impact : Vulnérabilités XSS, clickjacking (mitigé mais pas optimal)
Preuve :
// HSTS (si HTTPS)
// Note: À activer uniquement en HTTPS
// headers.insert(
// HeaderName::from_static("strict-transport-security"),
// HeaderValue::from_static("max-age=31536000; includeSubDomains")
// );
Cause racine : HSTS commenté (nécessite HTTPS)
Fix minimal :
if config.tls_enabled() {
headers.insert(
HeaderName::from_static("strict-transport-security"),
HeaderValue::from_static("max-age=31536000; includeSubDomains")
);
}
Plan de validation :
- Test : Requête HTTPS, vérifier header
Strict-Transport-Security - Test : Requête HTTP, header absent
Effet de bord : Nécessite TLS configuré
Effort : S (1h)
10. MOD-P2-010 : Injection SQL possible si requêtes non paramétrées
Impact : SQL injection si requêtes construites dynamiquement (non vérifié partout)
Preuve :
// Utiliser des requêtes SQL simples sans macros pour éviter les erreurs de driver
let total_sessions = sqlx::query(
"SELECT COUNT(*) as count FROM play_sessions WHERE started_at BETWEEN ? AND ?",
)
.bind(start_ts)
.bind(end_ts)
✅ DÉJÀ PROTÉGÉ : Utilise sqlx::query() avec .bind() (requêtes paramétrées)
Note : Vérifier tous les usages de sqlx::query! et sqlx::query_scalar! utilisent des paramètres
Autres Risques Sécurité (P2/P3)
MOD-P2-011 : Secrets dans logs (potentiel)
Impact : Tokens JWT, secrets exposés dans logs
Preuve : Vérifier tous les tracing::*! ne loggent pas de secrets
Fix : Audit des logs, masquer tokens/secrets
Effort : M (4h)
MOD-P2-012 : Pas de validation taille upload
Impact : DoS via upload fichiers énormes
Preuve : Endpoint upload non trouvé (peut-être absent)
Fix : Limiter taille body requests
Effort : S (2h)
MOD-P3-013 : Dépendances obsolètes (vulnérabilités)
Impact : Vulnérabilités connues dans dépendances
Preuve :
axum 0.7(latest: 0.8)sqlx 0.7(latest: 0.8)jsonwebtoken 9.2(latest: 10.2)
Fix : cargo audit + mise à jour dépendances
Effort : M (4h)
PHASE D — ROBUSTESSE & OBSERVABILITÉ
Logs structurés
État : ✅ Implémenté avec tracing
Format :
- Dev : Pretty (lisible)
- Prod : JSON (structuré)
Champs contextuels :
stream_id,user_id,track_id,client_ip,event, etc.
Rotation :
- ✅ Configuré (via
tracing-appender) - Rotation : Daily (>100MB) ou Hourly (≤100MB)
Niveaux :
RUST_LOG:stream_server=info(défaut)- Filtrage par module possible
Gaps :
- ⚠️ Pas de corrélation
request_id/trace_idautomatique - ⚠️ Logs fichiers optionnels (pas de config par défaut)
Métriques
État : ✅ Prometheus intégré
Endpoints :
GET /metrics: Métriques Prometheus
Métriques disponibles :
- Via
metricscrate (0.22) - Exporter :
metrics-exporter-prometheus(0.13)
Gaps :
- ⚠️ Métriques custom non documentées
- ⚠️ Pas de dashboard Grafana fourni (template existe mais non déployé)
Healthchecks
État : ✅ Implémenté
Endpoints :
GET /healthz: Liveness (basique)GET /readyz: Readiness (détaillé)GET /health: Health détaillé
Vérifications :
- ✅ Database : Ping PostgreSQL
- ✅ Audio directory : Existence
- ✅ RabbitMQ : Connexion (mode dégradé si down)
- ✅ Services : Statut global
Gaps :
- ⚠️ Pas de vérification Redis (si activé)
- ⚠️ Pas de vérification FFmpeg disponible
Timeouts / Retries / Circuit Breakers
Timeouts :
- ✅ HTTP : 30s (via
TimeoutLayer) - ✅ DB : 30s (connection timeout)
- ⚠️ WebSocket : Pas de timeout explicite
- ⚠️ FFmpeg : Pas de timeout (peut bloquer indéfiniment)
Retries :
- ✅ RabbitMQ : 3 retries avec backoff (2s)
- ⚠️ DB : Pas de retry automatique
- ⚠️ HTTP client (reqwest) : Pas de retry configuré
Circuit Breakers :
- ❌ Absent
Gestion de charge
Pool DB :
- ✅ Configuré : 10 max, 1 min
- ✅ Timeouts : 30s acquire, 600s idle, 3600s max lifetime
WebSocket :
- ⚠️ Pas de limite de connexions
- ⚠️ Pas de backpressure
Streaming :
- ✅
MAX_RANGE_SIZE: 10MB - ✅
MAX_FILE_SIZE: 100MB - ⚠️ Pas de limite de streams simultanés
FFmpeg :
- ✅
max_concurrent_jobs: 4 (configurable) - ⚠️ Pas de queue limit (peut consommer mémoire)
Migrations / Compatibilité
Migrations :
- ✅ SQLx migrations :
migrations/ - ✅ Auto-migration :
DB_MIGRATE_ON_START=true(défaut)
Tables :
stream_jobs: ✅ Migration 001stream_segments: ✅ Migration 002- ⚠️ FK vers
tracks: Table externe (backend Go), doit exister
Compatibilité :
- ⚠️ Pas de versioning de schéma
- ⚠️ Pas de rollback migrations
PHASE E — PERFORMANCE & SCALABILITÉ
Hotspots évidents
1. Allocations inutiles
Preuve :
- Clones de
AppState(Arc mais clones fréquents) - String allocations dans hot paths
Fix : Utiliser Cow<str>, &str où possible
Effort : M (4h)
2. Copies de buffers audio
Preuve : Streaming peut copier buffers inutilement
Fix : Utiliser Bytes (zero-copy) pour streaming
Effort : M (6h)
3. JSON sérialisation dans hot paths
Preuve : WebSocket messages sérialisés à chaque envoi
Fix : Cache sérialisation ou utiliser binaire (MessagePack)
Effort : L (8h)
4. Locks contention (DashMap/RwLock)
Preuve :
WebSocketManager.connections:RwLock<HashMap>SyncEngine: MultiplesDashMap/RwLock
Fix : Sharding, lock-free structures où possible
Effort : L (10h)
5. FFmpeg blocking dans async
Preuve : FFmpeg exécuté dans threads bloquants (correct) mais monitoring peut bloquer
Fix : Vérifier tous les appels FFmpeg sont dans spawn_blocking
Effort : M (4h)
Streaming optimisations
HTTP Range :
- ✅ Implémenté avec
tokio::fs::File::seek - ✅ Streaming avec
ReaderStream(pas de chargement complet en mémoire)
HLS :
- ⚠️ Génération playlists synchrones (peut bloquer)
- ⚠️ Segments non pré-générés (génération à la volée)
WebSocket :
- ✅ Broadcast channel (1000 capacity)
- ⚠️ Pas de compression WebSocket (per-message deflate)
Rust-specific
Async runtime :
- ✅ Tokio 1.35 (à jour)
- ⚠️ Pas de tuning runtime (threads, etc.)
Blocking in async :
- ⚠️ Vérifier tous les I/O bloquants sont dans
spawn_blocking
Memory :
- ⚠️ Pas de profiling mémoire
- ⚠️ Pas de limites mémoire explicites
PHASE F — LISTE EXHAUSTIVE DES PROBLÈMES
P0 — Bloquants (Sécurité / Crash / Build)
| ID | Titre | Impact | Preuve | Cause | Fix | Validation | Effet de bord | Effort |
|---|---|---|---|---|---|---|---|---|
| MOD-P0-001 | CORS permissif * par défaut |
CSRF, accès non autorisé | src/config/mod.rs:340, src/routes/api.rs:38 |
Default trop permissif | Require ALLOWED_ORIGINS en prod |
Test CORS depuis origine non autorisée | Devs doivent configurer | S (1h) |
| MOD-P0-002 | Endpoint /internal/jobs/transcode non authentifié |
DoS, jobs transcodage illimités | src/routes/api.rs:118,146 |
Endpoint interne exposé | Ajouter middleware auth | Test POST sans token → 401 | Services internes doivent s'auth | S (1h) |
| MOD-P0-003 | WebSocket sans auth obligatoire | Streaming anonyme possible | src/streaming/websocket.rs:802 |
Auth non implémentée | Valider JWT avant connexion | Test WS sans token → rejet | Clients doivent fournir JWT | M (4h) |
| MOD-P0-004 | Secrets en clair dans Git | Secrets compromis | k8s/production/secrets.yaml:8 |
Secrets commités | Supprimer du repo, utiliser Sealed Secrets | Vérifier fichier absent | Devs créent secrets manuellement | S (30min) |
| MOD-P0-005 | Table tracks externe non vérifiée |
FK violations, crashes | migrations/001_create_stream_jobs.sql:14 |
Dépendance externe non validée | Vérifier existence table au démarrage | Test FK violation → erreur claire | Backend Go doit créer table | S (2h) |
P1 — Critiques (Bugs fréquents / Dette bloquante)
| ID | Titre | Impact | Preuve | Cause | Fix | Validation | Effet de bord | Effort |
|---|---|---|---|---|---|---|---|---|
| MOD-P1-006 | Rate limiting stub (toujours true) |
Pas de protection DoS | src/middleware/rate_limit.rs:72 |
Implémentation placeholder | Implémenter avec governor ou Redis |
Test 1000 req → 429 après limite | Redis requis si distribué | M (4h) |
| MOD-P1-007 | Révocation JWT en mémoire | Tokens révoqués revalidés après restart | src/auth/mod.rs:124 |
Stockage non persistant | Redis ou DB pour révocation | Test révocation → restart → toujours rejeté | Redis/DB requis | M (4h) |
| MOD-P1-008 | Path traversal si validation bypass | Accès fichiers hors AUDIO_DIR |
src/utils/mod.rs:51 |
canonicalize() contourné |
Vérifier chemin résolu dans AUDIO_DIR |
Test ../../../etc/passwd → 403 |
AUDIO_DIR doit exister |
S (2h) |
| MOD-P1-009 | 174 unwrap() / expect() |
Panics en production | grep -r "unwrap|expect" src/ (174 matches) |
Gestion erreurs incomplète | Remplacer par ? ou match |
Test erreurs → pas de panic | Code plus verbeux | L (20h) |
| MOD-P1-010 | FFmpeg sans timeout | Jobs peuvent bloquer indéfiniment | src/transcoding/engine.rs (à vérifier) |
Pas de timeout configuré | Ajouter timeout FFmpeg (5min) | Test job bloqué → timeout | Jobs longs peuvent échouer | S (2h) |
| MOD-P1-011 | WebSocket pas de limite connexions | DoS mémoire | src/streaming/websocket.rs:231 |
Pas de limite | Limiter connexions (1000 max) | Test 2000 connexions → rejet | Clients peuvent être rejetés | S (2h) |
| MOD-P1-012 | HLS segments générés à la volée | Latence première requête | src/routes/api.rs:356 |
Pas de pré-génération | Pré-générer segments lors transcodage | Test première requête → rapide | Stockage requis | M (6h) |
| MOD-P1-013 | Pas de tests intégration | Bugs non détectés | Aucun test dans tests/ |
Tests manquants | Ajouter tests intégration | cargo test --test integration |
Maintenance tests | L (16h) |
P2 — Majeurs (Qualité / Maintenabilité)
| ID | Titre | Impact | Preuve | Cause | Fix | Validation | Effet de bord | Effort |
|---|---|---|---|---|---|---|---|---|
| MOD-P2-014 | 109 warnings Clippy | Qualité code dégradée | cargo clippy (109 warnings) |
Warnings non traités | Corriger warnings | cargo clippy → 0 warnings |
Code plus strict | M (8h) |
| MOD-P2-015 | Dépendances obsolètes | Vulnérabilités, perf | Cargo.toml (axum 0.7, sqlx 0.7) |
Pas de mise à jour | cargo update + tests |
cargo audit → 0 vulns |
Tests de régression | M (4h) |
| MOD-P2-016 | Pas de request_id / trace_id |
Debugging difficile | Logs sans corrélation | Pas implémenté | Middleware request_id |
Test logs → même request_id |
Overhead minimal | M (4h) |
| MOD-P2-017 | HSTS manquant (si HTTPS) | Man-in-the-middle | src/middleware/security.rs:178 |
Commenté | Activer si TLS | Test HTTPS → header HSTS | Nécessite TLS | S (1h) |
| MOD-P2-018 | Pas de circuit breaker | Cascading failures | Absent | Pas implémenté | Ajouter circuit breaker (DB, Redis) | Test DB down → circuit ouvert | Retries échouent plus tôt | M (6h) |
| MOD-P2-019 | Pas de retry DB | Erreurs transitoires non récupérées | src/database/pool.rs |
Pas de retry | Ajouter retry avec backoff | Test DB temporairement down → retry | Latence augmentée | S (2h) |
| MOD-P2-020 | Pas de monitoring Redis | État Redis inconnu | Health check ne vérifie pas Redis | Non implémenté | Ajouter check Redis dans health | Test Redis down → health degraded | Redis requis | S (1h) |
| MOD-P2-021 | Pas de compression WebSocket | Bande passante gaspillée | src/streaming/websocket.rs |
Pas activé | Activer per-message deflate | Test taille messages → réduite | CPU légèrement augmenté | S (2h) |
| MOD-P2-022 | Pas de pré-génération HLS | Latence première requête | src/routes/api.rs:356 |
Génération à la volée | Pré-générer lors transcodage | Test première requête → rapide | Stockage requis | M (6h) |
| MOD-P2-023 | Pas de cache Redis pour métadonnées | Requêtes DB répétées | src/cache/audio_cache.rs |
Cache fichiers uniquement | Cache DB queries | Test requêtes répétées → cache hit | Redis requis | M (4h) |
| MOD-P2-024 | Pas de backpressure WebSocket | Mémoire illimitée | src/streaming/websocket.rs:248 |
Pas de limite | Limiter buffer messages | Test messages rapides → backpressure | Messages peuvent être perdus | M (4h) |
| MOD-P2-025 | Pas de profiling mémoire | Fuites mémoire non détectées | Absent | Pas d'outil | Ajouter dhat ou valgrind |
Test fuites → détectées | Overhead profiling | S (2h) |
P3 — Mineurs (Cosmétique / Refactors)
| ID | Titre | Impact | Preuve | Cause | Fix | Validation | Effet de bord | Effort |
|---|---|---|---|---|---|---|---|---|
| MOD-P3-026 | Documentation cargo doc incomplète | DX dégradée | cargo doc (docstrings manquantes) |
Pas de doc | Ajouter docstrings | cargo doc --open → docs complètes |
Maintenance docs | M (8h) |
| MOD-P3-027 | Tests unitaires manquants | Couverture faible (~25%) | cargo test (peu de tests) |
Tests non écrits | Ajouter tests unitaires | cargo test --coverage → >80% |
Maintenance tests | L (20h) |
| MOD-P3-028 | Pas de benchmarks | Performance non mesurée | cargo bench (peu de benches) |
Benchmarks manquants | Ajouter benchmarks | cargo bench → métriques |
Maintenance benches | M (6h) |
| MOD-P3-029 | Code dupliqué (validation patterns) | Maintenance difficile | Plusieurs fichiers | Pas de factorisation | Extraire helpers | Test factorisation → moins de code | Refactor | M (4h) |
| MOD-P3-030 | Pas de migration rollback | Rollback impossible | migrations/ (pas de down) |
Pas implémenté | Ajouter migrations down | Test rollback → succès | Maintenance migrations | M (4h) |
PHASE G — PLAN D'EXÉCUTION
Checklist P0 (Ordre strict)
-
MOD-P0-004 : Supprimer secrets du repo (30min)
- Supprimer
k8s/production/secrets.yaml - Ajouter à
.gitignore - Documenter Sealed Secrets
- Supprimer
-
MOD-P0-001 : Fix CORS permissif (1h)
- Require
ALLOWED_ORIGINSen prod - Test CORS
- Require
-
MOD-P0-002 : Auth endpoint transcodage (1h)
- Ajouter middleware auth
- Test 401 sans token
-
MOD-P0-005 : Vérifier table
tracks(2h)- Check existence au démarrage
- Erreur claire si absente
-
MOD-P0-003 : Auth WebSocket (4h)
- Valider JWT avant connexion
- Test rejet sans token
Total P0 : ~8h (1 jour)
Checklist P1 (Par lots cohérents)
Lot 1 : Rate limiting + Révocation (8h)
- MOD-P1-006 : Implémenter rate limiting (4h)
- MOD-P1-007 : Révocation JWT persistante (4h)
Lot 2 : Sécurité paths + FFmpeg (4h)
- MOD-P1-008 : Fix path traversal (2h)
- MOD-P1-010 : Timeout FFmpeg (2h)
Lot 3 : WebSocket + HLS (10h)
- MOD-P1-011 : Limite connexions WS (2h)
- MOD-P1-012 : Pré-génération HLS (6h)
- MOD-P1-013 : Tests intégration (2h, début)
Lot 4 : Gestion erreurs (20h)
- MOD-P1-009 : Remplacer
unwrap()(20h, progressif)
Total P1 : ~42h (1 semaine)
Quick Wins (≤ 1h chacun)
- MOD-P2-017 : HSTS si HTTPS (1h)
- MOD-P2-020 : Monitoring Redis (1h)
- MOD-P2-021 : Compression WebSocket (2h)
- MOD-P2-019 : Retry DB (2h)
Total Quick Wins : ~6h
Tests à ajouter en priorité
-
Tests WebSocket (4h)
- Connexion, messages, déconnexion
- Auth, rate limiting
-
Tests streaming HTTP (4h)
- Range requests, signatures, path traversal
-
Tests transcodage (6h)
- FFmpeg pipeline, HLS generation, timeouts
-
Tests intégration (8h)
- End-to-end streaming, WebSocket sync
Total Tests : ~22h
PR Plan
PR 1 : Sécurité critique (P0)
Titre : fix(security): Remove secrets from repo, fix CORS, add auth to internal endpoints
Changements :
- MOD-P0-004 : Secrets
- MOD-P0-001 : CORS
- MOD-P0-002 : Auth transcodage
Tests : Tests sécurité ajoutés
PR 2 : WebSocket auth
Titre : feat(websocket): Add JWT authentication to WebSocket connections
Changements :
- MOD-P0-003 : Auth WebSocket
Tests : Tests WebSocket auth
PR 3 : Rate limiting + Révocation
Titre : feat(security): Implement rate limiting and persistent JWT revocation
Changements :
- MOD-P1-006 : Rate limiting
- MOD-P1-007 : Révocation
Tests : Tests rate limiting
PR 4 : Path traversal + FFmpeg timeout
Titre : fix(security): Fix path traversal validation and add FFmpeg timeout
Changements :
- MOD-P1-008 : Path traversal
- MOD-P1-010 : FFmpeg timeout
Tests : Tests sécurité
PR 5 : WebSocket improvements
Titre : feat(websocket): Add connection limits and compression
Changements :
- MOD-P1-011 : Limite connexions
- MOD-P2-021 : Compression
Tests : Tests WebSocket
PR 6 : HLS pré-génération
Titre : feat(hls): Pre-generate HLS segments during transcoding
Changements :
- MOD-P1-012 : Pré-génération HLS
Tests : Tests HLS
PR 7 : Tests intégration
Titre : test: Add integration tests for streaming and WebSocket
Changements :
- MOD-P1-013 : Tests intégration
PR 8 : Code quality
Titre : chore: Fix Clippy warnings and update dependencies
Changements :
- MOD-P2-014 : Clippy
- MOD-P2-015 : Dépendances
PR 9 : Observabilité
Titre : feat(observability): Add request_id, Redis monitoring, circuit breakers
Changements :
- MOD-P2-016 : Request ID
- MOD-P2-020 : Redis monitoring
- MOD-P2-018 : Circuit breakers
PR 10 : Gestion erreurs (progressif)
Titre : refactor: Replace unwrap() with proper error handling
Changements :
- MOD-P1-009 : Remplacer
unwrap()(par lots)
Tests : Tests erreurs
Estimation Totale
| Priorité | Problèmes | Effort | Délai |
|---|---|---|---|
| P0 | 5 | 8h | 1 jour |
| P1 | 8 | 42h | 1 semaine |
| P2 | 12 | 40h | 1 semaine |
| P3 | 5 | 42h | 1 semaine |
| Tests | - | 22h | 3 jours |
| TOTAL | 30 | 154h | ~4 semaines |
CONCLUSION
Le module veza-stream-server est fonctionnel mais présente des gaps critiques de sécurité et de robustesse qui doivent être corrigés avant production.
Points forts :
- Architecture solide (Axum, Tokio, SQLx)
- Logging structuré (tracing)
- Métriques Prometheus
- Health checks
Points faibles :
- Sécurité : CORS permissif, endpoints non authentifiés
- Robustesse : Rate limiting stub, révocation JWT non persistante
- Tests : Couverture faible (~25%)
- Performance : Pas de benchmarks, optimisations manquantes
Recommandation : Ne pas déployer en production avant correction des P0 et P1 critiques (sécurité).
Prochaines étapes :
- Corriger P0 (1 jour)
- Corriger P1 critiques (1 semaine)
- Ajouter tests (3 jours)
- Code review sécurité
- Déploiement staging
- Tests charge
- Déploiement production
Fin du rapport d'audit