sandbox_architecture.tex 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. Dans la suite, nous allons considérer que chaque processus représente une zone
  2. de privilège, définissant ainsi ses accès et/ou permissions. Je vais alors
  3. décrire l'architecture que j'ai conçue pour organiser ces processus et garder
  4. une exécution aussi proche que possible que dans l'approche multithread.
  5. \section{La notion d'étage}
  6. L'architecture que nous allons construire se place contextuellement dans celle
  7. de VLC. Elle se base fortement sur la réalisation d'un pipeline de traitement
  8. dynamique, créé au fur et à mesure de la lecture des flux multimédia.
  9. Les différents éléments de ce pipeline seront créés éventuellement dans la même
  10. zone de privilège, éventuellement dans une zone de privilège déjà existante ou
  11. même donnera lieu à la création d'une nouvelle zone de privilège. Pour désigner
  12. ce concept du côté de l'API publique de la sandbox, on crée la notion d'étage,
  13. ou \og{}stages\fg{}, qui permet de ne pas forcément indiquer comment sera créée la
  14. partie du pipeline demandée par le client.
  15. L'étage représente donc une abstraction utilisée par la partie \og{}VLC\fg{} mais
  16. réellement manipulée par la partie \og{}sandbox\fg{} du processus en question.
  17. \section{Le modèle broker}
  18. Dans ce modèle, on considère un processus privilégié qu'on appelle broker, qui
  19. va contrôler tous les échanges entre les différents processus qu'on appelle
  20. workers, mais également leur cycle de vie et leurs permissions et accès.
  21. %TODO: schéma broker-worker
  22. Le broker est donc en charge de faire le routage des différents messages, de
  23. transmettre les ressources et faire la vérification d'accès. Il est également
  24. utile pour vérifier que les processus workers sont encore en vie et signaler les
  25. erreurs.
  26. % TODO: expliquer IPC
  27. Les avantages du modèle broker sont qu'il est généralement le modèle le plus
  28. simple à implémenter, et n'a pas besoin de fonctionnalité particulière du point
  29. de vue du système, à part pour créer les communications inter-processus (IPC).
  30. Nous pouvons même simuler toutes les problématiques d'accès par des jetons
  31. d'accès représenté par une certaine IPC, que le broker associera à un accès réel
  32. et reproduira les actions demandées par le worker. Il est également bien plus
  33. simple à comprendre, à développer, à isoler et à suivre étant donné que tous les
  34. échanges passent par le broker avant d'être transmis au bon processus. Ce modèle
  35. constitue ainsi un cadre privilégié pour développer la sandbox et l'amener sur
  36. tous les systèmes, à condition de garder en tête les contraintes des autres
  37. modèles.
  38. Le processus broker sera initialisé dans le processus principal et sera donc
  39. père de tous les autres processus de la sandbox. Cela permettra d'utiliser des
  40. techniques comme \texttt{ptrace} sous Linux ou de pouvoir créer des objets
  41. destinés aux processus fils et de leur associer des droits d'accès sous Windows
  42. sans avoir à recourir à des droits de superutilisateur.
  43. \section{Le modèle orchestrateur}
  44. Dans ce modèle, on considère un processus privilégié qu'on appelle
  45. orchestrateur. Celui-ci n'est plus au centre des communications inter-processus
  46. mais permet d'initialiser les connexions entre deux workers qui vont ensuite
  47. discuter directement. On doit donc s'attendre à de meilleures performances étant
  48. donné qu'on diminue les changements de contexte et appels systèmes. On garde cet
  49. \og{}orchestrateur\fg{} pour initialiser chaque nouvel étage mais les modules peuvent
  50. fonctionner indépendamment après, du moment que tous les éléments qui
  51. sont liés à l'étage ont été créés.
  52. Ce modèle nécessite la possibilité d'initialiser une IPC entre deux workers qui
  53. doivent communiquer, mais potentiellement aussi mettre en place une autre IPC
  54. plus efficace pour les transferts de données du pipeline, commme des mémoire
  55. partagées. Il faut également que dans ce modèle, les workers ne puissent pas
  56. récupérer les droits des autres workers.
  57. C'est grâce à ce modèle qu'on peut mettre en valeur la puissance d'expression et
  58. de contrôle des jetons d'accès, car aucun contrôle de l'orchestrateur n'est
  59. nécessaire pour appliquer une réduction des privilèges et définir précisément
  60. les accès disponibles dans chaque partie du pipeline par conception plutôt que
  61. par des règles et contrôles d'accès.
  62. %TODO: schéma orchestrateur-worker
  63. \section{Les processus worker}
  64. %TODO: décrire rôle du processus worker
  65. Dans les deux modèles précédents, on a utilisé des processus workers qui vont
  66. avoir le même rôle, mais éventuellement des implémentations différentes.
  67. Chaque worker est équipé d'un thread jouant le rôle de boucle événementielle et
  68. récupérant les messages soit de tous les autres modules connectés, soit
  69. uniquement depuis le broker selon le modèle utilisé. La boucle événementielle
  70. sera décrite plus tard.
  71. Les workers peuvent démarrer de plusieurs façons. Soit un nouveau processus est
  72. créé en étant configuré comme un worker, avec des IPC correctes déjà attribuées.
  73. Soit le processus est créé depuis un fork et le thread worker peut démarrer tout
  74. de suite en ayant connaissance de son IPC vers l'orchestrateur ou le broker. La
  75. seconde option peut notamment apparaître lorsqu'un processus Zygote est utilisé.
  76. \section{La notion de processus Zygote}
  77. Afin d'optimiser le démarrage de nouveaux processus, beaucoup de systèmes
  78. utilisent un processus spécial, appelé \og{}Zygote\fg{}. Ce processus est responsable de
  79. créer les nouveaux processus et permet un démarrage plus rapide en
  80. préchargeant toutes les bibliothèques et préparant toutes les initialisations
  81. au préalable.
  82. % TODO expliquer et détailler ASLR et android
  83. Les nouveaux processus sont alors créés à partir d'un appel système
  84. \inltype{fork} qui est moins coûteux que la création complète d'un processus
  85. depuis rien. Mais n'est pas officiellement disponible sous Windows,
  86. officieusement disponible sur les versions Windows 10 via les détails internes
  87. liés à l'implémentation des pico-processus, et ne permet pas de pouvoir
  88. efficacement faire de l'ASLR entre les différents processus de l'application. Ce
  89. dernier point existe notamment sur Android, même si des mitigations existent.
  90. Le processus Zygote permet également d'efficacement cloisonner les descripteurs
  91. de fichier à hériter. Mais cela peut être préféré à une méthode plus
  92. systématique de libération de tous les descripteurs associés au processus si
  93. l'on ne souhaite pas utiliser ce processus pour des raisons de performances
  94. également.
  95. Néanmoins, l'autre partie très intéressante du Zygote concerne le débogage de la
  96. sandbox. Sous Linux, \texttt{gdb} n'est capable de s'accrocher qu'à un seul
  97. processus à la fois. Il faut alors choisir avec \texttt{set follow-fork-mode} si
  98. l'on veut déboguer le processus parent ou bien le processus enfant lors d'un
  99. appel à \inltype{fork}. Dans l'architecture que j'ai construite, nous réalisons
  100. les opérations de façon synchrone, même entre deux processus, ce qui permettra
  101. d'attacher des débogueurs au processus Zygote et basculer directement sur le
  102. processus enfant à chaque création de nouveau processus.
  103. \section{Démarrage de la sandbox}
  104. Du côté de l'application cliente qui utilise libvlc et doit être sandboxée, il
  105. faut pouvoir gérer les deux cas de création de processus. Je propose alors une
  106. API inspirée des travaux existants de Guillaume Fournier sur la sandbox seccomp,
  107. mais ne dépendant pas directement des objets libvlc et permettant plus
  108. d'évolution sur les paramètres passables au démarrage de la sandbox.
  109. \begin{code}{c}{API de sandboxing}
  110. vlc_sandbox_t *vlc_sandbox_Init( struct vlc_sandbox_cfg *cfg );
  111. VLC_API int vlc_sandbox_Start( vlc_sandbox_t *sandbox );
  112. VLC_API void vlc_sandbox_Close( vlc_sandbox_t *sandbox );
  113. VLC_API bool vlc_sandbox_ShouldExit( vlc_sandbox_t *sandbox );
  114. /* donne la valeur de retour pour le programme */
  115. VLC_API int vlc_sandbox_Exit( vlc_sandbox_t *sandbox );
  116. \end{code}
  117. Le client peut alors initialiser la sandbox avec le code qui suit. La structure
  118. \inltype{vlc_sandbox_cfg} est prévue à terme pour transmettre l'ensemble des
  119. politiques à appliquer aux différentes zones de privilèges, les paramètres
  120. de création des nouveaux objets dans la sandbox et tout paramètre de sécurité
  121. important.
  122. \begin{code}{c}{Démarrage de la sandbox}
  123. /* count et args contiennent les arguments modifiés pour le démarrage */
  124. vlc_sandbox_t *sandbox = vlc_sandbox_Init( &(struct vlc_sandbox_cfg) {
  125. .argc = count,
  126. .argv = args,
  127. /* point d'entrée dans la sandbox */
  128. .bootstrap = bootstrap_app,
  129. .create_vlc = libvlc_new,
  130. .delete_vlc = libvlc_release,
  131. });
  132. vlc_sandbox_Start( sandbox );
  133. /* À partir d'ici on fonctionne dans la sandbox et ce processus est devenu le
  134. * broker / orchestrateur. Quand le broker termine, libérer les ressources */
  135. return vlc_sandbox_Close( sandbox );
  136. \end{code}
  137. Dans le cas de la création de worker par fork, la boucle événementielle des
  138. workers est directement appelée. Dans le cas de la création de nouveaux
  139. processus pour chaque worker, \inltype{vlc_sandbox_Init} sera rappelé mais
  140. détectera dans les arguments de la ligne de commande qu'il s'agit d'un worker et
  141. initialisera ses paramètres correctement, puis \inltype{vlc_sandbox_Start}
  142. démarrera la boucle événementielle du worker. On a donc bien une API qui peut
  143. utiliser les deux modes de démarrage des processus.