Tuto 4 ready

This commit is contained in:
nemunaire 2021-10-31 20:51:17 +01:00
parent fb994050db
commit c960136430
18 changed files with 536 additions and 340 deletions

Binary file not shown.

View File

@ -1,6 +1,6 @@
include ../pandoc-opts.mk include ../pandoc-opts.mk
SOURCES_TUTO = tutorial.md setup.md mount.md namespaces.md cmpns.md docker-exec.md networkns.md pidns.md mountns.md userns.md rendu.md SOURCES_TUTO = tutorial.md mount.md intro.md setup.md howto.md setns.md setns-samples.md cmpns.md lifetime.md namespaces_more.md networkns.md pidns.md mountns.md userns.md docker-exec.md rendu.md
all: tutorial.pdf all: tutorial.pdf

View File

@ -1,4 +1,6 @@
## Exercice : comparaison de *namespace* \newpage
### Exercice : comparaison de *namespace* -- `cmpns.sh`
Les *namespaces* d'un programme sont exposés sous forme de liens symboliques Les *namespaces* d'un programme sont exposés sous forme de liens symboliques
dans le répertoire `/proc/<PID>/ns/`. dans le répertoire `/proc/<PID>/ns/`.
@ -7,10 +9,12 @@ Deux programmes qui partagent un même *namespace* auront un lien vers la même
structure de données. structure de données.
Écrivons un script ou un programme, `cmpns`, permettant de déterminer si deux Écrivons un script ou un programme, `cmpns`, permettant de déterminer si deux
programmes s'exécutent dans les mêmes *namespaces*. programmes s'exécutent dans les mêmes *namespaces*. On ignorera les
*namespace*s `*_for_children`, car ils ne font pas partie du cycle d'exécution
que l'on cherche à comparer.
### Exemples {.unnumbered} #### Exemples {.unnumbered}
<div lang="en-US"> <div lang="en-US">
``` ```
@ -20,6 +24,7 @@ programmes s'exécutent dans les mêmes *namespaces*.
- mnt: differ - mnt: differ
- net: differ - net: differ
- pid: differ - pid: differ
- time: same
- user: same - user: same
- uts: same - uts: same
``` ```
@ -33,6 +38,7 @@ programmes s'exécutent dans les mêmes *namespaces*.
- mnt: same - mnt: same
- net: same - net: same
- pid: same - pid: same
- time: same
- user: same - user: same
- uts: same - uts: same
``` ```
@ -52,6 +58,7 @@ Et pourquoi pas :
- mnt: differ - mnt: differ
- net: same - net: same
- pid: same - pid: same
- time: same
- user: same - user: same
- uts: same - uts: same
``` ```

View File

@ -1,3 +1,5 @@
\newpage
Exercice : `docker exec` Exercice : `docker exec`
------------------------ ------------------------

View File

@ -1,142 +1,28 @@
\newpage \newpage
Les espaces de noms -- *namespaces* {#namespaces} Utiliser les *namespace*s
=================================== -------------------------
## Introduction ### S'isoler dans un nouveau *namespace*
Les espaces de noms du noyau, les *namespaces*, permettent de Si l'on voit l'isolation procurée par les *namespace*s en parallèle avec les
dupliquer certaines structures, habituellement considérées uniques machines virtuelle, on peut se dire qu'il suffit d'exécuter un appel système
pour le noyau, dans le but de les isoler d'un groupe de processus à un pour arriver dans un conteneur bien isolé. Cependant, le choix fait par les
autre. développeurs de Linux a été de laisser le choix des espaces de noms dont on veut
se dissocier.
On en dénombre sept (le dernier ayant été ajouté dans Linux 4.6) : `cgroup`, L'intérêt principal de cette approche, exploitée bien après la mise en avant du
`IPC`, `network`, `mount`, `PID`, `user` et `UTS`. concept, est que l'utilisation des *namespace*s ne se limite pas seulement à
des machines virtuelles légères. On retrouve ainsi dans Google Chrome de
nombreuses utilisations des *namespace*s dans le simple but d'accroître la
sécurité de leur navigateur. Ainsi, les threads de rendu n'ont pas accès au
réseau et sont cloisonnés de manière transparente pour l'utilisateur.
La notion d'espace de noms est relativement nouvelle et a été intégrée Nous allons voir dans cette partie plusieurs méthodes pour utiliser ces espaces
progressivement au sein du noyau Linux. Aussi, toutes les structures de noms.
ne sont pas encore *containerisables* :
[le document fondateur](https://www.kernel.org/doc/ols/2006/ols2006v1-pages-101-112.pdf)
parle ainsi d'isoler les périphériques, ou encore l'horloge. Pour ce
dernier,
[un patch a même déjà été proposé](https://lwn.net/Articles/766089/).
### L'espace de noms `mount` {#mount-ns}
Depuis Linux 2.4.19.
Cet espace de noms isole 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.
Attention il convient cependant de prendre garde aux types de liaison existant
entre vos points de montage (voir la partie sur
[les particularités des points de montage](#mount)), car les montages et
démontages pourraient alors être répercutés dans l'espace de noms parent.
Une manière rapide pour s'assurer que nos modifications ne sortiront pas de
notre *namespace* est d'appliquer le type esclave à l'ensemble de nos points de
montage, récursivement, dès que l'on est entré dans notre nouvel espace de
noms.
<div lang="en-US">
```bash
mount --make-rslave /
```
</div>
### L'espace de noms `UTS` {#uts-ns} #### Avec son coquillage\
Depuis Linux 2.6.19.
Cet espace de noms isole le nom de machine et son domaine NIS.
### L'espace de noms `IPC` {#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`
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 autre, 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`
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`
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` {#cgroup-ns}
Depuis Linux 4.6.
Cet espace de noms filtre l'arborescence des *Control Group* en changeant la
racine de l'arborescence des cgroups. Au sein d'un *namespace*, 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.
## S'isoler dans un nouveau *namespace*
### Avec son coquillage
De la même manière que l'on peut utiliser l'appel système `chroot(2)` depuis un De la même manière que l'on peut utiliser l'appel système `chroot(2)` depuis un
shell via la commande `chroot(1)`, la commande `unshare(1)` permet de faire le shell via la commande `chroot(1)`, la commande `unshare(1)` permet de faire le
@ -169,13 +55,16 @@ Nous avons pu ici modifier le nom de la machine, sans que cela n'affecte notre
machine hôte. machine hôte.
### Les appels systèmes #### Les appels systèmes\
L'appel système par excellence pour contrôler l'isolation d'un nouveau L'appel système par excellence pour contrôler l'isolation d'un nouveau
processus est `clone(2)`. processus est `clone(2)`.
L'isolement ou non du processus est faite en fonction des `flags` qui sont Ce *syscall*, propre à Linux, crée habituellement un nouveau processus (mais
passés à la fonction : aussi des threads) enfant de notre processus courant, comme `fork(2)` (qui lui
est un appel système POSIX) mais prend en plus de nombreux
paramètres. L'isolement ou non du processus se fait en fonction des *flags* qui
sont passés :
* `CLONE_NEWNS`, * `CLONE_NEWNS`,
* `CLONE_NEWUTS`, * `CLONE_NEWUTS`,
@ -183,16 +72,18 @@ passés à la fonction :
* `CLONE_NEWPID`, * `CLONE_NEWPID`,
* `CLONE_NEWNET`, * `CLONE_NEWNET`,
* `CLONE_NEWUSER`, * `CLONE_NEWUSER`,
* `CLONE_NEWCGROUP`. * `CLONE_NEWCGROUP`,
* `CLONE_NEWTIME`.
On peut bien entendu cumuler un ou plusieurs de ces `flags`, et les combiner On peut bien entendu cumuler un ou plusieurs de ces *flags*, et les combiner
avec d'autres `flags` attendu par la fonction. avec d'autres attendus par `clone(2)`.
Les mêmes `flags` sont utilisés lors des appels à `unshare(2)` ou `setns(2)`. Ces mêmes *flags* sont utilisés lors des appels à `unshare(2)` ou `setns(2)`,
que nous verrons plus tard.
Pour créer un nouveau processus qui sera à la fois dans un nouvel espace de Pour créer un nouveau processus qui sera à la fois dans un nouvel espace de
noms réseau et dans un nouveau *namespace* `cgroup`, on écrirait un code noms réseau et dans un nouveau *namespace* `cgroup`, on écrirait un code C
similaire à : semblable à :
<div lang="en-US"> <div lang="en-US">
```c ```c
@ -217,102 +108,30 @@ Un exemple complet d'utilisation de `clone(2)` et du *namespace* `UTS` est
donné dans le `man` de l'appel système. donné dans le `man` de l'appel système.
## Rejoindre un *namespace* ##### `unshare`\
Rejoindre un espace de noms se fait en utilisant l'appel système `setns(2)`, L'appel système `clone(2)` va créer un nouveau processus, ou un nouveau
auquel on passe le *file descriptor* d'un des liens du dossier thread. Parfois, on souhaite faire entrer notre processus ou thread en cours
`/proc/<PID>/ns/` : dans un nouvel espace de noms. Dans ce cas, on utilisera l'appel système
`unshare(2)`. Celui-ci prend uniquement en argument une liste de *flags* des
*namespace*s dont on souhaite se dissocier.
<div lang="en-US"> ::::: {.warning}
```c Le comportement de `unshare(2)` (ou `unshare(3)`) avec les *namespace*s *PID*
#define _GNU_SOURCE et *Time* n'est pas celui que l'on peut attendre !
#include <fcntl.h>
#include <sched.h>
#include <stdlib.h>
// ./a.out /proc/PID/ns/FILE cmd args... En effet, après avoir appelé `unshare`, le processus reste tout de même dans
son *namespace* *Time* ou *PID* d'origine, seuls ses enfants (après un appel à
`fork(2)` ou `clone(2)` par exemple) seront dans le nouveau *namespace*.
int Cela poserait trop de problème de faire changer le PID d'un processus en cours
main(int argc, char *argv[]) d'exécution, de même qu'il serait impensable que la `CLOCK_MONOTONIC` puisse
{ faire un saut en avant ou en arrière. Alors le choix qui a été fait est que
int fd = open(argv[1], O_RDONLY); seuls les fils créés après l'appel à `unshare(2)` seront concrètement dans le
if (fd == -1) nouveau *namespace*. C'est dans cette situation que `pid` et `pid_for_children`
{ peuvent être différents dans le dossier `/proc/<PID>/ns`.
perror("open");
return EXIT_FAILURE;
}
if (setns(fd, 0) == -1) On ne remarque pas cette bizarrerie avec `clone(2)`, car il crée déjà un
{ nouveau processus, son PID et sa `CLOCK_MONOTONIC` sont directement à la bonne
perror("setns"); valeur dès l'exécution de la fonction `fn`.
return EXIT_FAILURE; :::::
}
execvp(argv[2], &argv[2]);
perror("execve");
return EXIT_FAILURE;
}
```
</div>
Dans un shell, on utilisera la commande `nsenter(1)` :
<div lang="en-US">
```bash
42sh# nsenter --uts=/proc/42/ns/uts /bin/bash
```
</div>
## Durée de vie d'un *namespace* {#ns-lifetime}
Le noyau tient à jour un compteur de références pour chaque *namespace*. Dès
qu'une référence tombe à 0, l'espace de noms est automatiquement libéré, les
points de montage sont démontés, les interfaces réseaux sont réattribués à
l'espace de noms initial, ...
Ce compteur évolue selon plusieurs critères, et principalement selon le nombre
de processus qui l'utilise. C'est-à-dire que, la plupart du temps, le
*namespace* est libéré lorsque le dernier processus s'exécutant dedans se
termine.
Lorsque l'on a besoin de référencer un *namespace* (par exemple pour le faire
persister après le dernier processus), on peut utiliser un `mount bind` :
<div lang="en-US">
```bash
42sh# touch /tmp/ns/myrefns
42sh# mount --bind /proc/<PID>/ns/mount /tmp/ns/myrefns
```
</div>
De cette manière, même si le lien initial n'existe plus (si le `<PID>` s'est
terminé), `/tmp/ns/myrefns` pointera toujours au bon endroit.
On peut très bien utiliser directement ce fichier pour obtenir un descripteur
de fichier valide vers le *namespace* (pour passer à `setns(2)`).
### Faire persister un *namespace*
Il n'est pas possible de faire persister un espace de noms d'un reboot à
l'autre.
Même en étant attaché à un fichier du disque, il s'agit d'un pointeur vers une
structure du noyau, qui ne persistera pas au redémarrage.
## Aller plus loin {-}
Je vous recommande la lecture des *man* suivants :
* `namespaces(7)` : introduisant et énumérant les *namespaces* ;
Pour tout connaître en détails, [la série d'articles de Michael Kerrisk sur
les *namespaces*](https://lwn.net/Articles/531114/) est excellente ! Auquel il
faut ajouter [le petit dernier sur le `cgroup`
*namespace*](https://lwn.net/Articles/621006/).
[Cet article de Michael Crosby montrant l'utilisation de clone(2)](https://web.archive.org/web/20190206073558/http://crosbymichael.com/creating-containers-part-1.html)
est également des plus intéressants, pour ce qui concerne la programmation
plus bas-niveau.

159
tutorial/4/intro.md Normal file
View File

@ -0,0 +1,159 @@
\newpage
Les espaces de noms -- *namespaces* {#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 sa propre pile
réseau, ... Voyons maintenant les *namespaces* qui vont nous permettre de faire
cela.
Initiation rapide
-----------------
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 de les isoler d'un groupe de processus à un autre.
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>
#### L'espace de noms `mount` {.unnumbered #mount-ns}
Depuis Linux 2.4.19.
Cet espace de noms isole 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.
#### 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 virtualisations 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.

40
tutorial/4/lifetime.md Normal file
View File

@ -0,0 +1,40 @@
### Durée de vie d'un *namespace* {#ns-lifetime}
Le noyau tient à jour un compteur de références pour chaque *namespace*. Dès
qu'une référence tombe à 0, la structure de l'espace de noms est
automatiquement libérée, les points de montage sont démontés, les interfaces
réseaux sont réattribués à l'espace de noms initial, ...
Ce compteur évolue selon plusieurs critères, et principalement selon le nombre
de processus qui l'utilisent. C'est-à-dire que, la plupart du temps, le
*namespace* est libéré lorsque le dernier processus s'exécutant dedans se
termine.
Lorsque l'on a besoin de référencer un *namespace* (par exemple pour le faire
persister après le dernier processus), on peut utiliser un `mount bind` :
<div lang="en-US">
```bash
42sh# touch /tmp/ns/myrefns
42sh# mount --bind /proc/<PID>/ns/mount /tmp/ns/myrefns
```
</div>
De cette manière, même si le lien initial n'existe plus (si le `<PID>` s'est
terminé), `/tmp/ns/myrefns` pointera toujours au bon endroit.
Il est aussi tout à fait possible d'utiliser directement ce fichier pour
obtenir un descripteur de fichier valide vers le *namespace* (pour passer à
`setns(2)`).
::::: {.question}
#### Faire persister un *namespace* ? {-}
\
Il n'est pas possible de faire persister un espace de noms d'un reboot à
l'autre.\
Même en étant attaché à un fichier du disque, il s'agit d'un pointeur vers une
structure du noyau, qui ne persistera pas au redémarrage.
:::::

View File

@ -3,7 +3,10 @@
Des particularités de `mount` {#mount} Des particularités de `mount` {#mount}
============================= =============================
Petite parenthèse avant de parler des *namespaces* ... Petite parenthèse avant de parler des *namespaces*, car nous avons encore
besoin d'appréhender un certain nombre de concepts relatifs aux montages de
systèmes de fichiers.
## Les points de montage ## Les points de montage
@ -19,7 +22,7 @@ deux endroits différents de votre arborescence ?
Si pour plein de raisons on pouvait se dire que cela ne devrait pas être Si pour plein de raisons on pouvait se dire que cela ne devrait pas être
autorisé, ce problème s'avère être à la base de beaucoup de fonctionnalités autorisé, ce problème s'avère être à la base de beaucoup de fonctionnalités
intéressantes. Le noyau va finalement décorréler les notions de montage, intéressantes. Le noyau va finalement décorréler les notions de montage,
d'accès et d'accroches dans l'arborescence : et par exemple, une partition ne d'accès et d'accroche dans l'arborescence : et par exemple, une partition ne
sera plus forcément démontée après un appel à `umount(2)`, mais le sera sera plus forcément démontée après un appel à `umount(2)`, mais le sera
seulement lorsque cette partition n'aura plus d'accroches dans aucune seulement lorsque cette partition n'aura plus d'accroches dans aucune
arborescence. arborescence.
@ -59,7 +62,7 @@ TARGET SOURCE FSTYPE OPTIONS
``` ```
</div> </div>
## `bind` ## `bind` -- montage miroir
Lorsque l'on souhaite monter à un deuxième endroit (ou plus) une partition, on Lorsque l'on souhaite monter à un deuxième endroit (ou plus) une partition, on
utilise le *bind mount* : utilise le *bind mount* :
@ -115,9 +118,9 @@ mount --rbind /sys sys
</div> </div>
## Les montages parfumés ## Les types de propagation des points de montage
On distingue quatre variétés de répercution des montages pour un sous-arbre : On distingue quatre variétés de propagation des montages pour un sous-arbre :
partagé, esclave, privé et non-attachable. partagé, esclave, privé et non-attachable.
Chacun va agir sur la manière dont seront propagées les nouvelles accroches au Chacun va agir sur la manière dont seront propagées les nouvelles accroches au
@ -232,7 +235,7 @@ mount --bind /mnt/test-slave /mnt/test-unbindable
</div> </div>
### Parfums récursifs ### Propagation récursive
Les options que nous venons de voir s'appliquent sur un point de montage. Il Les options que nous venons de voir s'appliquent sur un point de montage. Il
existe les mêmes options pour les appliquer en cascade sur les points d'attache existe les mêmes options pour les appliquer en cascade sur les points d'attache
@ -248,7 +251,7 @@ mount --make-runbindable mountpoint
</div> </div>
## `bind` de dossiers et de fichiers ## Montage miroir de dossiers et de fichiers
Il n'est pas nécessaire que le point d'accroche que l'on cherche à dupliquer Il n'est pas nécessaire que le point d'accroche que l'on cherche à dupliquer
pointe sur un point de montage (c'est-à-dire, dans la plupart des cas : une pointe sur un point de montage (c'est-à-dire, dans la plupart des cas : une
@ -258,7 +261,7 @@ que l'on pourrait faire entre plusieurs partitions et qui ne persisterait pas
au redémarrage (le *hardlink* persiste au redémarrage, mais doit se faire au au redémarrage (le *hardlink* persiste au redémarrage, mais doit se faire au
sein d'une même partition). sein d'une même partition).
Nous verrons dans la partie [*namespace* réseau](#net-ns), une utilisation Nous verrons dans la partie [*namespace* réseau](#net-ns) une utilisation
d'attache sur un fichier. d'attache sur un fichier.

View File

@ -1,17 +1,33 @@
\newpage \newpage
Le *namespace* `mount` Le *namespace* `mount`
====================== ----------------------
L'espace de noms `mount` permet d'isoler la vision du système de fichiers L'espace de noms `mount` permet d'isoler la vision du système de fichiers
qu'ont un processus et ses fils. qu'ont un processus et ses fils.
Peut-être que l'on peut trouver avec ça, un moyen de faire un `chroot` plus sûr ? Peut-être que l'on peut trouver avec ça, un moyen de faire un `chroot` plus sûr ?
::::: {.warning}
Attention il convient de prendre garde aux types de liaison existant entre vos
points de montage (voir la partie sur [les particularités des points de
montage](#mount)), car les montages et démontages pourraient alors être
répercutés dans l'espace de noms parent.\
## Préparation du changement de racine Une manière rapide pour s'assurer que nos modifications ne sortiront pas de
notre *namespace* est d'appliquer le type esclave à l'ensemble de nos points de
montage, récursivement, dès que l'on est entré dans notre nouvel espace de
noms.
### Avant propos <div lang="en-US">
```bash
mount --make-rslave /
```
</div>
:::::
### Préparation du changement de racine
Nous allons essayer de changer la racine de notre système de fichier. À la Nous allons essayer de changer la racine de notre système de fichier. À la
différence d'un `chroot(2)`, changer de racine est quelque chose d'un peu plus différence d'un `chroot(2)`, changer de racine est quelque chose d'un peu plus
@ -29,7 +45,7 @@ montage virtuels. Le changement de racine sera donc effectif uniquement dans
cet espace de noms. cet espace de noms.
### L'environnement #### L'environnement\
Pour pouvoir changer de racine, il est nécessaire que la nouvelle racine soit Pour pouvoir changer de racine, il est nécessaire que la nouvelle racine soit
la racine d'un point de montage, comme l'explique `pivot_root(2)`. En effet, il la racine d'un point de montage, comme l'explique `pivot_root(2)`. En effet, il
@ -49,7 +65,7 @@ Placez ensuite dans cette nouvelle racine le système de votre choix (cf. le TP
précédent pour les différentes méthodes et liens). précédent pour les différentes méthodes et liens).
## Changer de racine ### Exercice : Changer de racine -- `myswitch_root.sh`
Voici les grandes étapes du changement de racine : Voici les grandes étapes du changement de racine :
@ -59,7 +75,7 @@ Voici les grandes étapes du changement de racine :
3. `pivot_root` ! 3. `pivot_root` !
### S'isoler #### S'isoler\
Notre but étant de démonter toutes les partitions superflues, nous allons Notre but étant de démonter toutes les partitions superflues, nous allons
devoir nous isoler sur : devoir nous isoler sur :
@ -79,14 +95,14 @@ Isolons-nous :
</div> </div>
### Dissocier la propagation des démontages #### Dissocier la propagation des démontages\
Attention ! avant de pouvoir commencer à démonter les partitions, il faut Attention ! avant de pouvoir commencer à démonter les partitions, il faut
s'assurer que les démontages ne se propagent pas via une politique de *shared s'assurer que les démontages ne se propagent pas via une politique de *shared
mount*. mount*.
Commençons donc par étiqueter tous nos points de montage (de ce *namespace*), Commençons donc par étiqueter tous nos points de montage (de ce *namespace*),
comme esclave : comme esclaves :
<div lang="en-US"> <div lang="en-US">
```bash ```bash
@ -95,26 +111,26 @@ comme esclave :
</div> </div>
### Démonter tout ! #### Démonter tout !\
À vous maintenant de démonter vos points d'attache. Il ne devrait vous rester À vous maintenant de démonter vos points d'attache. Il ne devrait vous rester
après cette étape que : `/`, `/dev`, `/sys`, `/proc`, `/run` et leurs fils. après cette étape que : `/`, `/dev`, `/sys`, `/proc`, `/run` et leurs fils.
### Switch ! #### Switch !\
À ce stade, dans votre console, vous avez plusieurs solutions : utiliser À ce stade, dans votre console, vous avez plusieurs solutions : utiliser
`switch_root(8)` ou `pivot_root(8)`. La première abstrait plus de choses que la `switch_root(8)` ou `pivot_root(8)`. La première abstrait plus de choses que la
seconde. seconde.
#### `switch_root` ##### `switch_root`\
Cette commande s'occupe de déplacer les partitions restantes pour vous, et lance Cette commande s'occupe de déplacer les partitions restantes pour vous, et lance
la première commande (*init*) de votre choix. la première commande (*init*) de votre choix.
#### `pivot_root` ##### `pivot_root`\
Cette commande, plus proche du fonctionnement de l'appel système Cette commande, plus proche du fonctionnement de l'appel système
`pivot_root(2)`, requiert de notre part que nous ayons préalablement déplacé `pivot_root(2)`, requiert de notre part que nous ayons préalablement déplacé

View File

@ -0,0 +1,14 @@
### Aller plus loin {-}
Je vous recommande la lecture du *man* `namespaces(7)` introduisant et
énumérant les *namespaces*.
Pour tout connaître en détails, [la série d'articles de Michael Kerrisk sur les
*namespaces*](https://lwn.net/Articles/531114/) est excellente ! Auquel il faut
ajouter [l'article sur le plus récent `cgroup`
*namespace*](https://lwn.net/Articles/621006/) et [le petit dernier sur le
*namespace* `time`](https://lwn.net/Articles/766089/).
[Cet article de Michael Crosby montrant l'utilisation de clone(2)](https://web.archive.org/web/20190206073558/http://crosbymichael.com/creating-containers-part-1.html)
est également des plus intéressants, pour ce qui concerne la programmation
plus bas-niveau.

View File

@ -1,15 +1,15 @@
\newpage \newpage
Le *namespace* `network` {#net-ns} Le *namespace* `network` {#net-ns}
======================== ------------------------
## Introduction ### Introduction
L'espace de noms `network`, comme son nom l'indique permet de virtualiser tout L'espace de noms `network`, comme son nom l'indique permet de virtualiser tout
ce qui est en lien avec le réseau : les interfaces, les ports, les routes, les ce qui est en lien avec le réseau : les interfaces, les ports, les routes, les
règles de filtrage, etc. règles de filtrage, etc.
En entrant dans un nouvel espace de nom `network`, on se retrouve dans un En entrant dans un nouvel espace de noms `network`, on se retrouve dans un
environnement qui n'a plus qu'une interface de *loopback* : environnement qui n'a plus qu'une interface de *loopback* :
<div lang="en-US"> <div lang="en-US">
@ -30,12 +30,12 @@ possible de lancer un serveur web sans qu'il n'entre en conflit avec celui d'un
autre espace de noms. autre espace de noms.
## Premiers pas avec `ip netns` ### Premiers pas avec `ip netns`
La suite d'outils `iproute2` propose une interface simplifiée pour utiliser le La suite d'outils `iproute2` propose une interface simplifiée pour utiliser le
*namespace* `network` : `ip netns`. *namespace* `network` : `ip netns`.
Nous pouvons tout d'abord créer un nouvel espace de nom : Nous pouvons tout d'abord créer un nouvel espace de noms :
<div lang="en-US"> <div lang="en-US">
```bash ```bash
@ -84,7 +84,7 @@ PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
</div> </div>
## *Virtual Ethernet* ### *Virtual Ethernet*
Étant donné qu'une interface réseau ne peut être présente que dans un seul Étant donné qu'une interface réseau ne peut être présente que dans un seul
espace de noms à la fois, il n'est pas bien pratique d'imposer d'avoir une espace de noms à la fois, il n'est pas bien pratique d'imposer d'avoir une
@ -106,7 +106,7 @@ créé deux interfaces `veth0` et `veth1` : les paquets envoyés sur `veth0` son
donc reçus par `veth1` et les paquets envoyés à `veth1` sont reçus par `veth0`. donc reçus par `veth1` et les paquets envoyés à `veth1` sont reçus par `veth0`.
Dans cette configuration, ces deux interfaces ne sont pas très utiles, mais si Dans cette configuration, ces deux interfaces ne sont pas très utiles, mais si
l'on place l'une des deux extrêmités dans un autre *namespace* `network`, il l'on place l'une des deux extrémités dans un autre *namespace* `network`, il
devient alors possible de réaliser un échange de paquets entre les deux. devient alors possible de réaliser un échange de paquets entre les deux.
Pour déplacer `veth1` dans notre *namespace* `virli` : Pour déplacer `veth1` dans notre *namespace* `virli` :
@ -143,7 +143,7 @@ Il ne reste donc pas grand chose à faire pour fournir Internet à notre
conteneur : via un peu de NAT ou grâce à un pont Ethernet. conteneur : via un peu de NAT ou grâce à un pont Ethernet.
## Les autres types d'interfaces ### Les autres types d'interfaces
Le bridge ou le NAT obligera tous les paquets à passer à travers de nombreuses Le bridge ou le NAT obligera tous les paquets à passer à travers de nombreuses
couches du noyau. Utiliser les interfaces *veth* est plutôt simple et disponible couches du noyau. Utiliser les interfaces *veth* est plutôt simple et disponible
@ -151,7 +151,7 @@ partout, mais c'est loin d'être la technique la plus rapide ou la moins
gourmande. gourmande.
### VLAN #### VLAN\
Il est possible d'attribuer juste une interface de VLAN, si l'on a un switch Il est possible d'attribuer juste une interface de VLAN, si l'on a un switch
supportant la technologie [802.1q](https://fr.wikipedia.org/wiki/IEEE_802.1Q) supportant la technologie [802.1q](https://fr.wikipedia.org/wiki/IEEE_802.1Q)
@ -166,7 +166,7 @@ derrière notre machine.
</div> </div>
### MACVLAN #### MACVLAN\
<!-- https://hicu.be/bridge-vs-macvlan --> <!-- https://hicu.be/bridge-vs-macvlan -->
@ -180,7 +180,7 @@ destination d'un MAC seront délivrés à l'interface possédant la MAC. Les
différences entre les modes se trouvent au niveau de la communication entre les différences entre les modes se trouvent au niveau de la communication entre les
interfaces. interfaces.
#### VEPA ##### VEPA
Dans ce mode, tous les paquets sortants sont directement envoyés sur Dans ce mode, tous les paquets sortants sont directement envoyés sur
l'interface Ethernet de sortie, sans qu'aucun routage préalable n'ait été l'interface Ethernet de sortie, sans qu'aucun routage préalable n'ait été
@ -198,7 +198,7 @@ Pour construire une nouvelle interface de ce type :
</div> </div>
#### *Private* ##### *Private*
À la différence du mode *VEPA*, si un paquet émis par un conteneur à À la différence du mode *VEPA*, si un paquet émis par un conteneur à
destination d'un autre conteneur est réfléchi par un switch, le paquet ne sera destination d'un autre conteneur est réfléchi par un switch, le paquet ne sera
@ -214,7 +214,7 @@ conteneur de la même machine.
</div> </div>
#### *Bridge* ##### *Bridge*
À l'inverse des modes *VEPA* et *private*, les paquets sont routés selon leur À l'inverse des modes *VEPA* et *private*, les paquets sont routés selon leur
adresse MAC : si jamais une adresse MAC est connue, le paquet est délivré à adresse MAC : si jamais une adresse MAC est connue, le paquet est délivré à
@ -230,12 +230,12 @@ Pour construire une nouvelle interface de ce type :
</div> </div>
## Aller plus loin {-} ### Aller plus loin {-}
Pour approfondir les différentes techniques de routage, je vous Pour approfondir les différentes techniques de routage, je vous
recommande cet article : recommande cet article :
[Linux Containers and Networking](https://blog.flameeyes.eu/2010/09/linux-containers-and-networking). [Linux Containers and Networking](https://blog.flameeyes.eu/2010/09/linux-containers-and-networking).
Appliqué à Docker, vous apprécirez cet article : [Understanding Docker Appliqué à Docker, vous apprécierez cet article : [Understanding Docker
Networking Drivers and their use Networking Drivers and their use
cases](https://www.docker.com/blog/understanding-docker-networking-drivers-use-cases/). cases](https://www.docker.com/blog/understanding-docker-networking-drivers-use-cases/).

View File

@ -1,9 +1,9 @@
\newpage \newpage
Le *namespace* `PID` {#pid-ns} Le *namespace* `PID` {#pid-ns}
===================== --------------------
## Introduction {#pid-ns-intro} ### Introduction {#pid-ns-intro}
L'espace de noms `PID` est celui qui va nous permettre d'isoler un sous-arbre L'espace de noms `PID` est celui qui va nous permettre d'isoler un sous-arbre
de processus en créant un nouvel arbre, qui aura son propre processus considéré de processus en créant un nouvel arbre, qui aura son propre processus considéré
@ -18,7 +18,7 @@ pas de PID en cours de route, puisqu'il dépend du *namespace* dans lequel il se
trouve. trouve.
## Isolons ! ### Isolons !
Première étape s'isoler : Première étape s'isoler :
@ -32,7 +32,7 @@ Nous utilisons ici l'option `-f`, pour que le passage dans le nouvel espace de
noms des PID soit effectif (cf. [Introduction](#pid-ns-intro)). noms des PID soit effectif (cf. [Introduction](#pid-ns-intro)).
Un coup d'œil à `top` ou `ps aux` devrait nous montrer que l'on est maintenant Un coup d'œil à `top` ou `ps aux` devrait nous montrer que l'on est maintenant
seul processus ... pourtant, il n'en est rien, ces deux commandes continuent le seul processus ... pourtant, il n'en est rien, ces deux commandes continuent
d'afficher la liste complète des processus de notre système. d'afficher la liste complète des processus de notre système.
Cela est dû au fait que ces deux programmes, sous Linux, se basent sur le Cela est dû au fait que ces deux programmes, sous Linux, se basent sur le
@ -45,7 +45,7 @@ notre système initial. Pour s'en sortir, il est nécessaire de s'isoler dans un
*namespace* `mount` séparé. *namespace* `mount` séparé.
### Double isolation : ajout du *namespace* `mount` #### Double isolation : ajout du *namespace* `mount`\
Voici la nouvelle ligne de commande que l'on va utiliser : Voici la nouvelle ligne de commande que l'on va utiliser :
@ -62,7 +62,7 @@ Cette fois, `top` et `ps` nous rapportent bien que l'on est seul dans notre
*namespace*. *namespace*.
## Arborescence à l'extérieur du *namespace* ### Arborescence à l'extérieur du *namespace*
Lors de notre première tentative de `top`, lorsque `/proc` était encore monté Lors de notre première tentative de `top`, lorsque `/proc` était encore monté
sur le `procfs` de l'espace de noms initial : notre processus (au PID 1 dans sur le `procfs` de l'espace de noms initial : notre processus (au PID 1 dans
@ -79,14 +79,14 @@ parent, il faut le faire avec son identifiant de processus du même *namespace*
que le processus appelant. que le processus appelant.
## Processus orphelins et `nsenter` ### Processus orphelins et `nsenter`
Au sein d'un *namespace*, le processus au PID 1 est considéré comme le Au sein d'un *namespace*, le processus au PID 1 est considéré comme le
programme `init`, les mêmes propriétés s'appliquent donc. programme `init`, les mêmes propriétés s'appliquent donc.
Si un processus est orphelin, il est donc affiché comme étant fils du PID 1 Si un processus est orphelin, il est donc affiché comme étant fils du PID 1
dans son *namespace*[^PR_SET_CHILD_SUBREAPER] ; il n'est pas sorti de l'espace dans son *namespace*[^PR_SET_CHILD_SUBREAPER] ; il n'est pas sorti de l'espace
de nom. de noms.
[^PR_SET_CHILD_SUBREAPER]: en réalité, ce comportement est lié à la propriété [^PR_SET_CHILD_SUBREAPER]: en réalité, ce comportement est lié à la propriété
`PR_SET_CHILD_SUBREAPER`, qui peut être définie pour n'importe quel processus `PR_SET_CHILD_SUBREAPER`, qui peut être définie pour n'importe quel processus
@ -101,7 +101,7 @@ l'extérieur du conteneur, les propriétés d'`init` sont biens appliquées à
l'intérieur pour conserver un comportement cohérent. l'intérieur pour conserver un comportement cohérent.
## Aller plus loin {-} ### Aller plus loin {-}
N'hésitez pas à jeter un œil à la page de manuel consacré à cet espace de N'hésitez pas à jeter un œil à la page de manuel consacrée à cet espace de
noms : `pid_namespaces(7)`. noms : `pid_namespaces(7)`.

View File

@ -3,16 +3,19 @@
Projet et rendu Projet et rendu
=============== ===============
Projet Est attendu d'ici le TP suivant :
------
[Le sujet complet du projet est disponible ici](https://virli.nemunai.re/project-3.pdf). Il - le rendu des exercice de ce TP ;
n'est pas à rendre en même temps que le TP. Consultez ses modalités de rendu - vos réponses à [l'évaluation du cours](https://virli.nemunai.re/quiz/15).
pour plus d'informations.
Pour les GISTRE (et en bonus pour les SRS), [un
projet](https://virli.nemunai.re/project-2.pdf) est à rendre pour le 13
novembre. Consultez les modalités de rendu sur le sujet directement.
Modalités de rendu ## Modalités de rendu
------------------
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
vous devrez m'envoyer vos rendus signés avec votre clef PGP.
Un service automatique s'occupe de réceptionner vos rendus, de faire des Un service automatique s'occupe de réceptionner vos rendus, de faire des
vérifications élémentaires et de vous envoyer un accusé de réception (ou de vérifications élémentaires et de vous envoyer un accusé de réception (ou de
@ -23,14 +26,11 @@ et exclusivement à celle-ci que vous devez envoyer vos rendus. Tout rendu
envoyé à une autre adresse et/ou non signé et/ou reçu après la correction ne envoyé à une autre adresse et/ou non signé et/ou reçu après la correction ne
sera pas pris en compte. sera pas pris en compte.
Afin d'orienter correctement votre rendu, ajoutez une balise `[TP5]` au sujet Afin d'orienter correctement votre rendu, ajoutez une balise `[TP4]` au sujet
de votre courriel. N'hésitez pas à indiquer dans le corps du courriel votre de votre courriel. N'hésitez pas à indiquer dans le corps du message votre
ressenti et vos difficultés ou bien alors écrivez votre meilleure histoire ressenti et vos difficultés ou bien alors écrivez votre meilleure histoire
drôle si vous n'avez rien à dire. drôle si vous n'avez rien à dire.
Par ailleurs, n'oubliez pas de répondre à
[l'évaluation du cours](https://virli.nemunai.re/quiz/7).
Tarball Tarball
------- -------
@ -38,12 +38,14 @@ Tarball
Tous les exercices de ce TP sont à placer dans une tarball (pas d'archive ZIP, Tous les exercices de ce TP sont à placer dans une tarball (pas d'archive ZIP,
RAR, ...). RAR, ...).
Voici une arborescence type : Voici une arborescence type (adaptez les extensions et les éventuels
fichiers supplémentaires associés au langage que vous aurez choisi
pour chaque exercice) :
<div lang="en-US"> <div lang="en-US">
``` ```
login_x-TP5/cmpns.sh login_x-TP4/cmpns.sh
login_x-TP5/mydocker_exec.sh login_x-TP4/mydocker_exec.sh
login_x-TP5/myswitch_root.sh login_x-TP4/myswitch_root.sh
``` ```
</div> </div>

View File

@ -0,0 +1,57 @@
#### Exemple C\
Voici un exemple de code C utilisant `setns(2)` :
<div lang="en-US">
```c
#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <stdlib.h>
// ./a.out /proc/PID/ns/FILE cmd args...
int
main(int argc, char *argv[])
{
int fd = open(argv[1], O_RDONLY);
if (fd == -1)
{
perror("open");
return EXIT_FAILURE;
}
if (setns(fd, 0) == -1)
{
perror("setns");
return EXIT_FAILURE;
}
execvp(argv[2], &argv[2]);
perror("execve");
return EXIT_FAILURE;
}
```
</div>
#### Exemple shell\
Dans un shell, on utilisera la commande `nsenter(1)` :
<div lang="en-US">
```bash
42sh# unshare --uts /bin/bash
inutsns# echo $$
42
inutsns# hostname jument
# Keep this sheel active to perform nexts steps, in another shell.
42sh# hostname
chaton
42sh# nsenter --uts=/proc/42/ns/uts /bin/bash
inutsns# hostname
jument
```
</div>

67
tutorial/4/setns.md Normal file
View File

@ -0,0 +1,67 @@
\newpage
### Rejoindre un *namespace*
Rejoindre un espace de noms se fait en utilisant l'appel système `setns(2)`, ou
la commande `nsenter(1)`. Il est nécessaire de donner en argument
respectivement un *file descriptor* ou le chemin vers le fichier, lien
symbolique, représentant l'espace de nom.
#### Voir les *namespace*s d'un processus\
Chaque processus lancé est rattaché à une liste d'espaces de nom, y compris
s'il est issu du système de base (« l'hôte »).
Nous pouvons dès lors consulter le dossier `/proc/<PID>/ns/` de chaque
processus, pour consulter les différents espaces de nom de nos processus.
Tous les processus auront la même liste de fichiers, car ils sont liés à un
espace de noms par *namespace* utilisable avec le noyau. D'une machine à
l'autre, d'une version du noyau à l'autre, il est normal d'avoir une liste de
*namespace*s différents, mais d'un processus à l'autre sur le même noyau, nous
aurons les mêmes.
Ces fichiers sont en fait des liens symboliques un peu particuliers, car ils ne
pointent pas vers une destination valide :
<div lang="en-US">
```bash
42sh$ ls -l /proc/self/ns
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 net -> 'net:[4026532008]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 time -> 'time:[4026531834]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 user -> 'user:[4026531837]'
lrwxrwxrwx 1 nemunaire 0 1 oct. 23:42 uts -> 'uts:[4026531838]'
```
</div>
Les liens référencent une structure du noyau résidant en mémoire. Les numéros
entre crochets seront les mêmes pour deux processus qui partagent le même
espace de noms. La structure pointée sera différente si l'espace de nom est
différent, donc le numéro sera différent.
On ne peut pas afficher tel quel les structures, mais on peut l'ouvrir avec
`open(2)` pour obtenir un *file descriptor* que l'on pourra passer à
`setns(2)`.
Pour les commandes *shell*, il convient de donner en argument le chemin vers le
lien symbolique : la commande se chargera d'`open(2)` le fichier.
#### `*_for_children`\
Vous avez peut-être remarqué des fichiers `*_for_children` dans le dossier `ns`
de vos processus. Nous verrons par la suite que les espaces de noms *PID* et
*Time*, une fois créés, ne s'appliquent pas directement au processus en cours
d'exécution, la dissociation de *namespace* ne peut se faire que pour les
processus/threads fils.
`pid_for_children` et `time_for_children` représentent donc les *namespace*s
qui seront attribués aux fils lancés après un `unshare(2)` réussi.

View File

@ -1,14 +1,11 @@
\newpage ### Prérequis
Mise en place
=============
Noyau Linux #### Noyau Linux \
-----------
Ce TP requiert un noyau Linux, dans sa version 3.12 au minimum. Il doit de plus Pour pouvoir suivre les exercices ci-après, vous devez disposez d'un noyau
être compilé avec les options suivantes (lorsqu'elles sont disponibles pour Linux, idéalement dans sa version 5.6 ou mieux. Il doit de plus être compilé
votre version) : avec les options suivantes (lorsqu'elles sont disponibles pour votre version) :
<div lang="en-US"> <div lang="en-US">
``` ```
@ -16,6 +13,7 @@ General setup --->
[*] Control Group support ---> [*] Control Group support --->
-*- Namespaces support -*- Namespaces support
[*] UTS namespace [*] UTS namespace
[*] TIME namespace
[*] IPC namespace [*] IPC namespace
[*] User namespace [*] User namespace
[*] PID Namespaces [*] PID Namespaces
@ -38,6 +36,7 @@ CONFIG_CGROUPS=y
CONFIG_NAMESPACES=y CONFIG_NAMESPACES=y
CONFIG_UTS_NS=y CONFIG_UTS_NS=y
CONFIG_TIME_NS=y
CONFIG_IPC_NS=y CONFIG_IPC_NS=y
CONFIG_USER_NS=y CONFIG_USER_NS=y
CONFIG_PID_NS=y CONFIG_PID_NS=y
@ -52,11 +51,11 @@ CONFIG_VETH=m
``` ```
</div> </div>
Référez-vous, si besoin, au TP précédent pour la marche à suivre. Référez-vous, si besoin, à la précédente configuration que l'on a faite pour la
marche à suivre.
Paquets #### Paquets \
-------
Nous allons utiliser des programmes issus des Nous allons utiliser des programmes issus des
[`util-linux`](https://www.kernel.org/pub/linux/utils/util-linux/), de [`util-linux`](https://www.kernel.org/pub/linux/utils/util-linux/), de
@ -69,12 +68,18 @@ Sous Debian et ses dérivés, ces paquets sont respectivement :
* `procps` * `procps`
* `libcap2-bin` * `libcap2-bin`
Sous ArchLinux et ses dérivés, ces paquets sont respectivement :
À propos de la sécurité de l'espace de nom `user` * `util-linux`
------------------------------------------------- * `procps-ng`
* `libcap`
La sécurité du *namespace* `user` a souvent été remise en cause et on lui
attribue de nombreuses vulnérabilités. Je vous laisse consulter à ce sujet : #### À propos de la sécurité de l'espace de noms `user` \
La sécurité du *namespace* `user` a souvent été remise en cause par le passé,
on lui attribue de nombreuses vulnérabilités. Vous devriez notamment consulter
à ce sujet :
* [Security Implications of User Namespaces](https://blog.araj.me/security-implications-of-user-namespaces/) ; * [Security Implications of User Namespaces](https://blog.araj.me/security-implications-of-user-namespaces/) ;
* [Anatomy of a user namespaces vulnerability](https://lwn.net/Articles/543273/) ; * [Anatomy of a user namespaces vulnerability](https://lwn.net/Articles/543273/) ;
@ -84,13 +89,14 @@ attribue de nombreuses vulnérabilités. Je vous laisse consulter à ce sujet :
De nombreux projets ont choisi de ne pas autoriser l'utilisation de cet espace De nombreux projets ont choisi de ne pas autoriser l'utilisation de cet espace
de noms sans disposer de certaines *capabilities*[^userns-caps]. de noms sans disposer de certaines *capabilities*[^userns-caps].
[^userns-caps]: Sont nécessaires, conjointement, `CAP_SYS_ADMIN`, `CAP_SETUID` et `CAP_SETGID`. [^userns-caps]: Sont nécessaires, conjointement : `CAP_SYS_ADMIN`, `CAP_SETUID` et `CAP_SETGID`.
De nombreuses distributions ont par exemple choisi d'utiliser un paramètre du De nombreuses distributions ont choisi d'utiliser un paramètre du noyau pour
noyau pour adapter le comportement. adapter le comportement.
### Debian et ses dérivées {.unnumbered} ::::: {.question}
##### Debian et ses dérivées {.unnumbered}
Si vous utilisez Debian ou l'un de ses dérivés, vous devrez autoriser Si vous utilisez Debian ou l'un de ses dérivés, vous devrez autoriser
explicitement cette utilisation non-privilégiée : explicitement cette utilisation non-privilégiée :
@ -102,12 +108,13 @@ explicitement cette utilisation non-privilégiée :
</div> </div>
### Grsecurity {.unnumbered} ##### Grsecurity {.unnumbered}
D'autres patchs, tels que D'autres patchs, tels que
[*grsecurity*](https://forums.grsecurity.net/viewtopic.php?f=3&t=3929#p13904) ont [*grsecurity*](https://forums.grsecurity.net/viewtopic.php?f=3&t=3929#p13904) ont
fait le choix de désactiver cette possibilité sans laisser d'option pour la fait le choix de désactiver cette possibilité sans laisser d'option pour la
réactiver éventuellement à l'exécution. Pour avoir un comportement identique à réactiver éventuellement à l'exécution. Pour avoir un comportement identique à
celui de Debian, vous pouvez celui de Debian, vous pouvez
[appliquer ce patch](https://virli.nemunai.re/grsec-enable-user-ns.patch), sur [appliquer ce patch](https://virli.nemunai.re/grsec-enable-user-ns.patch) sur
vos sources incluant le patch de *grsecurity*. vos sources incluant le patch de *grsecurity*.
:::::

View File

@ -13,7 +13,10 @@ abstract: |
\vspace{1em} \vspace{1em}
Tous les exercices de ce TP sont à rendre à <virli@nemunai.re> au Tous les exercices de ce TP sont à rendre à <virli@nemunai.re> au
plus tard le jeudi 19 novembre 2020 à 12 h 42. plus tard le mercredi 4 novembre 2021 à 23 h 42. Consultez la dernière
section de chaque partie pour plus d'informations sur les éléments à
rendre. Et n'oubliez pas de répondre aux [questions de
cours](https://virli.nemunai.re/quiz/13).
En tant que personnes sensibilisées à la sécurité des échanges électroniques, En tant que personnes sensibilisées à la sécurité des échanges électroniques,
vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à

View File

@ -1,9 +1,9 @@
\newpage \newpage
Le *namespace* `user` {#user-ns} Le *namespace* `user` {#user-ns}
===================== ---------------------
## Introduction ### Introduction
L'espace de noms `user` est plutôt pratique car il permet de virtualiser la L'espace de noms `user` est plutôt pratique car il permet de virtualiser la
liste et les droits des utilisateurs. liste et les droits des utilisateurs.
@ -15,44 +15,44 @@ avoir besoin à l'intérieur de cet espace de noms, sans que cela ne réduise la
sécurité des composants à l'extérieur de cet espace. sécurité des composants à l'extérieur de cet espace.
## Comportement vis-à-vis des autres *namespaces* ### Comportement vis-à-vis des autres *namespaces*
Alors qu'il est normalement nécessaire d'avoir des privilèges pour créer de Alors qu'il est normalement nécessaire d'avoir des privilèges pour créer de
nouveaux espaces de noms, en commençant par demander un *namespace* nouveaux espaces de noms, en commençant par demander un *namespace*
utilisateurs, on obtient les privilèges requis pour créer tous les autres types utilisateur, on obtient les privilèges requis pour créer tous les autres types
de *namespaces*. de *namespaces*.
Grâce à cette technique, il est possible de lancer des conteneurs en tant que Grâce à cette technique, il est possible de lancer des conteneurs en tant que
simple utilisateur ; le projet [Singularity](https://sylabs.io/) repose simple utilisateur ; le projet [Singularity](https://sylabs.io/) repose
entièrement sur cela. en grande partie sur cela.
## Correspondance des utilisateurs et des groupes ### Correspondance des utilisateurs et des groupes
Comme pour les autres espaces de noms, le *namespace* `user` permet de ne Comme pour les autres espaces de noms, le *namespace* `user` permet de ne
garder dans le nouvel espace, que les utilisateurs et les groupes utiles au garder dans le nouvel espace, que les utilisateurs et les groupes utiles au
processus, en les renumérotant au passage si besoin. processus, en les renumérotant au passage si besoin.
### L'utilisateur -2 : *nobody* #### L'utilisateur -2 : *nobody*\
Lorsque l'on arrive dans un nouvel espace, aucun utilisateur ni groupe n'est Lorsque l'on arrive dans un nouvel espace, aucun utilisateur ni groupe n'est
défini. Dans cette situation, tous les identifiants d'utilisateur et de groupe, défini. Dans cette situation, tous les identifiants d'utilisateur et de groupe,
renvoyés par le noyau sont à -2 ; valeur qui correspond par convention à renvoyés par le noyau sont à -2 ; valeur qui correspond par convention à
l'utilisateur *nobody* et au groupe *nogroup*. l'utilisateur *nobody* et au groupe *nogroup*.
-1 étant réservé pour indiqué une erreur dans le retour d'une commande, ou la -1 étant réservé pour indiquer une erreur dans le retour d'une commande, ou la
non-modification d'un paramètres passé en argument d'une fonction. non-modification d'un paramètre passé en argument d'une fonction.
### `uid_map` et `gid_map` #### `uid_map` et `gid_map` \
#### `uid_map`
Pour établir la correspondance, une fois que l'on a créé le nouveau Pour établir la correspondance, une fois que l'on a créé le nouveau
*namespace*, ces deux fichiers, accessibles dans `/proc/self/`, peuvent être *namespace*, ces deux fichiers, accessibles dans `/proc/self/`, peuvent être
écrits une fois. écrits une fois.
##### `uid_map`\
Sur chaque ligne, on doit indiquer : Sur chaque ligne, on doit indiquer :
- L'identifiant marquant le début de la plage d'utilisateurs, pour le processus - L'identifiant marquant le début de la plage d'utilisateurs, pour le processus
@ -72,8 +72,8 @@ Par exemple, le *namespace* `user` initial défini la correspondance suivante :
</div> </div>
Cela signifie que les utilisateurs dont l'identifiant court de 0 à `MAX_INT - Cela signifie que les utilisateurs dont l'identifiant court de 0 à `MAX_INT -
2` inclu, dans cet espace de noms, correspondent aux utilisateurs allant de 0 à 2` inclus, dans cet espace de noms, correspondent aux utilisateurs allant de 0 à
`MAX_INT - 1` inclu, pour le processus affichant ce fichier. `MAX_INT - 1` inclus, pour le processus affichant ce fichier.
Lorsque l'on crée un *namespace* `user`, généralement, la correspondance vaut : Lorsque l'on crée un *namespace* `user`, généralement, la correspondance vaut :
@ -89,13 +89,13 @@ l'utilisateur root, dans le conteneur équivaut à l'utilisateur 1000 hors de
l'espace de noms. l'espace de noms.
#### `gid_map` ##### `gid_map`\
Le principe est identique pour ce fichier, mais agit sur les correspondances Le principe est identique pour ce fichier, mais agit sur les correspondances
des groupes au lieu des utilisateurs. des groupes au lieu des utilisateurs.
## Utilisation de l'espace de noms ### Utilisation de l'espace de noms
<div lang="en-US"> <div lang="en-US">
```bash ```bash
@ -110,7 +110,7 @@ jeu. L'idée étant que l'on a été désigné root dans son conteneur, on devra
pouvoir y faire ce que l'on veut, tant que l'on n'agit pas en dehors. pouvoir y faire ce que l'on veut, tant que l'on n'agit pas en dehors.
## Aller plus loin {-} ### Aller plus loin {-}
N'hésitez pas à jeter un œil à la page du manuel consacré à ce *namespace* : N'hésitez pas à jeter un œil à la page du manuel consacrée à ce *namespace* :
`user_namespaces(7)`. `user_namespaces(7)`.