veza/veza-stream-server/AUDIT_EXHAUSTIF_STREAM_SERVER.md
2025-12-12 21:34:34 -05:00

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 basique
  • GET /health : Health check détaillé
  • GET /healthz : Liveness probe (K8s)
  • GET /readyz : Readiness probe (K8s)
  • GET /metrics : Métriques Prometheus
  • GET /stream/:filename?expires=&sig= : Streaming audio avec signature HMAC
  • POST /internal/jobs/transcode : Transcodage interne (non authentifié ⚠️)
  • POST /v1/stream/transcode : Transcodage HLS
  • GET /v1/stream/job/:id : Statut job transcodage
  • GET /api/streams/jobs/:id/status : Statut détaillé job
  • GET /v1/stream/hls/:job_id/index.m3u8 : Manifest HLS master
  • GET /v1/stream/hls/:job_id/:segment : Segment HLS (.ts)
  • GET /hls/:track_id/master.m3u8 : Master playlist HLS
  • GET /hls/:track_id/:quality/playlist.m3u8 : Playlist qualité spécifique
  • GET /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 type discriminator
  • 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)

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

Cache

  • Redis (optionnel, via redis 0.25)
    • URL : REDIS_URL (env)
    • Pool : configurable

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

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 URLs
  • DATABASE_URL : PostgreSQL connection string
  • JWT_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:alpinealpine:latest
  • User non-root : app (UID 1001)
  • Healthcheck : wget http://localhost:8082/health
  • Port exposé : 8082

Kubernetes

Manifests : k8s/production/

  • stream-server-deployment.yaml : Deployment
  • configmap.yaml : Configuration
  • secrets.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_SECRET partagé
    • Audience : veza-services
    • Issuer : veza-platform
  • Métadonnées : Table tracks (UUID, metadata)
    • Référence FK : stream_jobs.track_id → tracks.id
  • 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 expires et sig (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 basique
  • StreamHighQuality : Streaming haute qualité
  • StreamUnlimited : Streaming illimité
  • UploadAudio : Upload fichiers
  • SystemAdmin : 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, OPTIONS
  • Access-Control-Allow-Headers : Content-Type, Authorization, Accept

Sécurité :

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Content-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 uuid crate)
  • 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) ou INTEGER (millisecondes)
  • Status : TEXT avec 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
  • thiserror pour définitions d'erreurs
  • anyhow pour erreurs contextuelles
  • ⚠️ 174 unwrap() / expect() : Risque de panics en production

Fichiers avec le plus d'unwrap() :

  • src/core/sync.rs : 16 occurrences
  • src/analytics/mod.rs : 11 occurrences
  • src/auth/mod.rs : 12 occurrences
  • src/middleware/logging.rs : 6 occurrences
  • src/routes/api.rs : 3 occurrences

Erreurs HTTP :

  • Mapping AppErrorStatusCode (via IntoResponse)
  • 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 :

  1. Test : curl -H "Origin: https://evil.com" http://localhost:8082/health
  2. Vérifier : Header Access-Control-Allow-Origin ne doit pas être * en prod
  3. 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 :

  1. Test : curl -X POST http://localhost:8082/internal/jobs/transcode -d '{}'
  2. Vérifier : 401 Unauthorized sans token
  3. 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 :

  1. Test : wscat -c "ws://localhost:8082/ws" (sans token)
  2. Vérifier : Connexion rejetée
  3. 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 :

  1. Supprimer k8s/production/secrets.yaml du repo
  2. Ajouter à .gitignore : **/secrets.yaml
  3. Documenter : Utiliser Sealed Secrets ou Vault

Plan de validation :

  1. Vérifier : git log --all --full-history -- k8s/production/secrets.yaml
  2. Vérifier : Fichier absent du repo
  3. 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 :

  1. Test : 1000 requêtes rapides depuis même IP
  2. Vérifier : Après 60 req/min, 429 Too Many Requests
  3. 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 :

  1. Test : Révoquer token, vérifier rejeté
  2. Test : Redémarrer serveur, token toujours rejeté (si Redis/DB)
  3. 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 :

  1. Test : GET /stream/../../../etc/passwd?expires=...&sig=...
  2. Vérifier : 403 Forbidden ou 400 Bad Request
  3. 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 :

  1. Test : Requête HTTPS, vérifier header Strict-Transport-Security
  2. 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_id automatique
  • ⚠️ 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 metrics crate (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 001
  • stream_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 : Multiples DashMap / 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)

  1. MOD-P0-004 : Supprimer secrets du repo (30min)

    • Supprimer k8s/production/secrets.yaml
    • Ajouter à .gitignore
    • Documenter Sealed Secrets
  2. MOD-P0-001 : Fix CORS permissif (1h)

    • Require ALLOWED_ORIGINS en prod
    • Test CORS
  3. MOD-P0-002 : Auth endpoint transcodage (1h)

    • Ajouter middleware auth
    • Test 401 sans token
  4. MOD-P0-005 : Vérifier table tracks (2h)

    • Check existence au démarrage
    • Erreur claire si absente
  5. 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)

  1. MOD-P1-006 : Implémenter rate limiting (4h)
  2. MOD-P1-007 : Révocation JWT persistante (4h)

Lot 2 : Sécurité paths + FFmpeg (4h)

  1. MOD-P1-008 : Fix path traversal (2h)
  2. MOD-P1-010 : Timeout FFmpeg (2h)

Lot 3 : WebSocket + HLS (10h)

  1. MOD-P1-011 : Limite connexions WS (2h)
  2. MOD-P1-012 : Pré-génération HLS (6h)
  3. MOD-P1-013 : Tests intégration (2h, début)

Lot 4 : Gestion erreurs (20h)

  1. 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é

  1. Tests WebSocket (4h)

    • Connexion, messages, déconnexion
    • Auth, rate limiting
  2. Tests streaming HTTP (4h)

    • Range requests, signatures, path traversal
  3. Tests transcodage (6h)

    • FFmpeg pipeline, HLS generation, timeouts
  4. 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 :

  1. Corriger P0 (1 jour)
  2. Corriger P1 critiques (1 semaine)
  3. Ajouter tests (3 jours)
  4. Code review sécurité
  5. Déploiement staging
  6. Tests charge
  7. Déploiement production

Fin du rapport d'audit