virli/tutorial/4/howto.md

139 lines
5.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\newpage
Utiliser les *namespace*s
-------------------------
### S'isoler dans un nouveau *namespace*
Si l'on voit l'isolation procurée par les *namespace*s en parallèle avec les
machines virtuelles, on peut se dire qu'il suffit d'exécuter un appel système
pour arriver dans un conteneur bien isolé. Cependant, le choix fait par les
développeurs de Linux a été de laisser le choix des espaces de noms dont on veut
se dissocier.
L'intérêt principal de cette approche, exploitée bien après la mise en avant du
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.
Nous allons voir dans cette partie plusieurs méthodes pour utiliser ces espaces
de noms.
#### Dans son shell
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
nécessaire pour lancer l'appel système `unshare(2)`, puis, tout comme
`chroot(1)`, exécuter le programme passé en paramètre.
En fonction des options qui lui sont passées, `unshare(1)` va créer le/les
nouveaux *namespaces* et placer le processus dedans.
Par exemple, nous pouvons modifier sans crainte le nom de notre machine, si
nous sommes passés dans un autre *namespace* `UTS` :
<div lang="en-US">
```
42sh# hostname --fqdn
koala.zoo.paris
42sh# sudo unshare -u /bin/bash
bash# hostname --fqdn
koala.zoo.paris
bash# hostname lynx.zoo.paris
bash# hostname --fqdn
lynx.zoo.paris
bash# exit
42sh# hostname --fqdn
koala.zoo.paris
```
</div>
Nous avons pu ici modifier le nom de la machine, sans que cela n'affecte notre
machine hôte.
#### Les appels systèmes
L'appel système par excellence pour contrôler l'isolation d'un nouveau
processus est `clone(2)`.
Ce *syscall*, propre à Linux, crée habituellement un nouveau processus (mais
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_NEWUTS`,
* `CLONE_NEWIPC`,
* `CLONE_NEWPID`,
* `CLONE_NEWNET`,
* `CLONE_NEWUSER`,
* `CLONE_NEWCGROUP`,
* `CLONE_NEWTIME`.
On peut bien entendu cumuler un ou plusieurs de ces *flags*, et les combiner
avec d'autres attendus par `clone(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
noms réseau et dans un nouveau *namespace* `cgroup`, on écrirait un code C
semblable à :
<div lang="en-US">
```c
#include <sched.h>
#define STACKSIZE (1024 * 1024)
static char child_stack[STACKSIZE];
int clone_flags = CLONE_CGROUP | CLONE_NEWNET | SIGCHLD;
pid_t pid = clone(do_execvp, // First function executed by child
child_stack + STACKSIZE, // Assume stack grows downward
clone_flags, // clone specials flags
args); // Arguments to pass to
// do_execvp
```
</div>
Dans cet exemple, le processus fils créé disposera d'un nouvel espace de noms
pour les *CGroups* et disposera d'une nouvelle pile réseau.
Un exemple complet d'utilisation de `clone(2)` et du *namespace* `UTS` est
donné dans le `man` de l'appel système.
##### `unshare`\
L'appel système `clone(2)` va créer un nouveau processus, ou un nouveau
thread. Parfois, on souhaite faire entrer notre processus ou thread en cours
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.
::::: {.warning}
Le comportement de `unshare(2)` (ou `unshare(3)`) avec les *namespace*s *PID*
et *Time* n'est pas celui que l'on peut attendre !
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*.
Cela poserait trop de problème de faire changer le PID d'un processus en cours
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
seuls les fils créés après l'appel à `unshare(2)` seront concrètement dans le
nouveau *namespace*. C'est dans cette situation que `pid` et `pid_for_children`
peuvent être différents dans le dossier `/proc/<PID>/ns`.
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
valeur dès l'exécution de la fonction `fn`.
:::::