2021-10-31 19:51:17 +00:00
|
|
|
|
Utiliser les *namespace*s
|
|
|
|
|
-------------------------
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
### S'isoler dans un nouveau *namespace*
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
Si l'on voit l'isolation procurée par les *namespace*s comme des 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.
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
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 à
|
2022-11-11 09:14:16 +00:00
|
|
|
|
des machines virtuelles légères. On retrouve ainsi dans Google Chrome et
|
|
|
|
|
Firefox 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.
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
Nous allons voir dans cette partie plusieurs méthodes pour utiliser ces espaces
|
|
|
|
|
de noms.
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
#### Dans son shell\
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
|
|
|
|
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
|
2017-11-09 00:30:41 +00:00
|
|
|
|
nécessaire pour lancer l'appel système `unshare(2)`, puis, tout comme
|
2016-10-19 03:24:05 +00:00
|
|
|
|
`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
|
2022-02-24 19:43:43 +00:00
|
|
|
|
nous sommes passés dans un autre *namespace* `UTS` :
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
2017-10-17 06:29:07 +00:00
|
|
|
|
<div lang="en-US">
|
2018-11-16 01:38:41 +00:00
|
|
|
|
```
|
|
|
|
|
42sh# hostname --fqdn
|
|
|
|
|
koala.zoo.paris
|
|
|
|
|
42sh# sudo unshare -u /bin/bash
|
2019-11-03 17:54:22 +00:00
|
|
|
|
bash# hostname --fqdn
|
|
|
|
|
koala.zoo.paris
|
|
|
|
|
bash# hostname lynx.zoo.paris
|
|
|
|
|
bash# hostname --fqdn
|
|
|
|
|
lynx.zoo.paris
|
|
|
|
|
bash# exit
|
2018-11-16 01:38:41 +00:00
|
|
|
|
42sh# hostname --fqdn
|
|
|
|
|
koala.zoo.paris
|
2016-10-19 03:24:05 +00:00
|
|
|
|
```
|
2017-10-17 06:29:07 +00:00
|
|
|
|
</div>
|
2016-10-19 03:24:05 +00:00
|
|
|
|
|
2017-11-09 00:30:41 +00:00
|
|
|
|
Nous avons pu ici modifier le nom de la machine, sans que cela n'affecte notre
|
2016-10-19 03:24:05 +00:00
|
|
|
|
machine hôte.
|
|
|
|
|
|
2015-10-07 01:45:39 +00:00
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
#### Les appels système\
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
|
|
|
|
L'appel système par excellence pour contrôler l'isolation d'un nouveau
|
|
|
|
|
processus est `clone(2)`.
|
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
Ce *syscall*, propre à Linux, crée habituellement un nouveau processus enfant
|
|
|
|
|
de notre processus courant (mais il peut aussi créer des *threads*, on va voir
|
|
|
|
|
qu'il fait beaucoup de choses), comme `fork(2)` (qui lui est un appel système
|
|
|
|
|
POSIX). Mais il prend en plus de nombreux paramètres. L'isolement ou non du
|
|
|
|
|
processus se fait en fonction des *flags* qui sont passés. On retrouve donc :
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
|
|
|
|
* `CLONE_NEWNS`,
|
|
|
|
|
* `CLONE_NEWUTS`,
|
|
|
|
|
* `CLONE_NEWIPC`,
|
|
|
|
|
* `CLONE_NEWPID`,
|
|
|
|
|
* `CLONE_NEWNET`,
|
|
|
|
|
* `CLONE_NEWUSER`,
|
2021-10-31 19:51:17 +00:00
|
|
|
|
* `CLONE_NEWCGROUP`,
|
|
|
|
|
* `CLONE_NEWTIME`.
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
::::: {.more}
|
|
|
|
|
|
|
|
|
|
Le nom du *flag* `CLONE_NEWNS` est historique et assez peu explicite
|
|
|
|
|
contrairement aux autres : il désigne en fait l'espace de nom `mount`.
|
|
|
|
|
|
|
|
|
|
Au départ, les *namespace*s ont étés pensés pour former un tout : une couche
|
|
|
|
|
d'isolation complète pour les processus. Mais lors des développements suivants,
|
|
|
|
|
il s'est avéré pratique de pouvoir choisir finement de quels aspects on
|
|
|
|
|
souhaitait se dissocier.
|
|
|
|
|
|
|
|
|
|
C'est ainsi que pour chaque nouveau *namespace*, un nouveau *flag* est
|
|
|
|
|
introduit.
|
|
|
|
|
|
|
|
|
|
:::::
|
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
On peut bien entendu cumuler un ou plusieurs de ces *flags*, et les combiner
|
|
|
|
|
avec d'autres attendus par `clone(2)`.
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
Ces mêmes *flags* sont utilisés lors des appels à `unshare(2)` ou `setns(2)`,
|
|
|
|
|
que nous verrons plus tard.
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
::::: {.code}
|
|
|
|
|
|
2017-11-09 00:30:41 +00:00
|
|
|
|
Pour créer un nouveau processus qui sera à la fois dans un nouvel espace de
|
2021-10-31 19:51:17 +00:00
|
|
|
|
noms réseau et dans un nouveau *namespace* `cgroup`, on écrirait un code C
|
2022-02-24 19:43:43 +00:00
|
|
|
|
semblable à :
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2017-10-17 06:29:07 +00:00
|
|
|
|
<div lang="en-US">
|
2016-10-20 01:17:42 +00:00
|
|
|
|
```c
|
2018-11-16 01:38:41 +00:00
|
|
|
|
#include <sched.h>
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2021-09-11 12:41:43 +00:00
|
|
|
|
#define STACKSIZE (1024 * 1024)
|
2018-11-16 01:38:41 +00:00
|
|
|
|
static char child_stack[STACKSIZE];
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2018-11-16 01:38:41 +00:00
|
|
|
|
int clone_flags = CLONE_CGROUP | CLONE_NEWNET | SIGCHLD;
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2022-05-04 09:18:16 +00:00
|
|
|
|
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
|
2016-10-20 01:17:42 +00:00
|
|
|
|
```
|
2017-10-17 06:29:07 +00:00
|
|
|
|
</div>
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2021-09-11 12:41:43 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
::::: {.question}
|
|
|
|
|
|
|
|
|
|
#### Quel est le rôle du *flag* `SIGCHLD` ? {-}
|
|
|
|
|
\
|
|
|
|
|
|
|
|
|
|
Lorsque l'on crée un nouveau processus, on ajoute l'option `SIGCHLD` afin
|
|
|
|
|
d'être notifié par signal lorsque notre processus fil a terminé son
|
|
|
|
|
exécution. Cela permet d'être réveillé de notre `wait(2)`.
|
|
|
|
|
|
|
|
|
|
:::::
|
|
|
|
|
|
|
|
|
|
:::::
|
|
|
|
|
|
|
|
|
|
L'appel système `clone(2)` va donc créer un nouveau processus, ou un nouveau
|
|
|
|
|
*thread*, mais parfois on souhaite juste isoler notre processus actuel.
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2015-10-07 01:45:39 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
##### `unshare`\
|
2015-10-07 01:45:39 +00:00
|
|
|
|
|
2022-11-11 09:14:16 +00:00
|
|
|
|
Lorsque l'on souhaite faire entrer notre processus courant ou notre *thread*
|
|
|
|
|
dans un nouvel espace de noms, on peut utiliser l'appel système
|
2021-10-31 19:51:17 +00:00
|
|
|
|
`unshare(2)`. Celui-ci prend uniquement en argument une liste de *flags* des
|
|
|
|
|
*namespace*s dont on souhaite se dissocier.
|
2015-10-07 01:45:39 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
::::: {.warning}
|
2022-11-11 09:14:16 +00:00
|
|
|
|
|
|
|
|
|
**Le comportement de `unshare(2)` (ou `unshare(3)`) avec les *namespace*s *PID*
|
|
|
|
|
et *Time* n'est pas celui que l'on peut attendre !**\
|
2015-10-08 01:48:26 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
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*.
|
2015-10-08 01:48:26 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
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`.
|
2016-10-20 01:17:42 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
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`.
|
2022-11-11 09:14:16 +00:00
|
|
|
|
|
2021-10-31 19:51:17 +00:00
|
|
|
|
:::::
|
2022-11-11 09:14:16 +00:00
|
|
|
|
|
|
|
|
|
Nous avons vu comment créer et nous dissocier d'un espace de nom. Maintenant
|
|
|
|
|
voyons comment en rejoindre un déjà existant.
|