
Secure code execution
CodeJail gère l'exécution de code non fiable dans des sandboxes sécurisés. Il est conçu principalement pour l'exécution Python, mais peut également être utilisé pour d'autres langages.
La sécurité est appliquée avec AppArmor. Si votre système d'exploitation ne prend pas en charge AppArmor, ou si le profil AppArmor n'est pas défini et configuré correctement, alors CodeJail ne protégera pas l'exécution.
CodeJail est conçu pour être configurable, et se configurera automatiquement pour l'exécution Python si vous l'installez correctement.
Un sandbox CodeJail se compose de plusieurs éléments :
#) Environnement sandbox. Pour une configuration Python, il s'agirait de Python et des paquets de base associés sous forme de virtualenv. Ceci est désigné tout au long de ce document par . Cet environnement est en lecture seule et partagé entre les instanciations de sandbox.
Le code sandboxé a également accès aux bibliothèques système dans la mesure où le profil AppArmor le permet.
#) Répertoire d'exécution du sandbox. Il s'agit d'un répertoire éphémère en lecture seule nommé
comme /tmp/codejail-XXXXXXXX contenant le code soumis
(./jailed_code), des fichiers supplémentaires facultatifs, et un répertoire temporaire
inscriptible (./tmp) que le code soumis peut utiliser comme espace de travail.
Le code soumis est généralement le code fourni par l'étudiant pour être
testé sur le serveur, et les fichiers supplémentaires sont généralement un
python_lib.zip contenant des bibliothèques de notation ou utilitaires.
Pour fonctionner, CodeJail nécessite deux comptes utilisateur. Le premier compte est le compte
principal sous lequel le code s'exécute, qui a les droits de créer des
sandboxes. Ce compte sera désigné par <SANDBOX_CALLER>. Le
second compte est celui sous lequel le sandbox s'exécute. Il s'agit
généralement du compte sandbox.
Cette bibliothèque est actuellement testée pour fonctionner avec les versions suivantes
Python :
Ubuntu :
(Notez que la version de Python utilisée dans le sandbox peut être différente de la version utilisée pour la bibliothèque elle-même.)
Ces instructions détaillent comment configurer votre système d'exploitation afin que
CodeJail puisse exécuter du code Python en toute sécurité. Cependant, il est également possible de définir
codejail.safe_exec.ALWAYS_BE_UNSAFE = True et d'exécuter directement le code Python soumis
sur la machine, sans aucune sécurité. Cela peut convenir aux
machines de développeurs qui ne se préoccupent pas de la sécurité, et permet de tester
une intégration avec l'API de CodeJail. Il ne doit toutefois pas être utilisé si une entrée provient
de sources non fiables. N'utilisez pas cette option dans les systèmes de production.
Pour sécuriser l'exécution Python, vous allez créer un nouveau virtualenv. Cela signifie que vous en aurez deux : le virtualenv principal pour votre projet, et le nouveau pour le code Python sandboxé.
Choisissez un emplacement pour le nouveau virtualenv, appelez-le . Il sera
automatiquement détecté et utilisé si vous le placez juste à côté de votre
virtualenv existant, avec -sandbox ajouté. Ainsi, si votre virtualenv existant se trouve dans
/home/chris/ve/myproj, faites en sorte que soit /home/chris/ve/myproj-sandbox.
L'utilisateur qui exécute le LMS est <SANDBOX_CALLER>, par exemple vous sur
votre machine de développement, ou www-data sur un serveur.
Les autres détails ici dépendent de votre configuration :
Créez le nouveau virtualenv avec --copies afin de disposer d'un exécutable Python distinct à restreindre::
$ sudo python3.12 -m venv --copies
Par défaut, le virtualenv ne ferait que créer un lien symbolique vers le Python système, et la configuration par défaut d'AppArmor sur certains systèmes d'exploitation peut empêcher le confinement de s'appliquer à celui-ci.
(Facultatif) Si vous avez des paquets particuliers que vous souhaitez rendre disponibles pour votre code sandboxé, installez-les en activant l'environnement virtuel sandbox, et en utilisant pip pour les installer::
$ /bin/pip install -r requirements/sandbox.txt
Ajoutez un utilisateur sandbox::
$ sudo addgroup sandbox $ sudo adduser --disabled-login sandbox --ingroup sandbox
Autorisez le serveur web à exécuter le Python sandboxé en tant que sandbox. Créez le fichier
/etc/sudoers.d/01-sandbox::
$ sudo visudo -f /etc/sudoers.d/01-sandbox
<SANDBOX_CALLER> ALL=(sandbox) SETENV:NOPASSWD:/bin/python <SANDBOX_CALLER> ALL=(sandbox) SETENV:NOPASSWD:/usr/bin/find <SANDBOX_CALLER> ALL=(ALL) NOPASSWD:/usr/bin/pkill
(Notez que le binaire find peut exécuter du code arbitraire, ce fichier sudoers n'est donc pas sûr pour des usages autres que CodeJail.)
Modifiez un profil AppArmor. Il s'agit d'un fichier texte spécifiant les limites sur
l'exécutable Python sandboxé. Le fichier doit se trouver dans /etc/apparmor.d et doit
être nommé d'après l'exécutable, les barres obliques étant remplacées par des points. Par
exemple, si votre Python sandboxé se trouve dans /home/chris/ve/myproj-sandbox/bin/python,
alors votre profil AppArmor doit être /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python.
Si votre CodeJail est correctement configuré pour utiliser safe_exec, essayez ces commandes dans votre terminal Python::
import codejail.jail_code
codejail.jail_code.configure('python', '<SANDENV>/bin/python', user='sandbox')
import codejail.safe_exec
jailed_globals = {}
codejail.safe_exec.safe_exec("output=open('/etc/passwd').read()", jailed_globals)
print(jailed_globals) # should be unreachable if codejail is working properly
Cela devrait échouer avec une exception.
Si vous devez modifier les paquets installés dans le virtualenv de votre sandbox, vous devrez désactiver AppArmor, car votre Python sandboxé n'a pas les droits pour modifier les fichiers de son répertoire site-packages.
Désactivez AppArmor pour votre sandbox::
$ sudo apt-get install apparmor-utils # if you haven't already $ sudo aa-complain /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python
Installez ou modifiez les paquets installés::
$ pip install -r requirements/sandbox.txt
Réactivez AppArmor pour votre sandbox::
$ sudo aa-enforce /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python
Pour exécuter les tests, vous devez effectuer les étapes d'installation standard. Ensuite, vous devez définir les variables d'environnement suivantes::
$ export CODEJAIL_TEST_USER=<owner of sandbox (usually 'sandbox')>
$ export CODEJAIL_TEST_VENV=<SANDENV>
Exécutez les tests avec le Makefile::
$ make tests
Plusieurs tests de proxy sont ignorés si le mode proxy n'est pas configuré.
CodeJail est suffisamment polyvalent pour être utilisé dans divers projets afin d'exécuter du code non fiable. Il fournit deux couches :
jail_code.py offre une exécution sécurisée des sous-processus. Pour ce faire,
il exécute le programme dans un sous-processus géré par AppArmor.
safe_exec.py offre un traitement spécialisé de l'exécution Python, en utilisant
jail_code pour fournir la sémantique de l'instruction exec de Python.
CodeJail exécute les programmes sous AppArmor. AppArmor est une fonctionnalité fournie par le système d'exploitation pour limiter les ressources auxquelles les programmes peuvent accéder. Pour exécuter du code Python avec un accès limité aux ressources, nous créons un nouveau virtualenv, puis nous désignons cet exécutable Python dans un profil AppArmor, et restreignons les ressources dans ce profil. CodeJail exécutera le programme Python fourni avec cet exécutable, et AppArmor limitera automatiquement les ressources auxquelles il peut accéder. CodeJail utilise également setrlimit pour limiter la quantité de temps CPU et/ou de mémoire disponible pour le processus.
codejail.jail_code prend un programme à exécuter, des fichiers à copier dans son
environnement, des arguments de ligne de commande et un flux stdin. Il crée un
répertoire temporaire, crée ou copie les fichiers nécessaires, lance un sous-processus pour
exécuter le code, et renvoie la sortie et le statut de sortie du processus.
codejail.safe_exec émule l'instruction exec de Python. Il prend un bloc de
code Python, et l'exécute en utilisant jail_code, modifiant le dictionnaire des globals par
effet de bord. safe_exec procède en sérialisant les globals vers et depuis
le sous-processus au format JSON.
Si codejail ou AppArmor n'est pas configuré correctement, codejail peut par défaut exécuter le code de manière non sécurisée (sans sandbox). Il n'est pas sécurisé par défaut. Les projets intégrant codejail devraient envisager d'inclure une suite de tests d'exécution qui vérifie le confinement correct au démarrage avant d'accepter des entrées non fiables.
L'isolation du sandbox est obtenue via le confinement AppArmor. Codejail facilite cela, mais ne peut pas isoler l'exécution sans l'utilisation d'AppArmor.
Les limites de ressources ne peuvent être contraintes qu'à l'aide des mécanismes que la rlimit de Linux met à disposition. Quelques lacunes notables :
FSIZE de rlimit puisse limiter la taille de n'importe quel fichier qu'un
processus peut créer, et limiter le nombre de fichiers qu'il a ouverts à un
moment donné, elle ne peut pas limiter le nombre total de fichiers écrits, et donc
ne peut pas limiter le nombre total d'octets écrits dans tous les fichiers.
Une atténuation partielle consiste à contraindre le temps d'exécution maximal. (De toute façon, tous les fichiers
écrits dans le sandbox seront supprimés à la fin de l'exécution.)NPROC contraint la capacité du processus courant à
créer de nouveaux threads et processus, mais le compteur d'utilisation (combien de processus
existent déjà) est la somme sur tous les processus ayant le même UID, même dans
d'autres conteneurs sur le même hôte où l'UID peut être mappé à un nom d'utilisateur
différent. Cette contrainte s'applique également à l'utilisateur de l'application en raison de la manière dont
les rlimits sont appliquées. Même si des UIDs sont choisis pour ne pas être utilisés par d'autres
logiciels sur l'hôte, plusieurs processus sandbox codejail sur le même hôte
partageront ce pool d'utilisation et peuvent réduire mutuellement leur capacité à créer
des processus. Dans cette situation, NPROC devra être défini plus haut qu'il
ne le serait pour une instance codejail unique traitant une seule requête à la fois.Les sandbox ne sont pas fortement isolés les uns des autres. Avec une configuration correcte, le code non fiable ne devrait pas pouvoir découvrir d'autres exécutions de code en cours, mais si cette hypothèse est violée, un sandbox pourrait théoriquement interférer avec un autre.
Veuillez ne pas signaler les problèmes de sécurité publiquement. Veuillez envoyer un e-mail à [email protected].
Voir l'exemple de profil dans apparmor-profiles/. Le profil doit être
personnalisé pour correspondre à l'emplacement de votre sandbox.
Analysez les profils::
$ sudo apparmor_parser --replace --warn=all --warn=no-debug-cache --Werror <APPARMOR_FILE>
Réactivez le virtualenv principal de votre projet.
Désactivez l'utilisation de PAM pour définir les rlimits::
sed -i '/pam_limits.so/d' /etc/pam.d/sudo