Principe de fonctionnement

Contrairement aux machines virtuelles, un conteneur n’est pas une entité isolée du noyau ; il s’agit d’un processus Linux auquel sont appliquées plusieurs restrictions. Le noyau continue d’exécuter ce processus comme n’importe quel autre, ce qui permet de le retrouver dans /proc. Cette réalité implique que les frontières de sécurité d’un conteneur reposent sur les mécanismes du noyau plutôt que sur une hypervision complète.

Architecture du runtime Docker

Docker construit un conteneur à partir de cinq primitives du noyau : les namespaces qui masquent les vues du système de fichiers, des processus et du réseau ; les cgroups qui limitent CPU, mémoire et I/O ; ainsi que divers modules réseau (veth, bridge, NAT) qui isolent le trafic. Les images Docker ne sont pas des artefacts binaires mais des répertoires stockés sur le disque. Lors d’un

docker run -it --rm ubuntu /bin/bash
, Docker télécharge l’image ubuntu, la place dans un répertoire interne, puis invoque chroot ou pivot_root pour redéfinir la racine du processus.

Le composant runc, implémenté en Go, orchestre la création du conteneur. Le runtime Go, naturellement multithread, complique l’appel de syscalls qui exigent un contexte monothread, notamment clone avec des flags de namespace. Pour contourner ce problème, runc charge un module C exécuté avant l’initialisation du runtime Go, garantissant que les syscalls critiques se déroulent dans un thread unique. De plus, Go ne propose pas fork() ; le fork traditionnel ne duplique que le thread principal, ce qui est inadapté à un runtime qui doit créer un processus, appliquer des restrictions, puis exec le binaire cible. runc résout ce manque en relançant /proc/self/exe avec des arguments spécifiques, simulant ainsi le comportement de fork.

Contraintes de privilèges et sécurité

Le démon Docker s’exécute généralement en tant que root afin d’accéder aux API du noyau requérant des privilèges élevés (par ex. manipulation de cgroups ou de namespaces). Cette exigence expose la surface d’attaque du système hôte : toute compromission du démon peut conduire à une escalade de privilèges. Les solutions « rootless » tentent de déléguer ces opérations à des processus non privilégiés, mais elles rencontrent encore des limites, notamment au niveau du réseau où la pile doit être ré‑implémentée en espace utilisateur pour reproduire les fonctionnalités complètes de Docker.

En résumé, Docker repose sur des abstractions du noyau Linux plutôt que sur une couche de virtualisation indépendante. Cette approche offre une légèreté remarquable, mais elle impose des dépendances strictes aux capacités du noyau et aux privilèges du processus daemon, ce qui doit être pris en compte lors de la conception de systèmes sécurisés.