Espaces de noms & Conteneurs · Section 2
unshare(2)
Détache certaines parties du contexte d'exécution du processus appelant — espaces de noms, fs, files — sans forker.
Signature
#include <sched.h>
int unshare(int flags);- flags
- OU bit-à-bit de drapeaux CLONE_*. Chaque drapeau détache la ressource correspondante. CLONE_NEWUSER est spécial : demandable par des appelants non privilégiés (selon la config noyau), et une fois accordé l'appelant a CAP_SYS_ADMIN dans le nouvel espace — débloquant les autres demandes CLONE_NEW*.
Description
unshare() permet à un processus de détacher des ressources sélectionnées de son contexte parent sans passer par clone(). flags est un masque de valeurs CLONE_* : CLONE_NEWUSER crée un nouvel espace utilisateur, CLONE_NEWNET un nouvel espace réseau, CLONE_NEWNS un nouvel espace de montage, CLONE_NEWPID un nouvel espace de PID (effectif pour le *prochain* enfant), CLONE_NEWUTS / CLONE_NEWIPC / CLONE_NEWCGROUP / CLONE_NEWTIME pour les autres, et CLONE_FILES / CLONE_FS / CLONE_SYSVSEM pour les ressources non-espace-de-noms. unshare() est l'inverse de l'héritage : plutôt que « créer un enfant qui ne partage pas X », il dit « cesser de partager X avec quiconque ». C'est la primitive principale de la commande unshare(1) et de la construction de conteneurs rootless.
Numéros par architecture
| Architecture | Numéro | ABI | Point d'entrée |
|---|---|---|---|
| x86 (i386) | 310 | i386 | sys_unshare |
| x64 (x86_64) | 272 | common | sys_unshare |
| ARM64 (aarch64) | 97 | — | sys_unshare |
Historique noyau
Introduit dans Linux 2.6.16.
2.6.16
unshare() a été ajouté en 2.6.16 avec la famille d'espaces de noms. Au départ il supportait seulement CLONE_FILES, CLONE_FS, CLONE_NEWNS, CLONE_SYSVSEM ; les noyaux suivants ont ajouté chaque nouvel espace en drapeau.
3.8
CLONE_NEWUSER est arrivé (Linux 3.8), rendant les conteneurs rootless possibles. Le mécanisme de mapping UID/GID des espaces utilisateur est venu avec ; le motif canonique du conteneur non privilégié est unshare(CLONE_NEWUSER|CLONE_NEWNS|…) puis écriture de /proc/self/uid_map.
4.6
CLONE_NEWCGROUP a été ajouté — un processus peut avoir une vue virtualisée de /proc/self/cgroup, utilisé par les hiérarchies cgroupv2 imbriquées.
seccomp & conteneurs
Docker default profile
Bloqué
Podman default profile
Bloqué
unshare() est BLOQUÉ par défaut dans Docker et Podman car l'autoriser dans un conteneur permet à la charge de créer des espaces imbriqués — fondation des exploits de conteneurs imbriqués et chemin riche en CVE. À activer explicitement seulement pour les conteneurs qui hébergent légitimement des runtimes imbriqués (Docker-in-Docker, Kubernetes-in-Kubernetes). Filtrer pour refuser CLONE_NEWUSER spécifiquement est une alternative plus serrée qui permet d'autres usages de unshare tout en bloquant le chemin d'escalade par espace utilisateur.
libseccomp
// unshare() is NOT on the Docker default allow-list. Explicitly deny.
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(unshare), 0);Exemple strace
$ strace -e unshare unshare -U -r /bin/whoami 2>&1 | head -3
unshare(CLONE_NEWUSER) = 0unshare() dans strace affiche le masque de bits symboliquement. Le motif classique « je veux tester en root » est unshare(CLONE_NEWUSER) suivi d'une écriture dans /proc/self/uid_map — visible directement. Pour les commandes ip netns add on voit unshare(CLONE_NEWNET) puis des bind-mounts vers /run/netns/<nom>.
Sécurité & observabilité
unshare(CLONE_NEWUSER) est le point d'entrée des chaînes d'escalade par espace utilisateur non privilégié — historiquement plusieurs CVE (CVE-2018-18955, CVE-2022-0185, CVE-2022-2588) ont donné root via des constructions d'espace utilisateur. L'atténuation est sysctl kernel.unprivileged_userns_clone=0 sur les hôtes durcis ; Debian / Ubuntu l'activent souvent. Le tracepoint eBPF sys_enter_unshare avec les drapeaux décodés donne une observabilité complète ; une charge conteneur appelant unshare(CLONE_NEWUSER|…) est rare et mérite alerte. De l'intérieur, les readlinks /proc/self/ns/* exposent les numéros d'inode d'espaces actuels — comparer aux espaces attendus du conteneur détecte la dérive.
Erreurs
- EINVAL
- Combinaison de drapeaux invalide, ou drapeau non supporté par ce noyau.
- ENOMEM
- Mémoire noyau insuffisante.
- ENOSPC
- —
- EPERM
- Appelant sans CAP_SYS_ADMIN pour l'un des espaces demandés (tout sauf CLONE_NEWUSER sur les systèmes où l'userns non privilégié est activé). Sur Debian avec kernel.unprivileged_userns_clone=0, même CLONE_NEWUSER échoue en EPERM pour non-root.
- EUSERS
- La profondeur d'espaces utilisateur imbriqués dépasserait MAX_USER_NAMESPACE_LEVEL (32).
Drapeaux
- CLONE_FILES
- 0x00000400
- Détache la table des descripteurs. Après unshare(CLONE_FILES), ouvrir/fermer un fd ici n'affecte plus les frères qui partageaient la table (par ex. via clone(CLONE_FILES)).
- CLONE_FS
- 0x00000200
- —
- CLONE_NEWCGROUP
- 0x02000000
- Nouvel espace cgroup. La vue /proc/self/cgroup du processus est rebasée — utile pour la conteneurisation imbriquée.
- CLONE_NEWIPC
- 0x08000000
- —
- CLONE_NEWNET
- 0x40000000
- Nouvel espace réseau. État réseau vide (pas d'interface sauf lo). Utilisé par les runtimes de conteneurs et ip netns.
- CLONE_NEWNS
- 0x00020000
- Nouvel espace de montage. Les mount() / umount() suivants sont locaux à cet espace. Base des racines MS_PRIVATE et des montages rootless.
- CLONE_NEWPID
- 0x20000000
- Nouvel espace PID. Le prochain enfant créé via fork()/clone() devient PID 1 dans le nouvel espace ; le processus appelant reste dans l'ancien.
- CLONE_NEWTIME
- 0x00000080
- —
- CLONE_NEWUSER
- 0x10000000
- Nouvel espace utilisateur. Disponible aux appelants non privilégiés et fondation des conteneurs rootless. Dans le nouvel userns, l'appelant peut mapper une paire UID/GID et obtenir CAP_SYS_ADMIN, ce qui débloque les autres créations d'espaces.
- CLONE_NEWUTS
- 0x04000000
- —
- CLONE_SYSVSEM
- 0x00040000
- —