Articles

Réflexions et découvertes en Cybersécurité
Retour aux articles

De root ? Là ou on va, on n'a pas besoin de root

Ou comment Podman conteneurise sans privilèges sudo

Podman banner

Introduction

Docker a gagné. En quelques années, il est devenu le standard de la conteneurisation. Son CLI, son écosystème, ses concepts sont aujourd'hui la lingua franca de tout développeur qui touche à du déploiement.

Mais Docker traîne une dette architecturale : un daemon central qui tourne en root par défaut. Si Docker propose désormais un Rootless Mode officiel (depuis la version 19.03 dont j'ai appris l'existence pendant la rédaction de cet article), celui-ci reste une couche optionnelle dont la configuration est moins directe que celle de Podman. Podman, à l'inverse, a été pensé dès le départ pour fonctionner sans privilèges et sans daemon de manière native. Podman est la réponse à ce problème. Pas un fork mais une réécriture avec des contraintes de sécurité posées dès le départ. Daemonless, rootless, et presque entièrement compatible avec le CLI Docker.

Cet article détaille pourquoi l'architecture Docker pose problème, comment Podman la résout, et comment la transition se passe en pratique.

Quand tout tourne en root

Docker utilise une architecture client-serveur. Le client Docker communique avec dockerd, qui se charge du travail lourd : construire, exécuter et distribuer les conteneurs. Le client et le daemon peuvent tourner sur le même système, et communiquent via une API REST, sur des sockets UNIX ou une interface réseau. Dockerd est le cœur du réacteur et il tourne avec les droits root dans sa configuration standard.

Les implications sécuritaires sont directes. Si un attaquant parvient à s'échapper d'un conteneur mal configuré via une image malicieuse, une vulnérabilité dans le runtime ou une mauvaise gestion des volumes il tombe dans un processus root. Si quelque chose tourne mal avec le daemon (un bug innocent, un crash, ou dans le pire des cas un exploit de sécurité), le système hôte est potentiellement en danger.

Pour finir, l'auditabilité est délicate. Dans les logs système, les opérations sur les conteneurs apparaissent comme des actions du daemon et non pas de l'utilisateur qui les a initiées. Sur un serveur partagé, comprendre qui a fait quoi devient une épreuve.

Podman, ou l'architecture sans maître

Podman est développé principalement par Red Hat. Son nom est une abréviation de "Pod Manager", référence directe aux pods de Kubernetes. Mais ce qui le distingue fondamentalement de Docker tient en un mot : daemonless.

Concrètement, si je lance podman run monimage, Podman ne délègue pas la requête à un service central. Il crée directement le conteneur comme un processus enfant de mon shell. Le conteneur est donc mon enfant dans l'arbre des processus. Les logs montrent exactement quel utilisateur a lancé quel processus. Sur VPS par exemple ou un server d'entreprise, c'est une information de valeur.

Cette absence de daemon repose sur conmon (Container Monitor), un outil léger qui surveille chaque conteneur. Cette architecture permet à systemd de traiter les conteneurs comme des services standards.

Le rootless, la sécurité by design

Le mode rootless est là où Podman fait vraiment la différence. L'idée : je lance un conteneur, y compris en tant que root à l'intérieur du conteneur, sans aucun privilège root sur ma machine hôte. Comment est-ce possible ? Via une fonctionnalité du noyau Linux : les namespaces.

Quand un utilisateur non privilégié lance Podman, l'outil crée et rejoint un user namespace. Une fois Podman devenu root à l'intérieur de ce user namespace, il est autorisé à monter certains systèmes de fichiers et à configurer le conteneur. Il n'y a pas d'escalade de privilège ici, au-delà des UIDs supplémentaires disponibles pour l'utilisateur.

Le mécanisme concret repose sur deux fichiers : /etc/subuid et /etc/subgid. Podman nécessite que l'utilisateur qui l'exécute dispose d'une plage d'UIDs listée dans ces fichiers. Le paquet shadow-utils les fournit selon les distributions.

Ces fichiers définissent des plages d'UIDs et de GIDs que chaque utilisateur peut allouer à ses conteneurs. Par exemple, l'utilisateur raph peut utiliser les UIDs 100000 à 165535 à l'intérieur de ses conteneurs.

En pratique ça signifie que si mon conteneur tourne en root, alors root dans le conteneur correspond en réalité à mon compte utilisateur sur l'hôte. L'UID/GID 1 dans le conteneur correspond au premier UID/GID de ma plage dans /etc/subuid//etc/subgid. Si je monte un répertoire de l'hôte dans un conteneur en mode rootless et crée un fichier en tant que root dans ce conteneur, ce fichier appartient à mon utilisateur sur l'hôte.

Pour configurer une plage pour votre utilisateur :

BASH
usermod --add-subuids 100000-165535 --add-subgids 100000-165535 alice

Si vous êtes sur un ancien système linux, vérifiez au préalable que kernel.unprivileged_userns_clone est à 1. Si la valeur est à 0, activez-la via sysctl. Le noyau linux-hardened la désactivais par défaut, c'est une couche de sécurité qui empêche les utilisateurs non privilégiés de créer des namespaces.

Avec Podman, les données des conteneurs sont stockées dans le graph root de stockage, dont le chemin par défaut est ~/.local/share/containers/storage. Tout reste dans le home directory de l'utilisateur, sans toucher au système.

Les compromis techniques

Il serait malhonnête de dire que Podman est la solution parfaite. Ce gain de sécurité impose un compromis technique majeur : le réseau. Un utilisateur non privilégié ne peut pas créer d'interfaces réseau virtuelles sur l'hôte. Podman doit donc utiliser des outils comme slirp4netns pour émuler une pile réseau en espace utilisateur.

En pratique, cela se traduit par plusieurs frictions concrètes. Sur le débit pur, slirp4netns introduit une surcharge mesurable, comptez environ 10 à 30% de perte de bande passante selon les benchmarks, ce qui reste acceptable pour la plupart des workloads applicatifs, mais qui peut devenir un facteur bloquant pour du transfert de fichiers massif ou des services très latence-sensitifs. La propagation de l'IP source est également problématique : le conteneur voit souvent l'IP de la passerelle au lieu de l'IP réelle du client distant, ce qui casse les logs d'accès et les règles de rate-limiting basées sur l'IP.

Il existe cependant une alternative plus récente : pasta (via le projet passt), qui remplace slirp4netns dans les versions récentes de Podman (à partir de la 4.4). Pasta adopte une approche différente, il opère au niveau des sockets plutôt qu'en émulant une interface réseau complète, ce qui réduit significativement la surcharge et améliore la propagation de l'IP source. Si vous êtes sur une distribution récente, Podman devrait l'utiliser par défaut ; vérifiez avec :

BASH
podman info | grep networkBackend

Enfin, un point souvent négligé : le partage de conteneurs entre utilisateurs. En mode rootless, les conteneurs d'un utilisateur sont invisibles aux autres, y compris à root. C'est une fonctionnalité de sécurité, mais ça peut surprendre dans un contexte d'administration système où l'on s'attend à pouvoir inspecter les processus de n'importe quel utilisateur avec podman ps.

Docker, c'est toi ? La transition quasi-transparente

La question qui revient systématiquement est la suivante : mes images et mes habitudes Docker vont-elles survivre au passage à Podman ? La réponse courte est oui, et c'est un choix ex professo.

Cette compatibilité totale est possible car les deux outils partagent le même ADN : le format OCI (Open Container Initiative). L'OCI est un standard industriel qui définit comment une image de conteneur doit être construite et exécutée. Une image construite avec Docker est 100% lisible par Podman. L'interface en ligne de commande (CLI) de Podman a été calquée sur celle de Docker. Pour la quasi-totalité des usages, il suffit de remplacer le mot docker par podman.

Le débat de l'alias : Beaucoup d'utilisateurs ajoutent alias docker=podman dans leur fichier .bashrc pour ne pas changer leurs réflexes ou pour faire fonctionner des scripts "legacy".

Personnellement : Je vous suggère plutôt de ne pas utiliser d'alias. L'alias docker=podman est un piège, il masque les spécificités du mode rootless, notamment les permissions de volumes (UID mapping) ou les contextes SELinux (:Z), rendant le débogage d'autant plus complexe.


Conclusion : une mise à jour architecturale

In fine, Podman n'est pas un clone de Docker avec un nom différent. C'est une réponse à une question que Docker n'a jamais vraiment posée : "Pourquoi un outil de gestion de conteneurs a-t-il besoin d'un daemon root permanent ?"

Podman est un excellent outil quand on a besoin d'une sécurité accrue, d'une séparation par UID via les namespaces, et d'une intégration native avec systemd. Ce sont précisément les cas d'usage qui comptent en prod, sur des serveurs partagés, ou dans des pipelines CI où la moindre escalade de privilège est un vecteur d'attaque.

Mais Podman ne s'arrête pas là. Si vous utilisez systemd pour gérer vos services, jetez un œil à Quadlet, intégré nativement depuis Podman 4.4, qui permet de décrire un conteneur comme une unit systemd en quelques lignes, sans passer par podman generate systemd. C'est l'approche recommandée pour faire tourner des conteneurs comme des services de première classe sur une machine Linux.

Si vous travaillez dans un contexte où plusieurs outils coexistent, il vaut aussi la peine de connaître nerdctl, l'alternative CLI de l'écosystème containerd. Podman et nerdctl répondent à des philosophies légèrement différentes, Podman reste plus proche du workflow développeur solo ou en équipe restreinte, nerdctl s'inscrit davantage dans l'orbite Kubernetes. Connaître les deux vous donne une carte plus complète du paysage.

La bonne nouvelle reste inchangée : la compatibilité CLI est quasi totale, les images Docker existantes fonctionnent sans modification, et la mémoire musculaire n'a pas besoin d'être ré-entraînée. Pas de daemon à démarrer. Pas de sudo. Le processus appartient à votre utilisateur. C'est là la promesse de Podman.

Références