165 lines
6.8 KiB
Markdown
165 lines
6.8 KiB
Markdown
\newpage
|
||
|
||
Les espaces de noms -- *namespaces*
|
||
===================================
|
||
|
||
Nous avons vu un certain nombre de fonctionnalités offertes par le noyau Linux
|
||
pour limiter, autoriser ou contraindre différents usages des ressources de
|
||
notre machine.
|
||
|
||
Ces fonctionnalités sont très utiles pour éviter les dénis de service, mais nos
|
||
processus ne sont pas particulièrement isolés du reste du système. On aimerait
|
||
maintenant que nos processus n'aient pas accès à l'ensemble des fichiers, ne
|
||
puissent pas interagir avec les autres processus, avoir leur propre pile
|
||
réseau, ... Voyons maintenant les *namespaces* qui vont nous permettre de faire
|
||
cela.
|
||
|
||
|
||
Présentation des *namespaces*
|
||
-----------------------------
|
||
|
||
Les espaces de noms du noyau, que l'on appelle *namespaces*, permettent de
|
||
dupliquer certaines structures, habituellement considérées uniques pour le
|
||
noyau, dans le but qu'un groupe de processus soit isolé d'autres processus, sur
|
||
certains aspects de l'environnement dans lequel il s'exécute.
|
||
|
||
On en dénombre huit (le dernier ayant été ajouté dans Linux 5.6) : `cgroup`,
|
||
`IPC`, `network`, `mount`, `PID`, `time`, `user` et `UTS`.
|
||
|
||
La notion d'espace de noms est relativement nouvelle et a été intégrée
|
||
progressivement au sein du noyau Linux. Aussi, toutes les structures ne sont
|
||
pas encore *containerisables* : le document fondateur[^NSDOC] parle ainsi
|
||
d'isoler les journaux d'événements ou encore les modules de sécurité (LSM, tels
|
||
que AppArmor, SELinux, Yama, ...).
|
||
|
||
[^NSDOC]: <https://www.kernel.org/doc/ols/2006/ols2006v1-pages-101-112.pdf>
|
||
|
||
Commençons par passer en revue rapidement les différents *namespaces*.
|
||
|
||
#### L'espace de noms `mount` {.unnumbered #mount-ns}
|
||
|
||
Depuis Linux 2.4.19.
|
||
|
||
Cet espace de noms dissocie la liste des points de montage.
|
||
|
||
Chaque processus appartenant à un *namespace mount* différent peut monter,
|
||
démonter et réorganiser à sa guise les points de montage, sans que cela n'ait
|
||
d'impact sur les processus hors de cet espace de noms. Une partition ne sera
|
||
donc pas nécessairement démontée après un appel à `umount(2)`, elle le sera
|
||
lorsqu'elle aura effectivement été démontée de chaque *namespace mount* dans
|
||
lequel elle était montée.
|
||
|
||
Il s'agit d'une version améliorée de nos bons vieux `chroot`, puisqu'il n'est
|
||
plus possible de s'en échapper en remontant l'arborescence.
|
||
|
||
|
||
#### L'espace de noms `UTS` {.unnumbered #uts-ns}
|
||
|
||
Depuis Linux 2.6.19.
|
||
|
||
Cet espace de noms isole le nom de machine et son domaine NIS.
|
||
|
||
|
||
#### L'espace de noms `IPC` {.unnumbered #ipc-ns}
|
||
|
||
Depuis Linux 2.6.19.
|
||
|
||
Cet espace de noms isole les objets IPC et les files de messages POSIX.
|
||
|
||
Une fois le *namespace* attaché à un processus, il ne peut alors plus parler
|
||
qu'avec les autres processus de son espace de noms (lorsque ceux-ci passent par
|
||
l'API IPC du noyau).
|
||
|
||
|
||
#### L'espace de noms `PID` {.unnumbered}
|
||
|
||
Depuis Linux 2.6.24.
|
||
|
||
Cet espace de noms isole la liste des processus et virtualise leurs numéros.
|
||
|
||
Une fois dans un espace, le processus ne voit que le sous-arbre de processus
|
||
également attachés à son espace. Il s'agit d'un sous-ensemble de l'arbre global
|
||
de PID : les processus de tous les PID *namespaces* apparaissent donc dans
|
||
l'arbre initial.
|
||
|
||
Pour chaque nouvel espace de noms de processus, une nouvelle numérotation est
|
||
initiée. Ainsi, le premier processus de cet espace porte le numéro 1 et aura
|
||
les mêmes propriétés que le processus `init` usuel\ ; entre autres, si un
|
||
processus est rendu orphelin dans ce *namespace*, il devient un fils de ce
|
||
processus, et non un fils de l'`init` de l'arbre global.
|
||
|
||
|
||
#### L'espace de nom `network` {.unnumbered}
|
||
|
||
Depuis Linux 2.6.29.
|
||
|
||
Cet espace de noms fournit une isolation pour toutes les ressources associées
|
||
aux réseaux : les interfaces, les piles protocolaires IPv4 et IPv6, les tables
|
||
de routage, règles pare-feu, ports numérotés, etc.
|
||
|
||
Une interface réseau (`eth0`, `wlan0`, ...) ne peut se trouver que dans un seul
|
||
espace de noms à la fois. Il est par contre possible de les déplacer.
|
||
|
||
Lorsque le *namespace* est libéré (généralement lorsque le dernier processus
|
||
attaché à cet espace de noms se termine), les interfaces qui le composent sont
|
||
ramenées dans l'espace initial/racine (et non pas dans l'espace parent, en cas
|
||
d'imbrication).
|
||
|
||
|
||
#### L'espace de noms `user` {.unnumbered}
|
||
|
||
Depuis Linux 3.8.
|
||
|
||
Cet espace de noms isole la liste des utilisateurs, des groupes, leurs
|
||
identifiants, les *capabilities*, la racine et le trousseau de clefs du noyau.
|
||
|
||
La principale caractéristique est que les identifiants d'utilisateur et de
|
||
groupe pour un processus peuvent être différents entre l'intérieur et
|
||
l'extérieur de l'espace de noms. Il est donc possible, alors que l'on est un
|
||
simple utilisateur à l'extérieur du *namespace*, d'avoir l'UID 0 dans le
|
||
conteneur.
|
||
|
||
|
||
#### L'espace de noms `cgroup` {.unnumbered #cgroup-ns}
|
||
|
||
Depuis Linux 4.6.
|
||
|
||
Cet espace de noms filtre l'arborescence des *Control Group* en changeant la
|
||
racine vue par le processus. Au sein d'un *namespace* `cgroup`, la racine vue
|
||
correspond en fait à un sous-groupe de l'arborescence globale.
|
||
|
||
Ainsi, un processus dans un *CGroup* *namespace* ne peut pas voir le contenu
|
||
des sous-groupes parents (pouvant laisser fuiter des informations sur le reste
|
||
du système). Cela peut également permettre de faciliter la migration de
|
||
processus (d'un système à un autre) : l'arborescence des *cgroups* n'a alors
|
||
plus d'importance car le processus ne voit que son groupe.
|
||
|
||
|
||
#### L'espace de noms `time` {.unnumbered #time-ns}
|
||
|
||
Depuis Linux 5.6.
|
||
|
||
Avec cet espace de noms, il n'est pas possible de virtualiser l'heure d'un de
|
||
nos conteneurs (on peut seulement changer le fuseau horaire, puisqu'ils sont
|
||
gérés par la `libc`). Les horloges virtualisées avec ce *namespace* sont les
|
||
compteurs `CLOCK_MONOTONIC` et `CLOCK_BOOTTIME`.
|
||
|
||
Lorsque l'on souhaite mesurer un écoulement de temps, la méthode naïve consiste
|
||
à enregistrer l'heure au départ de notre opération, puis à faire une
|
||
soustraction avec l'heure de fin. Cette technique fonctionne bien, à partir du
|
||
moment où l'on est sûr que l'horloge ne remontera pas dans le temps, parce
|
||
qu'elle se synchronise ou que le changement d'heure été/hiver
|
||
intervient, ... Pour palier ces situations imprévisibles, le noyau expose une
|
||
horloge dite monotone (`CLOCK_MONOTONIC`) : cette horloge démarre à un entier
|
||
abstrait et s'incrèmente chaque seconde qui passe, sans jamais sauter de
|
||
secondes, ni revenir en arrière. C'est une horloge fiable pour calculer des
|
||
intervalles de temps.
|
||
|
||
De la même manière `CLOCK_BOOTTIME` mesure le temps qui s'écoule, mais prend en
|
||
compte les moments où la machine est en veille (alors que `CLOCK_MONOTONIC` ne
|
||
compte que les moments où la machine est en éveil).
|
||
|
||
Étant donné l'usage de ces deux horloges, en cas de migration d'un processus
|
||
d'une machine à une autre, il convient de recopier l'état des horloges
|
||
monotones qu'il utilise, afin que ses calculs ne soient pas chamboulés.
|