Statut : isolation du moteur faite, voir ADR 0028 (§7). Le reste de ce document date de la conception et n'a pas été repris ligne à ligne. Les mesures de type seccomp/AppArmor sont à évaluer : elles ne seront annoncées qu'une fois testées sur la distribution de référence.
Décision associée : ADR 0010 (FFmpeg/ffprobe en processus enfant derrière une interface
Engine).
1. Hypothèse de départ
Tout fichier média est une entrée hostile. FFmpeg et ffprobe parsent des centaines de formats ; des failles de démuxeurs et décodeurs sont publiées régulièrement. L'objectif n'est pas de rendre FFmpeg sûr, mais de limiter ce qu'un processus FFmpeg compromis ou emballé peut faire.
2. Construction des commandes
- Lancement par
exec.CommandContextavec arguments séparés. Jamais desh -c, jamais de concaténation de chaîne. - Les arguments sont produits par l'adaptateur FFmpeg à partir d'un plan typé (codec, débit, résolution, pistes, filtres choisis dans des énumérations). Aucune valeur libre venant d'un client n'atteint la ligne de commande.
- Les chemins d'entrée viennent exclusivement de la couche
storage(§4), jamais d'une requête. - Les sorties sont écrites dans le répertoire de travail du job (§5), avec des noms générés par le serveur.
- Les noms de fichiers d'entrée commençant par
-ou contenant:sont passés sous une forme non ambiguë (chemin absolu, préfixefile:) pour éviter leur interprétation comme option ou protocole. - Option
-nostdin,-hide_banner, sortie de progression lisible par machine (-progress pipe:1), stderr lu en continu sans blocage et tronqué au-delà d'une taille plafond.
3. Protocoles et démuxeurs
- Entrées locales :
-protocol_whitelist file(ajouterpipeuniquement si un flux interne l'exige). Interdit de factohttp,https,tcp,udp,rtmp,concat,subfile,data, etc. - Les formats capables de référencer d'autres fichiers ou URL (playlists HLS/DASH en entrée,
concat,ffconcat, références externes de certains conteneurs) sont refusés en entrée de bibliothèque ou traités par une politique explicite. Liste exacte à établir en phase 2 avec le corpus de test. - ffprobe reçoit les mêmes restrictions que ffmpeg. Fait (lot 2c,
internal/mediaanalysis/ffprobe) : sous Linux et macOS, le serveur ouvre le fichier par la racine confinée de la source et le passe à ffprobe comme descripteur 3 (-protocol_whitelist fd -fd 3 -i fd:). ffprobe ne voit aucun chemin et ne peut rien ouvrir d'autre : nifile:, ni réseau, ni listeconcat, ni lien hors source (vérifié par un test avec un vrai lien vers l'extérieur). Sous Windows, où Go ne transmet pas de descripteur supplémentaire, un chemin est passé après vérification que sa cible réelle reste dans la source, avec-protocol_whitelist file. Avant FFmpeg 6.0 (Debian 12 a la 5.1), le protocolefdn'existe pas : le moteur rouvre le descripteur parfile:/dev/fd/3, avec-format_whitelistlimité aux formats de fichiers multimédias. Les démultiplexeurs qui ouvrent d'autres fichiers (hls,dash,concat,vobsub…) en sont exclus, et une liste de lecture piégée est refusée (essayé). Sous Landlock, le lanceur ne permet de relire que ce fichier (essayé sur un noyau 6.18). Délai de 60 s par fichier, sorties bornées (16 Mio sur stdout, 8 Kio sur stderr), arguments jamais passés par un shell. - Les futures sources TV/IPTV, qui exigent des protocoles réseau, utiliseront un profil d'exécution séparé et explicitement autorisé, jamais le profil des bibliothèques.
4. Chemins validés par la couche storage
- Chaque source de bibliothèque déclare une racine autorisée. Seule la couche
storagerésout un identifiant de fichier en chemin. - Normalisation, refus des composants
.., refus de tout chemin résolu hors racine. - Liens symboliques : suivis uniquement si leur cible reste dans une racine autorisée ; sinon le fichier est ignoré et signalé dans les diagnostics admin.
os.Root: adopté (lot 2a,internal/storage). Toute lecture d'un fichier de bibliothèque passe par une racine confinée :.., chemins absolus et liens symboliques qui sortent de la source sont refusés par l'interface du système. C'est testé sous Linux avec un vrai lien vers l'extérieur. Les liens internes à la source restent suivis. Les racines elles-mêmes sont validées : chemin absolu, hors zones système (/etc,/proc,/sys,/dev,/var/lib… ;%SystemRoot%sous Windows), sans chevauchement avecDATA_DIR(base et clé) ni avec une autre source.- Les montages de médias sont en lecture seule (recommandé côté OS et imposé dans les exemples systemd/Docker).
5. Répertoires de travail
- Un répertoire unique par job :
<PROTOFLIX_TRANSCODE_DIR>/<job-id>/, créé en0700, propriété de l'utilisateur de service. PROTOFLIX_TRANSCODE_DIRdoit être sur un volume distinct ou soumis à quota (§6) ; jamais dans le répertoire de la base ni dans une bibliothèque.- Suppression à la fin du job, à l'annulation, et par un nettoyage au démarrage (répertoires orphelins après crash) et périodique (TTL).
- Le serveur sert les segments en lisant ce répertoire via des noms qu'il a générés, jamais via un nom fourni par le client.
6. Limites de ressources
| Ressource | Mécanisme prévu (Linux) |
|---|---|
| Durée | context avec échéance par job ; watchdog qui tue les processus sans progression pendant N secondes |
| Mémoire | cgroup v2 : MemoryMax= sur l'unité systemd du service (ou d'un sous-scope dédié aux transcodages) ; Docker : mem_limit |
| CPU | CPUQuota= / CPUWeight= ; priorité nice plus basse pour les jobs de fond que pour la lecture interactive |
| Processus | TasksMax= ; FFmpeg -threads borné par le Resource Manager |
| Fichiers et taille de sortie | LimitNOFILE= ; quota disque du répertoire de transcodage et plafond par job |
| Entrées excessives | Rejet avant lancement : résolution, nombre de pistes, durée, débit au-delà de plafonds configurables (valeurs issues de l'analyse ffprobe) |
| Concurrence | Resource Manager (ADR / brief §7.4) : nombre de transcodages simultanés configurable, rejet ou dégradation contrôlée plutôt que saturation |
L'option la plus robuste étudiée : lancer chaque transcodage dans un scope systemd transitoire (systemd-run --scope ou API D-Bus) avec ses propres limites. Coût de complexité et disponibilité hors systemd (Docker) à évaluer ; à défaut, limites globales du service + setrlimit sur le processus enfant.
7. Compte et privilèges
- Le service tourne sous un utilisateur système dédié
protoflix, jamais root. - FFmpeg hérite de cet utilisateur. Option étudiée : un second utilisateur
protoflix-mediaencore plus restreint pour les processus enfants (lecture seule des médias, écriture du seul répertoire de transcodage), au prix d'une gestion des permissions plus complexe. - Accès GPU : appartenance aux groupes
render/videouniquement si l'accélération est activée. NoNewPrivileges=yes,PrivateTmp=yes,ProtectSystem=strict,ProtectHome=yes,ReadOnlyPaths=pour les bibliothèques,ReadWritePaths=limité aux répertoires de données (voirdocs/operations/deployment.md). Compatibilité de chaque option avec l'accès GPU à tester.
8. Seccomp, AppArmor, isolation supplémentaire
À évaluer en phase 6, sans engagement :
SystemCallFilter=@system-servicesur l'unité systemd, avec liste d'exceptions mesurée (les pilotes GPU utilisent desioctlspécifiques).- Profil AppArmor (Debian/Ubuntu) pour FFmpeg : lecture des racines de bibliothèques, écriture du répertoire de transcodage, accès
/dev/driou/dev/nvidia*. - Isolation réseau des processus FFmpeg (
PrivateNetwork=sur un scope dédié, ou espace de noms réseau) : une défense forte contre l'exfiltration, à condition que l'IPTV utilise un profil séparé.
Critère d'adoption : la mesure ne casse ni l'accélération matérielle validée ni les performances au-delà d'un seuil mesuré.
9. Fichiers hostiles : comportement attendu
- Échec d'analyse ou de transcodage ⇒ erreur classée (
file_corrupt,unsupported_format,resource_limit,engine_crash), visible dans les diagnostics admin, sans chemin ni sortie brute pour les non-admins. - Un fichier qui fait planter ffprobe de façon répétée est mis en quarantaine logique (plus de nouvelles tentatives automatiques) jusqu'à action admin ou changement du fichier/de la version de FFmpeg.
- État au lot 2c : un seul échec suffit. Le fichier est enregistré
unreadableavec le message de ffprobe, et n'est plus analysé tant que son contenu ne change pas ou que la version du normaliseur (ParserVersion) n'augmente pas. Une nouvelle version de FFmpeg ne relance pas encore ces fichiers ; l'action admin non plus (à faire avec la vue des diagnostics).
- État au lot 2c : un seul échec suffit. Le fichier est enregistré
- Pas de boucle de retries infinie : nombre de tentatives borné par le système de jobs.
10. Maintenance
- Version et configuration de compilation de FFmpeg enregistrées et affichées dans les diagnostics.
- Suivi des avis de sécurité FFmpeg ; mise à jour du binaire distribué dans un délai défini par la politique de publication (à fixer en phase 6).
- Licences et options
--enable-gpl/--enable-nonfreeinventoriées (SBOM), voirdocs/media/.
Source : docs/security/transcoder-hardening.md, dans le dépôt de ProtoFlix.