Contexte et objectifs
Lizzie Bradford a publié un prototype de conteneur Linux d'environ 500 lignes de code (plus 70 lignes après révision). L’objectif est d’identifier le jeu minimal de restrictions nécessaire pour exécuter du code non‑fiable, tout en illustrant les interactions entre namespaces, capabilities, cgroups, setrlimit, seccomp et user namespaces. Le programme s’appuie sur le noyau 4.7.10.201610222037‑1‑grsec exécuté sur une architecture x86_64.
Mécanismes de confinement
Le conteneur utilise plusieurs mécanismes du noyau Linux :
Namespaces isolent les vues du système (PID, mount, UTS, réseau, etc.) en créant des ensembles distincts d’objets kernel. Capabilities restreignent les actions que l’UID 0 peut réaliser ; le code supprime les capacités non essentielles en configurant les ensembles bounding et inheritable. Cgroups limitent la consommation de mémoire, CPU, I/O et nombre de processus via le pseudo‑système de fichiers /sys/fs/cgroup. setrlimit impose des limites de ressources classiques (ex. RLIMIT_NOFILE) qui restent utiles lorsque cgroups ne couvrent pas certains paramètres. Enfin, seccomp‑bpf filtre les appels système autorisés, bloquant les fonctions potentiellement dangereuses comme mount ou ptrace.
Implémentation et code
Le fichier contained.c compile avec la ligne suivante :
#define _GNU_SOURCE
#include <seccomp.h>
…
int main(int argc, char **argv) { … }Le programme parse les options -c (commande), -m (répertoire racine) et -u (UID). Après validation du noyau, il crée un user namespace (si disponible), écrit les mappings /proc/<pid>/uid_map et gid_map, puis effectue les étapes suivantes :
=> setting cgroups...memory...cpu...pids...blkio...done.
=> setting rlimit...done.
=> remounting everything with MS_PRIVATE...remounted.
=> dropping capabilities...bounding...inheritable...done.
=> filtering syscalls...done.Chaque étape repose sur un appel système explicite : mount avec MS_PRIVATE pour éviter les propagations, prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, …) pour le filtrage, et setns pour rejoindre les namespaces créés.
Analyse de sécurité et limites
Le prototype montre que, même avec un code très compact, il est possible d’appliquer l’ensemble des primitives de confinement modernes. Cependant, plusieurs contraintes subsistent :
• User namespaces sont désactivés par défaut dans de nombreuses distributions ; leur activation nécessite un noyau compilé avec CONFIG_USER_NS et expose des vulnérabilités d’escalade de privilèges documentées (ex. CVE‑2019‑5736). L’auteur note que les bugs restent fréquents, même avec les correctifs les plus récents.
• Le filtrage seccomp repose sur une liste noire ; une omission (par exemple clone avec des flags non‑standard) peut réintroduire des vecteurs d’attaque. Une approche blanche, bien que plus sûre, augmente la complexité du code.
• Cgroups v1 est utilisé dans l’exemple ; la migration vers cgroups v2 offrirait un contrôle unifié mais nécessite des ajustements de syntaxe (memory.max, cpu.max).
• Le programme ne gère pas le réseau (network namespace est mentionné mais non configuré), ce qui laisse une surface d’exposition si le conteneur accède à des sockets externes.
En résumé, le projet démontre que 500 lignes suffisent pour mettre en place un environnement isolé, mais la robustesse dépend de la mise à jour du noyau, de la configuration stricte des listes blanches seccomp et de l’activation prudente des user namespaces.