tuto4: done
This commit is contained in:
parent
886197bfe8
commit
a476cd8b6d
14 changed files with 643 additions and 331 deletions
|
@ -1,4 +1,6 @@
|
||||||
SOURCES = tutorial.md mount.md namespaces.md networkns.md pidns.md mountns.md userns.md project-intro.md project-body.md project-rendu.md sondage.md
|
SOURCES_TUTO = tutorial.md setup.md cmpns.md docker-exec.md mountns.md rendu.md
|
||||||
|
SOURCES_LESSON = lesson.md mount.md namespaces.md networkns.md pidns.md userns.md
|
||||||
|
|
||||||
PANDOCOPTS = --latex-engine=xelatex \
|
PANDOCOPTS = --latex-engine=xelatex \
|
||||||
--standalone \
|
--standalone \
|
||||||
--normalize \
|
--normalize \
|
||||||
|
@ -13,10 +15,13 @@ PANDOCOPTS = --latex-engine=xelatex \
|
||||||
--include-in-header=../header.tex
|
--include-in-header=../header.tex
|
||||||
|
|
||||||
|
|
||||||
all: tutorial.pdf
|
all: lesson.pdf tutorial.pdf
|
||||||
|
|
||||||
tutorial.pdf: ${SOURCES}
|
lesson.pdf: ${SOURCES_LESSON}
|
||||||
|
pandoc ${PANDOCOPTS} -o $@ $+
|
||||||
|
|
||||||
|
tutorial.pdf: ${SOURCES_TUTO}
|
||||||
pandoc ${PANDOCOPTS} -o $@ $+
|
pandoc ${PANDOCOPTS} -o $@ $+
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
rm tutorial.pdf
|
rm lesson.pdf tutorial.pdf
|
||||||
|
|
60
tutorial/4/cmpns.md
Normal file
60
tutorial/4/cmpns.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
Comparaison de *namespace*
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Les *namespaces* d'un programme sont exposés sous forme de liens symboliques
|
||||||
|
dans le répertoire `/proc/<PID>/ns/`.
|
||||||
|
|
||||||
|
Deux programmes qui partagent un même *namespace* auront un lien vers la même
|
||||||
|
structure de données.
|
||||||
|
|
||||||
|
Écrivons un script ou un programme, `cmpns`, permettant de déterminer si deux
|
||||||
|
programmes s'exécutent dans les mêmes *namespaces*.
|
||||||
|
|
||||||
|
|
||||||
|
Exemples {.unnumbered}
|
||||||
|
--------
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```sh
|
||||||
|
42sh$ ./cmpns $(pgrep influxdb) $(pgrep init)
|
||||||
|
- cgroup: differ
|
||||||
|
- ipc: differ
|
||||||
|
- mnt: differ
|
||||||
|
- net: differ
|
||||||
|
- pid: differ
|
||||||
|
- user: same
|
||||||
|
- uts: same
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```sh
|
||||||
|
42sh$ ./cmpns $(pgrep init) self
|
||||||
|
- cgroup: same
|
||||||
|
- ipc: same
|
||||||
|
- mnt: same
|
||||||
|
- net: same
|
||||||
|
- pid: same
|
||||||
|
- user: same
|
||||||
|
- uts: same
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Ici, `self` fait référence au processus actuellement exécuté.
|
||||||
|
|
||||||
|
Et pourquoi pas :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```sh
|
||||||
|
42sh$ unshare -m ./cmpns $$ self
|
||||||
|
- cgroup: same
|
||||||
|
- ipc: same
|
||||||
|
- mnt: differ
|
||||||
|
- net: same
|
||||||
|
- pid: same
|
||||||
|
- user: same
|
||||||
|
- uts: same
|
||||||
|
```
|
||||||
|
</div>
|
49
tutorial/4/docker-exec.md
Normal file
49
tutorial/4/docker-exec.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
`docker exec`
|
||||||
|
=============
|
||||||
|
|
||||||
|
Après voir lu la partie concernant les *namespaces*, vous avez dû comprendre
|
||||||
|
qu'un `docker exec`, n'était donc rien de plus qu'un `nsenter(1)`.
|
||||||
|
|
||||||
|
Réécrivons, en quelques lignes, la commande `docker exec` !
|
||||||
|
|
||||||
|
Pour savoir si vous avez réussi, comparez les sorties des commandes :
|
||||||
|
|
||||||
|
- `ip address` ;
|
||||||
|
- `hostname` ;
|
||||||
|
- `mount` ;
|
||||||
|
- `pa -aux` ;
|
||||||
|
- ...
|
||||||
|
|
||||||
|
|
||||||
|
Tests {.unnumbered}
|
||||||
|
-----
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```shell
|
||||||
|
42sh$ docker run --name mywebserver -d -p 80:80 nginx
|
||||||
|
d63ceae863956f8312aca60b7a57fbcc1fdf679ae4c90c5d9455405005d4980a
|
||||||
|
42sh$ docker container inspect --format '{{ .State.Pid }}' mywebserver
|
||||||
|
234269
|
||||||
|
|
||||||
|
42sh# ./mydocker_exec mywebserver ip address
|
||||||
|
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
|
||||||
|
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||||
|
inet 127.0.0.1/8 scope host lo
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||||
|
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||||
|
inet 172.17.0.1/16 scope global eth0
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
|
||||||
|
42sh# hostname
|
||||||
|
koala.zoo.paris
|
||||||
|
42sh# ./mydocker_exec mywebserver hostname
|
||||||
|
d63ceae86395
|
||||||
|
|
||||||
|
42sh# ./mydocker_exec mywebserver mount
|
||||||
|
42sh# ./mydocker_exec mywebserver ps aux
|
||||||
|
...
|
||||||
|
```
|
||||||
|
</div>
|
14
tutorial/4/lesson.md
Normal file
14
tutorial/4/lesson.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
title: Virtualisation légère -- Linux Internals partie 2
|
||||||
|
subtitle: Support de cours
|
||||||
|
author: Pierre-Olivier *nemunaire* Mercier
|
||||||
|
institute: EPITA
|
||||||
|
date: Jeudi 2 novembre 2017
|
||||||
|
...
|
||||||
|
|
||||||
|
Le but de cette seconde partie sur les mécanismes internes du noyau va nous
|
||||||
|
permettre d'utiliser les commandes et les appels systèmes relatifs aux espaces
|
||||||
|
de noms du noyau Linux ainsi que d'appréhender la complexité des sytèmes de
|
||||||
|
fichiers.
|
||||||
|
|
||||||
|
\tableofcontents
|
|
@ -1,6 +1,6 @@
|
||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
Des particularités de `mount`
|
Des particularités de `mount` {#mount}
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
## Les points de montage
|
## Les points de montage
|
||||||
|
@ -14,7 +14,7 @@ précédent.
|
||||||
Mais avez-vous déjà essayé de monter la même partition d'un disque physique à
|
Mais avez-vous déjà essayé de monter la même partition d'un disque physique à
|
||||||
deux endroits différents de votre arborescence ?
|
deux endroits différents de votre arborescence ?
|
||||||
|
|
||||||
Si pour plein de raisons on pourrait 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'accroches dans l'arborescence : et par exemple, une partition ne
|
||||||
|
@ -22,26 +22,62 @@ 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.
|
||||||
|
|
||||||
Durant cette partie du TP, vous allez avoir besoin de la commande
|
La commande `findmnt(1)`, des
|
||||||
`findmnt(1)`. Commençons par la lancer maintenant pour se familiariser avec les
|
[`util-linux`](https://www.kernel.org/pub/linux/utils/util-linux/) nous permet
|
||||||
différents points d'accroche actuellement montés.
|
d'avoir une vision arborescente des points de montage en cours d'utilisation.
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
TARGET SOURCE FSTYPE OPTIONS
|
||||||
|
/ /dev/sda1 ext4 rw,relatime,data=ordered
|
||||||
|
├─/proc proc proc rw,nosuid,nodev,noexec,relatime
|
||||||
|
├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime
|
||||||
|
│ ├─/sys/kernel/security securityfs securityfs rw,nosuid,nodev,noexec,relatime
|
||||||
|
│ ├─/sys/firmware/efi/efivars efivarfs efivarfs ro,relatime
|
||||||
|
│ └─/sys/fs/cgroup cgroup_root tmpfs rw,nosuid,nodev,noexec,relatime,size=10240k,mode=755
|
||||||
|
│ ├─/sys/fs/cgroup/unified none cgroup2 rw,nosuid,nodev,noexec,relatime
|
||||||
|
│ ├─/sys/fs/cgroup/cpuset cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset
|
||||||
|
│ ├─/sys/fs/cgroup/cpu cpu cgroup rw,nosuid,nodev,noexec,relatime,cpu
|
||||||
|
│ ├─/sys/fs/cgroup/cpuacct cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpuacct
|
||||||
|
│ ├─/sys/fs/cgroup/blkio blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio
|
||||||
|
│ ├─/sys/fs/cgroup/memory memory cgroup rw,nosuid,nodev,noexec,relatime,memory
|
||||||
|
│ ├─/sys/fs/cgroup/devices devices cgroup rw,nosuid,nodev,noexec,relatime,devices
|
||||||
|
│ ├─/sys/fs/cgroup/freezer freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer
|
||||||
|
│ ├─/sys/fs/cgroup/net_cls net_cls cgroup rw,nosuid,nodev,noexec,relatime,net_cls
|
||||||
|
│ ├─/sys/fs/cgroup/perf_event perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event
|
||||||
|
│ ├─/sys/fs/cgroup/net_prio net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_prio
|
||||||
|
│ └─/sys/fs/cgroup/pids pids cgroup rw,nosuid,nodev,noexec,relatime,pids
|
||||||
|
├─/dev devtmpfs devtmpfs rw,nosuid,size=10240k,nr_inodes=486250,mode=755
|
||||||
|
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000
|
||||||
|
│ ├─/dev/shm tmpfs tmpfs rw
|
||||||
|
│ └─/dev/mqueue mqueue mqueue rw,nosuid,nodev,noexec,relatime
|
||||||
|
├─/home /dev/sda3 ext4 rw,nosuid,nodev,relatime,data=ordered
|
||||||
|
├─/run tmpfs tmpfs rw,nosuid,nodev,noexec,mode=755
|
||||||
|
└─/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,relatime
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
## `bind`
|
## `bind`
|
||||||
|
|
||||||
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* : `mount --bind olddir newdir`.
|
utilise le *bind mount* :
|
||||||
|
|
||||||
Lorsque l'on utilise des `chroot`s sur un système complet (par exemple lorsqu'on
|
<div lang="en-US">
|
||||||
l'installe ou qu'on le répare via un live CD), il est nécessaire de dupliquer
|
```
|
||||||
les points de montage de `/dev`, `/proc` et `/sys`.
|
mount --bind olddir newdir
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
Reprenons l'environnement que nous avons créé au précédent TP. Sans monter ces
|
Lorsque l'on souhaite `chroot` dans un système complet (par exemple lorsqu'on
|
||||||
partitions, vous ne serez pas en mesure d'utiliser le système dans son
|
l'installe ou qu'on le répare via un *live CD*), il est nécessaire de dupliquer
|
||||||
intégralité : vous ne pourrez pas monter les partitions indiquées par le
|
certains points de montage, tels que `/dev`, `/proc` et `/sys`.
|
||||||
`/etc/fstab`, vous ne pourrez pas utiliser `top` ou `ps`, ...
|
|
||||||
|
|
||||||
Pour que tout cela fonctionne, vous avez besoin au préalable d'exécuter les
|
Sans monter ces partitions, vous ne serez pas en mesure d'utiliser le système
|
||||||
|
dans son intégralité : vous ne pourrez pas monter les partitions indiquées par
|
||||||
|
le `/etc/fstab`, vous ne pourrez pas utiliser `top` ou `ps`, `sysctl` ne pourra
|
||||||
|
pas accorder les paramètres du noyau, ...
|
||||||
|
|
||||||
|
Pour que tout cela fonctionne, nous aurons besoin, au préalable, d'exécuter les
|
||||||
commandes suivantes :
|
commandes suivantes :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
|
@ -53,12 +89,12 @@ commandes suivantes :
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
En se `chroot`ant à nouveau dans cette nouvelle racine, nous voyons que tous
|
En se `chroot`ant à nouveau dans cette nouvelle racine, tous nos outils
|
||||||
nos outils fonctionnent comme prévu.
|
fonctionneront comme prévu.
|
||||||
|
|
||||||
Tous ? ... en fait non. Si l'on jette un œil à `findmnt(1)`, nous constatons
|
Tous ? ... en fait non. Si l'on jette un œil à `findmnt(1)`, nous constatons
|
||||||
par exemple que `/sys/fs/cgroup` dans notre nouvelle racine est vide, alors que
|
par exemple que `/sys/fs/cgroup` dans notre nouvelle racine est vide, alors que
|
||||||
celui de notre machine hôte contient bien les répertoires de nos CGroups.
|
celui de notre machine hôte contient bien les répertoires de nos *cgroups*.
|
||||||
|
|
||||||
`--bind` va se contenter d'attacher le système de fichiers (ou au moins une
|
`--bind` va se contenter d'attacher le système de fichiers (ou au moins une
|
||||||
partie de celui-ci) à un autre endroit, sans se préoccuper des points de
|
partie de celui-ci) à un autre endroit, sans se préoccuper des points de
|
||||||
|
@ -79,25 +115,22 @@ correct de lancer :
|
||||||
|
|
||||||
## Les montages parfumés
|
## Les montages parfumés
|
||||||
|
|
||||||
On distingue 4 variétés de répercution des montages pour un sous-arbre :
|
On distingue quatre variétés de répercution des montages pour un sous-arbre :
|
||||||
partagé, esclave, privé et non-attachable.
|
partagé, esclave, privé et non-attachable.
|
||||||
|
|
||||||
Il s'agit d'agir sur la manière dont seront propagées les nouvelles accroches
|
Chacun va agir sur la manière dont seront propagées les nouvelles accroches au
|
||||||
au sein d'un système de fichiers attaché à plusieurs endroit.
|
sein d'un système de fichiers attaché à plusieurs endroits.
|
||||||
|
|
||||||
|
|
||||||
### partagé -- *shared mount*
|
### partagé -- *shared mount*
|
||||||
|
|
||||||
Une nouvelle accroche sera propagée parmi tous les systèmes de fichiers de ce
|
Dans une montage partagé, une nouvelle accroche sera propagée parmi tous les
|
||||||
partage (on parle de *peer group*).
|
systèmes de fichiers de ce partage (on parle de *peer group*).
|
||||||
|
|
||||||
Essayons de voir à quoi cela correspond avec l'exemple suivant :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
# Création de nos répertoires de travail
|
# Création de nos répertoires de travail
|
||||||
cd /mnt
|
mkdir /mnt/test-shared
|
||||||
mkdir test-shared
|
|
||||||
|
|
||||||
# On s'assure que le dossier que l'on va utiliser pour nos tests utilise bien la politique shared
|
# On s'assure que le dossier que l'on va utiliser pour nos tests utilise bien la politique shared
|
||||||
mount --make-shared /tmp
|
mount --make-shared /tmp
|
||||||
|
@ -118,20 +151,19 @@ mount -t tmpfs none /mnt/test-shared/toto
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Un coup de `findmnt` nous montre l'existence de deux nouveaux points de
|
Un coup de `findmnt` nous montre l'existence de deux nouveaux points de
|
||||||
montages. À `/mnt/test-shared/toto`, mais également à `/tmp/toto`.
|
montage. À `/mnt/test-shared/toto`, mais également à `/tmp/toto`.
|
||||||
|
|
||||||
|
|
||||||
### esclave -- *slave mount*
|
### esclave -- *slave mount*
|
||||||
|
|
||||||
De la même manière que lorsque la propagation est partagée, cette politique
|
De la même manière que lorsque la propagation est partagée, cette politique
|
||||||
propagera, mais seulement dans un sens. Le point de montage déclaré comme
|
propagera, mais seulement dans un sens. Le point de montage déclaré comme
|
||||||
esclave ne propagera pas ses nouveaux points de montage.
|
esclave ne propagera pas ses nouveaux points de montage à son *maître*.
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
# Suite de l'exemple précédent
|
# Suite de l'exemple précédent
|
||||||
cd /mnt
|
cd /mnt/test-slave
|
||||||
mkdir test-slave
|
|
||||||
|
|
||||||
# Duplication de l'accroche, sans s'occuper des éventuels sous-accroches
|
# Duplication de l'accroche, sans s'occuper des éventuels sous-accroches
|
||||||
mount --bind /mnt/test-shared /mnt/test-slave
|
mount --bind /mnt/test-shared /mnt/test-slave
|
||||||
|
@ -164,9 +196,10 @@ Le nouveau point de montage n'est pas propagé dans `/mnt/test-shared/bar`.
|
||||||
|
|
||||||
### privé -- *private mount*
|
### privé -- *private mount*
|
||||||
|
|
||||||
C'est le mode le plus simple : ici les points de montage ne sont pas propagés.
|
C'est le mode le plus simple : ici les points de montage ne sont tout
|
||||||
|
simplement pas propagés.
|
||||||
|
|
||||||
Pour forcer un point d'accroche a ne pas propager et à ne pas recevoir de
|
Pour forcer un point d'accroche à ne pas propager et à ne pas recevoir de
|
||||||
propagation, on utilise l'option suivante :
|
propagation, on utilise l'option suivante :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
|
@ -198,9 +231,9 @@ mount --bind /mnt/test-slave /mnt/test-unbindable
|
||||||
|
|
||||||
### Parfums récursifs
|
### Parfums récursifs
|
||||||
|
|
||||||
Les options que nous venons de voir s'applique 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
|
||||||
contenu dans le sous-arbre :
|
contenus dans leur sous-arbre :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
|
@ -215,9 +248,10 @@ mount --make-runbindable mountpoint
|
||||||
## `bind` de dossiers et de fichiers
|
## `bind` 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. Il peut parfaitement pointer sur un dossier, et
|
pointe sur un point de montage (c'est-à-dire, dans la plupart des cas : une
|
||||||
même sur un simple fichier, à la manière d'un *hardlink*, mais que l'on
|
partition ou un système de fichiers virtuel). Il peut parfaitement pointer sur
|
||||||
pourrait faire entre plusieurs partition et qui ne persisterait pas au
|
un dossier, et même sur un simple fichier, à la manière d'un *hardlink*, mais
|
||||||
|
que l'on pourrait faire entre plusieurs partitions et qui ne persisterait pas au
|
||||||
redémarrage.
|
redémarrage.
|
||||||
|
|
||||||
Nous verrons dans la partie *namespace* réseau, une utilisation d'attache sur
|
Nous verrons dans la partie *namespace* réseau, une utilisation d'attache sur
|
||||||
|
@ -226,11 +260,11 @@ un fichier.
|
||||||
|
|
||||||
## Déplacer un point de montage
|
## Déplacer un point de montage
|
||||||
|
|
||||||
À tout moment, il est possible réorganiser les points de montage, en les
|
À tout moment, il est possible de réorganiser les points de montage, en les
|
||||||
déplaçant. Comme cela se fait sans démonter de partition, il est possible de le
|
déplaçant. Comme cela se fait sans démonter de partition, il est possible de le
|
||||||
faire même si un fichier est en cours d'utilisation. Il faut cependant veiller
|
faire même si un fichier est en cours d'utilisation. Il faut cependant veiller
|
||||||
à ce que les programmes suceptibles d'aller chercher un fichier à l'ancien
|
à ce que les programmes susceptibles d'aller chercher un fichier à l'ancien
|
||||||
emplacement soit prévenu du changement.
|
emplacement soient prévenu du changement.
|
||||||
|
|
||||||
On utilise pour cela l'option `--move` de `mount(8)` :
|
On utilise pour cela l'option `--move` de `mount(8)` :
|
||||||
|
|
||||||
|
@ -249,13 +283,14 @@ mount --move /dev /newroot/dev
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Il est courant de faire appel à cette option lorsque l'on souhaite changer la
|
Il est courant de faire appel à cette option lorsque l'on souhaite changer la
|
||||||
racine de notre système de fichier : passer de l'initramfs au système au
|
racine de notre système de fichiers: par exemple pour passer de l'*initramfs* au
|
||||||
booter, de notre système hôte au système d'un conteneur, ...
|
système démarré, de notre système hôte au système d'un conteneur, ...
|
||||||
|
|
||||||
|
|
||||||
## Aller plus loin
|
## Aller plus loin
|
||||||
|
|
||||||
Voici quelques articles qui valent de détour :
|
Voici quelques articles qui valent le détour, en lien avec les points de
|
||||||
|
montage :
|
||||||
|
|
||||||
* [Shared subtree](https://lwn.net/Articles/159077) et la
|
* [Shared subtree](https://lwn.net/Articles/159077) et la
|
||||||
[documentation du noyau associée](https://kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) ;
|
[documentation du noyau associée](https://kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) ;
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
Le *namespace* `mount`
|
Le *namespace* `mount`
|
||||||
======================
|
======================
|
||||||
|
|
||||||
## Introduction
|
L'espace de noms `mount` permet d'isoler la vision du système de fichiers
|
||||||
|
qu'ont un processus et ses fils.
|
||||||
Le *namespace* `mount`, comme nous l'avons vu au chapitre précédent, permet
|
|
||||||
d'isoler la vision du système de fichiers qu'à 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 ?
|
||||||
|
|
||||||
|
@ -42,8 +40,8 @@ Si vous n'avez pas de partition à disposition, vous pouvez utiliser un `tmpfs`
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
mkdir /mnt/newroot
|
42sh# mkdir /mnt/newroot
|
||||||
mount -t tmpfs none /mnt/newroot
|
42sh# mount -t tmpfs none /mnt/newroot
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -76,7 +74,7 @@ Isolons-nous :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
unshare -p -m -f --mount-proc
|
42sh# unshare -p -m -f --mount-proc
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -92,7 +90,7 @@ comme esclave :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
mount --make-rslave /
|
42sh# mount --make-rslave /
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -119,12 +117,13 @@ 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ées
|
`pivot_root(2)`, requiert de notre part que nous ayons préalablement déplacé
|
||||||
les partitions systèmes à leur place dans la nouvelle racine.
|
les partitions systèmes à leur place dans la nouvelle racine.
|
||||||
|
|
||||||
L'appel de la commande, qui prend en argument le chemin de la nouvelle racine
|
L'appel de la commande sert à intervertir les deux racines ; elle prend en argument :
|
||||||
et le chemin dans la nouvelle racine où placer l'ancienne, va donc intervertir
|
|
||||||
les deux racines.
|
* le chemin de la nouvelle racine,
|
||||||
|
* le chemin dans la nouvelle racine où placer l'ancienne.
|
||||||
|
|
||||||
Une fois le pivot effectué, on peut démonter l'ancienne racine.
|
Une fois le pivot effectué, on peut démonter l'ancienne racine.
|
||||||
|
|
||||||
|
@ -133,6 +132,6 @@ par :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
exec chroot / command
|
42sh# exec chroot / command
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,53 +1,77 @@
|
||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
Les *namespaces*
|
Les espaces de noms -- *namespaces* {#namespaces}
|
||||||
================
|
===================================
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Les espaces de noms du noyau, les *namespaces*, permettent de dupliquer
|
Les espaces de noms du noyau, les *namespaces*, permettent de
|
||||||
certaines structures du noyau, dans le but de les isoler d'un groupe de
|
dupliquer certaines structures, habituellement considérées uniques
|
||||||
processus à un autre.
|
pour le noyau, dans le but de les isoler d'un groupe de processus à un
|
||||||
|
autre.
|
||||||
|
|
||||||
On en dénombre 7 depuis Linux 4.6 : `CGroup`, `IPC`, `network`, `mount`, `PID`,
|
On en dénombre sept depuis Linux 4.6 : `cgroup`, `IPC`, `network`,
|
||||||
`user` et `UTS`.
|
`mount`, `PID`, `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](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/179825/).
|
||||||
|
|
||||||
### `mount` *namespaces*
|
### L'espace de noms `mount` {#mount-ns}
|
||||||
|
|
||||||
Depuis Linux 2.4.19.
|
Depuis Linux 2.4.19.
|
||||||
|
|
||||||
Isole la liste des points de montage.
|
Cet espace de noms isole la liste des points de montage.
|
||||||
|
|
||||||
Chaque processus d'un *namespace* différent peut monter, démonter et
|
Chaque processus appartenant à un *namespace* différent peut monter, démonter
|
||||||
réorganiser à sa guise les points de montage. Une partition ne sera donc pas
|
et réorganiser à sa guise les points de montage, sans que cela n'ait d'impact
|
||||||
nécessairement démonté après un appel à `umount(2)`, elle le sera lorsqu'elle
|
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* dans lequel elle était
|
aura effectivement été démontée de chaque *namespace* dans lequel elle était
|
||||||
montée.
|
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.
|
||||||
|
|
||||||
### `UTS` *namespaces*
|
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.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mount --make-rslave /
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### L'espace de noms `UTS` {#uts-ns}
|
||||||
|
|
||||||
Depuis Linux 2.6.19.
|
Depuis Linux 2.6.19.
|
||||||
|
|
||||||
Isole le nom de machine et son domaine NIS.
|
Cet espace de noms isole le nom de machine et son domaine NIS.
|
||||||
|
|
||||||
|
|
||||||
### `IPC` *namespaces*
|
### L'espace de noms `IPC` {#ipc-ns}
|
||||||
|
|
||||||
Depuis Linux 2.6.19.
|
Depuis Linux 2.6.19.
|
||||||
|
|
||||||
Isole les objets IPC et les files de messages POSIX.
|
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
|
Une fois le *namespace* attaché à un processus, il ne peut alors plus parler
|
||||||
qu'avec les autres processus de son *namespace*.
|
qu'avec les autres processus de son espace de noms (lorsque ceux-ci passent par
|
||||||
|
l'API IPC du noyau).
|
||||||
|
|
||||||
|
|
||||||
### `PID` *namespaces*
|
### L'espace de noms `PID`
|
||||||
|
|
||||||
Depuis Linux 2.6.24.
|
Depuis Linux 2.6.24.
|
||||||
|
|
||||||
Isole la liste des processus et virtualise leurs numéros.
|
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
|
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
|
également attachés à son espace. Il s'agit d'un sous-ensemble de l'arbre global
|
||||||
|
@ -55,22 +79,22 @@ de PID : les processus de tous les PID *namespaces* apparaissent donc dans
|
||||||
l'arbre initial.
|
l'arbre initial.
|
||||||
|
|
||||||
Pour chaque nouvel espace de noms de processus, une nouvelle numérotation est
|
Pour chaque nouvel espace de noms de processus, une nouvelle numérotation est
|
||||||
initié ; ainsi, le premier processus de cet espace porte le numéro 1 et aura
|
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
|
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 est rendu orphelin dans ce *namespace*, il devient un fils de ce
|
||||||
processus, et non un fils de l'`init` de l'arbre global.
|
processus, et non un fils de l'`init` de l'arbre global.
|
||||||
|
|
||||||
|
|
||||||
### `network` *namespaces*
|
### L'espace de nom `network`
|
||||||
|
|
||||||
Depuis Linux 2.6.29.
|
Depuis Linux 2.6.29.
|
||||||
|
|
||||||
Fourni une isolation pour toutes les ressources associées aux réseaux : les
|
Cet espace de noms fournit une isolation pour toutes les ressources associées
|
||||||
interfaces, les piles protocolaires IPv4 et IPv6, les tables de routage,
|
aux réseaux : les interfaces, les piles protocolaires IPv4 et IPv6, les tables
|
||||||
pare-feu, ports numérotés, etc.
|
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
|
Une interface réseau (`eth0`, `wlan0`, ...) ne peut se trouver que dans un seul
|
||||||
*namespace* à la fois, mais il est possible de les déplacer.
|
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
|
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
|
attaché à cet espace de noms se termine), les interfaces qui le composent sont
|
||||||
|
@ -78,30 +102,32 @@ ramenées dans l'espace initial (et non pas dans l'espace parent, en cas
|
||||||
d'imbrication).
|
d'imbrication).
|
||||||
|
|
||||||
|
|
||||||
### `user` *namespaces*
|
### L'espace de noms `user`
|
||||||
|
|
||||||
Depuis Linux 3.8.
|
Depuis Linux 3.8.
|
||||||
|
|
||||||
Isole la liste des utilisateurs, des groupes, leurs identifiants, les
|
Cet espace de noms isole la liste des utilisateurs, des groupes, leurs
|
||||||
capabilities, la racine et le trousseau de clefs du noyau.
|
identifiants, les *capabilities*, la racine et le trousseau de clefs du noyau.
|
||||||
|
|
||||||
La principale caractéristique est que les identifiants d'utilisateur et de
|
La principale caractéristique est que les identifiants d'utilisateur et de
|
||||||
groupe pour un processus peuvent être différent entre l'intérieur et
|
groupe pour un processus peuvent être différents entre l'intérieur et
|
||||||
l'extérieur du conteneur. Il est alors possible, alors que l'on est un simple
|
l'extérieur de l'espace de noms. Il est donc possible, alors que l'on est un
|
||||||
utilisateur à l'extérieur du *namespace*, d'avoir l'UID 0 dans le conteneur.
|
simple utilisateur à l'extérieur du *namespace*, d'avoir l'UID 0 dans le
|
||||||
|
conteneur.
|
||||||
|
|
||||||
|
|
||||||
### `CGroup` *namespaces*
|
### L'espace de noms `cgroup` {#cgroup-ns}
|
||||||
|
|
||||||
Depuis Linux 4.6.
|
Depuis Linux 4.6.
|
||||||
|
|
||||||
Isole la vue de la racine des *Control Group* en la plaçant sur un
|
Cet espace de noms filtre l'arborescence des *Control Group* en changeant la
|
||||||
sous-groupe de l'arborescence.
|
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
|
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
|
des sous-groupes parents (pouvant laisser fuiter des informations sur le reste
|
||||||
du système). Cela peut également permettre de faciliter la migration de
|
du système). Cela peut également permettre de faciliter la migration de
|
||||||
processus (d'un système à un autre) : l'arborescences des cgroups n'a alors
|
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.
|
plus d'importance car le processus ne voit que son groupe.
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,14 +138,14 @@ plus d'importance car le processus ne voit que son groupe.
|
||||||
|
|
||||||
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
|
||||||
nécessaire pour appeler l'appel système `unshare(2)`, puis, tout comme
|
nécessaire pour lancer l'appel système `unshare(2)`, puis, tout comme
|
||||||
`chroot(1)`, exécuter le programme passé en paramètre.
|
`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
|
En fonction des options qui lui sont passées, `unshare(1)` va créer le/les
|
||||||
nouveaux *namespaces* et placer le processus dedans.
|
nouveaux *namespaces* et placer le processus dedans.
|
||||||
|
|
||||||
Par exemple, nous pouvons modifier sans crainte le nom de notre machine, si
|
Par exemple, nous pouvons modifier sans crainte le nom de notre machine, si
|
||||||
nous sommes passé dans un autre *namespace* `UTS` :
|
nous sommes passés dans un autre *namespace* `UTS` :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
|
@ -137,13 +163,9 @@ koala.zoo.paris
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Nous avons pu ici modifier le nom de machine, sans que cela n'affecte notre
|
Nous avons pu ici modifier le nom de la machine, sans que cela n'affecte notre
|
||||||
machine hôte.
|
machine hôte.
|
||||||
|
|
||||||
Essayons maintenant avec d'autres options de notre programme pour voir les
|
|
||||||
effets produits : par exemple, comparons un `ip address` à l'extérieur et à
|
|
||||||
l'intérieur d'un `unshare -n`.
|
|
||||||
|
|
||||||
|
|
||||||
### Les appels systèmes
|
### Les appels systèmes
|
||||||
|
|
||||||
|
@ -166,8 +188,9 @@ avec d'autres `flags` attendu par la fonction.
|
||||||
|
|
||||||
Les mêmes `flags` sont utilisés lors des appels à `unshare(2)` ou `setns(2)`.
|
Les mêmes `flags` sont utilisés lors des appels à `unshare(2)` ou `setns(2)`.
|
||||||
|
|
||||||
Pour créer un nouveau processus qui sera à la fois dans un nouvel *namespace*
|
Pour créer un nouveau processus qui sera à la fois dans un nouvel espace de
|
||||||
réseau et dans un nouveau *namespace* CGroup, on écrirait un code similaire à :
|
noms réseau et dans un nouveau *namespace* `cgroup`, on écrirait un code
|
||||||
|
similaire à :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```c
|
```c
|
||||||
|
@ -188,67 +211,10 @@ pid_t pid = clone(do_execvp,
|
||||||
Le premier argument est un pointeur sur fonction. Il s'agit de la fonction qui
|
Le premier argument est un pointeur sur fonction. Il s'agit de la fonction qui
|
||||||
sera appelée par le nouveau processus.
|
sera appelée par le nouveau processus.
|
||||||
|
|
||||||
## Comparaison de *namespace*
|
|
||||||
|
|
||||||
Les *namespaces* d'un programme sont exposés sous forme de liens symboliques
|
|
||||||
dans le répertoire `/proc/<PID>/ns/`.
|
|
||||||
|
|
||||||
Deux programmes qui partagent un même *namespace* auront un lien vers la même
|
|
||||||
structure de données.
|
|
||||||
|
|
||||||
Écrivons un script ou un programme, `cmpns`, permettant de déterminer si deux
|
|
||||||
programmes s'exécutent dans les mêmes *namespaces*.
|
|
||||||
|
|
||||||
|
|
||||||
### Exemples
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```sh
|
|
||||||
42sh$ ./cmpns $(pgrep influxdb) $(pgrep init)
|
|
||||||
- cgroup: differ
|
|
||||||
- ipc: differ
|
|
||||||
- mnt: differ
|
|
||||||
- net: differ
|
|
||||||
- pid: differ
|
|
||||||
- user: same
|
|
||||||
- uts: same
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```sh
|
|
||||||
42sh$ ./cmpns $(pgrep init) self
|
|
||||||
- cgroup: same
|
|
||||||
- ipc: same
|
|
||||||
- mnt: same
|
|
||||||
- net: same
|
|
||||||
- pid: same
|
|
||||||
- user: same
|
|
||||||
- uts: same
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Ici, `self` fait référence au processus actuellement exécuté.
|
|
||||||
|
|
||||||
Et pourquoi pas :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
|
||||||
```sh
|
|
||||||
42sh$ unshare -m ./cmpns $$ self
|
|
||||||
- cgroup: same
|
|
||||||
- ipc: same
|
|
||||||
- mnt: differ
|
|
||||||
- net: same
|
|
||||||
- pid: same
|
|
||||||
- user: same
|
|
||||||
- uts: same
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
## Rejoindre un *namespace*
|
## Rejoindre un *namespace*
|
||||||
|
|
||||||
Rejoindre un *namespace* se fait en utilisant l'appel système `setns(2)`,
|
Rejoindre un espace de noms se fait en utilisant l'appel système `setns(2)`,
|
||||||
auquel on passe le *file descriptor* d'un des liens du dossier
|
auquel on passe le *file descriptor* d'un des liens du dossier
|
||||||
`/proc/<PID>/ns/` :
|
`/proc/<PID>/ns/` :
|
||||||
|
|
||||||
|
@ -293,31 +259,15 @@ Dans un shell, on utilisera la commande `nsenter(1)` :
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
### `docker exec`
|
## Durée de vie d'un *namespace* {#ns-lifetime}
|
||||||
|
|
||||||
Si vous avez bien suivi jusque là, vous avez dû comprendre qu'un `docker exec`,
|
Le noyau tient à jour un compteur de références pour chaque *namespace*. Dès
|
||||||
n'était donc rien de plus qu'un `nsenter(1)`.
|
qu'une référence tombe à 0, l'espace de noms est automatiquement libéré, les
|
||||||
|
|
||||||
Réécrivons, en quelques lignes, la commande `docker exec` !
|
|
||||||
|
|
||||||
Pour savoir si vous avez réussi, comparez les sorties des commandes :
|
|
||||||
|
|
||||||
- `ip address` ;
|
|
||||||
- `hostname` ;
|
|
||||||
- `mount` ;
|
|
||||||
- `pa -aux` ;
|
|
||||||
- ...
|
|
||||||
|
|
||||||
|
|
||||||
## Durée de vie d'un *namespace*
|
|
||||||
|
|
||||||
Le noyau tient à jour un compteur de référence pour chaque *namespace*. Dès
|
|
||||||
qu'une référence tombe à 0, le *namespace* est automatiquement libéré, les
|
|
||||||
points de montage sont démontés, les interfaces réseaux sont réattribués à
|
points de montage sont démontés, les interfaces réseaux sont réattribués à
|
||||||
l'espace de noms initial, ...
|
l'espace de noms initial, ...
|
||||||
|
|
||||||
Ce compteur évolue selon plusieurs critères, et principalement selon le nombre
|
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
|
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
|
*namespace* est libéré lorsque le dernier processus s'exécutant dedans se
|
||||||
termine.
|
termine.
|
||||||
|
|
||||||
|
@ -339,7 +289,8 @@ de fichier valide vers le *namespace* (pour passer à `setns(2)`).
|
||||||
|
|
||||||
### Faire persister un *namespace*
|
### Faire persister un *namespace*
|
||||||
|
|
||||||
Il n'est pas possible de faire persister un namespace d'un reboot à l'autre.
|
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
|
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.
|
structure du noyau, qui ne persistera pas au redémarrage.
|
||||||
|
@ -349,12 +300,13 @@ structure du noyau, qui ne persistera pas au redémarrage.
|
||||||
|
|
||||||
Je vous recommande la lecture des *man* suivants :
|
Je vous recommande la lecture des *man* suivants :
|
||||||
|
|
||||||
* `namespaces(7)` : introduisant et énumérant les namespaces ;
|
* `namespaces(7)` : introduisant et énumérant les *namespaces* ;
|
||||||
|
|
||||||
Pour tout connaître en détails, [la série d'articles de Michael Kerrisk sur
|
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
|
les *namespaces*](https://lwn.net/Articles/531114/) est excellente ! Auquel il
|
||||||
faut ajouter [le petit dernier sur le CGroup
|
faut ajouter [le petit dernier sur le `cgroup`
|
||||||
*namespace*](https://lwn.net/Articles/621006/).
|
*namespace*](https://lwn.net/Articles/621006/).
|
||||||
|
|
||||||
Cet article
|
[Cet article de Michael Crosby montrant l'utilisation de clone(2)](http://crosbymichael.com/creating-containers-part-1.html)
|
||||||
[de Michael Crosby montrant l'utilisation de clone(2)](http://crosbymichael.com/creating-containers-part-1.html).
|
est également des plus intéressants, pour ce qui concerne la programmation
|
||||||
|
plus bas-niveau.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
Le *namespace* `network`
|
Le *namespace* `network` {#net-ns}
|
||||||
========================
|
========================
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
@ -10,7 +10,7 @@ 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 nom `network`, on se retrouve dans un
|
||||||
environnement qui n'a plus que l'interface de loopback :
|
environnement qui n'a plus qu'une interface de *loopback* :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
|
@ -20,9 +20,13 @@ environnement qui n'a plus que l'interface de loopback :
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Qui dit nouvelle pile réseau, dit également que les ports qui étaient assignés
|
Bien que portant le même nom que l'interface de *loopback* de notre
|
||||||
|
environnement principal, il s'agit bien de deux interfaces isolées l'une de
|
||||||
|
l'autre.
|
||||||
|
|
||||||
|
Qui dit nouvelle pile réseau, dit également que les ports qui sont assignés
|
||||||
dans l'espace principal, ne le sont plus dans le conteneur : il est donc
|
dans l'espace principal, ne le sont plus dans le conteneur : il est donc
|
||||||
possible de lancer un serveur web sans qu'il entre en conflit avec celui d'un
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,45 +35,51 @@ autre espace de noms.
|
||||||
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`.
|
||||||
|
|
||||||
Tout d'abord, nous allons créer un nouvel espace de nom :
|
Nous pouvons tout d'abord créer un nouvel espace de nom :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
42sh$ ip netns add virli
|
42sh# ip netns add virli
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
La technique utilisée ici pour avoir des *namespaces* nommés est la même que
|
La technique utilisée ici pour avoir des *namespaces* nommés est la même que
|
||||||
celle que nous avons vu dans la première partie sur les *namespaces* : via un
|
celle que nous avons vue dans
|
||||||
`mount --bind` dans le dossier `/var/run/netns/`. Cela permet de faire
|
[la première partie sur les *namespaces*](#ns-lifetime) : via un `mount --bind`
|
||||||
persister le namespace malgré le fait que plus aucun processus ne s'y exécute.
|
dans le dossier `/var/run/netns/`. Cela permet de faire persister le namespace
|
||||||
|
malgré le fait que plus aucun processus ne s'y exécute.
|
||||||
|
|
||||||
Maintenant que notre *namespace* est créé, voyons s'il contient des interfaces :
|
Maintenant que notre *namespace* est créé, nous pouvons regarder s'il contient
|
||||||
|
des interfaces :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```sh
|
```sh
|
||||||
42sh$ ip netns exec virli ip link
|
42sh# ip netns exec virli ip link
|
||||||
|
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
|
||||||
|
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Cette commande ne devrait vous montrer que l'interface de *loopback*, car nous
|
Cette commande ne nous montre que l'interface de *loopback*, car nous n'avons
|
||||||
n'avons pour l'instant pas encore attaché la moindre interface.
|
pour l'instant pas encore attaché la moindre interface.
|
||||||
|
|
||||||
D'ailleurs, cette interface est rapportée comme étant désactivée, activons-là
|
D'ailleurs, cette interface est rapportée comme étant désactivée, activons-là
|
||||||
via la commande :
|
via la commande :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
42sh$ ip netns exec virli ip link set dev lo up
|
42sh# ip netns exec virli ip link set dev lo up
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Si tout se passe bien, vous devriez maintenant pouvoir lancer un `ping` sur
|
À ce stade, nous pouvons déjà commencer à lancer un `ping` sur cette interface:
|
||||||
cette interface :
|
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
42sh$ ip netns exec virli ping 127.0.0.1
|
42sh# ip netns exec virli ping 127.0.0.1
|
||||||
|
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
|
||||||
|
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.038 ms
|
||||||
|
...
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -86,18 +96,18 @@ type `veth` :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
ip link add veth0 type veth peer name veth1
|
42sh# ip link add veth0 type veth peer name veth1
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Une interface `veth` se comporte comme un tube bidirectionnel : tout ce qui
|
Une interface `veth` se comporte comme un tube bidirectionnel : tout ce qui
|
||||||
entre d'un côté sort de l'autre et inversement. La commande précédente a donc
|
entre d'un côté sort de l'autre et inversement. La commande précédente a donc
|
||||||
créé deux interfaces `veth0` et `veth1` : les paquets envoyés sur `veth0` sont
|
créé deux interfaces `veth0` et `veth1` : les paquets envoyés sur `veth0` sont
|
||||||
donc reçu 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é dans une 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 paquet 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` :
|
||||||
|
|
||||||
|
@ -107,7 +117,7 @@ Pour déplacer `veth1` dans notre *namespace* `virli` :
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Il ne reste plus maintenant qu'à assigner une IP à chacune des interfaces :
|
Il ne reste maintenant plus qu'à assigner une IP à chacune des interfaces :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
|
@ -116,7 +126,10 @@ Il ne reste plus maintenant qu'à assigner une IP à chacune des interfaces :
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Testons maintenant que la communication entre les deux passe bient :
|
Dès lors[^linkdown], nous pouvons `ping`er chaque extrêmité :
|
||||||
|
|
||||||
|
[^linkdown]: Il peut être nécessaire d'activer chaque lien, via `ip link set
|
||||||
|
vethX up`.
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
|
@ -127,21 +140,21 @@ Testons maintenant que la communication entre les deux passe bient :
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Il ne reste donc pas grand chose à faire pour fournir Internet à notre
|
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 interface *veth* est plutôt simple et disponible
|
couches du noyau. Utiliser les interfaces *veth* est plutôt simple et disponible
|
||||||
partout, mais c'est loin d'être la technique la plus rapide ou la moins
|
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 switch les
|
Il est possible d'attribuer juste une interface de VLAN, si l'on a switch
|
||||||
supportant.
|
supportant la technologie [802.1q](https://fr.wikipedia.org/wiki/IEEE_802.1Q).
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
|
@ -154,40 +167,64 @@ supportant.
|
||||||
|
|
||||||
### MACVLAN
|
### MACVLAN
|
||||||
|
|
||||||
Lorsque l'on a pas assez de carte ethernet et que le switch ne supporte pas les
|
<!-- https://hicu.be/bridge-vs-macvlan -->
|
||||||
VLAN, le noyau met à disposition un routage basé sur les adresses MAC : le
|
|
||||||
|
Lorsque l'on n'a pas assez de carte ethernet et que le switch ne supporte pas
|
||||||
|
les VLAN, le noyau met à disposition un routage basé sur les adresses MAC : le
|
||||||
MACVLAN. S'il est activé dans votre noyau, vous allez avoir le choix entre l'un
|
MACVLAN. S'il est activé dans votre noyau, vous allez avoir le choix entre l'un
|
||||||
des deux modes : VEPA ou *bridge*.
|
des quatre modes : *private*, VEPA, *bridge* ou *passthru*.
|
||||||
|
|
||||||
|
Quelque soit le mode choisi, les paquets en provenance d'autres machines et à
|
||||||
|
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
|
||||||
|
interfaces.
|
||||||
|
|
||||||
#### VEPA
|
#### VEPA
|
||||||
|
|
||||||
Dans ce mode, tous les paquets sortant sont directement envoyé sur l'interface
|
Dans ce mode, tous les paquets sortants sont directement envoyés sur
|
||||||
ethernet de sortie, sans qu'aucun routage préalable n'est été effectué. Ainsi,
|
l'interface Ethernet de sortie, sans qu'aucun routage préalable n'ait été
|
||||||
si un paquet est à destination d'un des autres conteneur de la machine, c'est à
|
effectué. Ainsi, si un paquet est à destination d'un des autres conteneurs de
|
||||||
l'équipement réseau derrière la machine de rerouter le paquet vers la machine
|
la machine, c'est à l'équipement réseau derrière la machine de rerouter le
|
||||||
émetrice.
|
paquet vers la machine émettrice (par exemple un switch
|
||||||
|
[802.1Qbg](http://www.ieee802.org/1/pages/802.1bg.html)).
|
||||||
|
|
||||||
Pour construire une nouvelle interface de ce type :
|
Pour construire une nouvelle interface de ce type :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
ip link add link eth0 mac0 type macvlan mode vepa
|
42sh# ip link add link eth0 mac0 type macvlan mode vepa
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
#### *Private*
|
||||||
|
|
||||||
|
À 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
|
||||||
|
pas délivré.
|
||||||
|
|
||||||
|
Dans ce mode, on est donc assuré qu'aucun conteneur ne puisse parler à un
|
||||||
|
conteneur de la même machine.
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
42sh# ip link add link eth0 mac1 type macvlan mode private
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
#### *Bridge*
|
#### *Bridge*
|
||||||
|
|
||||||
À l'inverse du mode *VEPA*, les paquets sont routés selon leur adresse MAC : si
|
À l'inverse des modes *VEPA* et *private*, les paquets sont routés selon leur
|
||||||
jamais une adresse MAC est connuee, le paquet est délivré à l'interface MACVLAN
|
adresse MAC : si jamais une adresse MAC est connue, le paquet est délivré à
|
||||||
correspondante ; dans le cas contraire, le paquet est envoyé sur l'interface de
|
l'interface MACVLAN correspondante ; dans le cas contraire, le paquet est
|
||||||
sortie.
|
envoyé sur l'interface de sortie.
|
||||||
|
|
||||||
Pour construire une nouvelle interface de ce type :
|
Pour construire une nouvelle interface de ce type :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
ip link add link eth0 mac1 type macvlan mode bridge
|
42sh# ip link add link eth0 mac2 type macvlan mode bridge
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
Le *namespace* `PID`
|
Le *namespace* `PID` {#pid-ns}
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
## Introduction
|
## Introduction {#pid-ns-intro}
|
||||||
|
|
||||||
Le *namespace* `PID` est celui qui va nous permettre d'isoler un sous-arbre de
|
L'espace de noms `PID` est celui qui va nous permettre d'isoler un sous-arbre
|
||||||
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é
|
||||||
comme l'`init`.
|
comme l'`init`.
|
||||||
|
|
||||||
Contrairement aux autres *namespaces* où l'on peut demander à se séparer du
|
Contrairement aux autres *namespaces* où l'on peut demander à se séparer du
|
||||||
*namespace* en question à n'importe quel moment de l'exécution du processus,
|
*namespace* en question à n'importe quel moment de l'exécution du processus,
|
||||||
via `unshare(2)` ou `setns(2)` par exemple, dans le cas du *namespace* PID, le
|
via `unshare(2)` ou `setns(2)` par exemple, ici, le changement n'est valable
|
||||||
changement n'est valable qu'après le prochain `fork(2)` (ou similaire). Le
|
qu'après le prochain `fork(2)` (ou similaire).
|
||||||
*namespace* PID du processus courant n'est pas changé, afin que le processus ne
|
L'espace de noms PID du processus courant n'est pas changé, afin que le
|
||||||
change pas de PID en cours de route (en fonction du *namespace* dans lequel il
|
processus ne change pas de PID en cours de route (puisque fonction du
|
||||||
se trouve).
|
*namespace* dans lequel il se trouve).
|
||||||
|
|
||||||
|
|
||||||
## Isolons !
|
## Isolons !
|
||||||
|
@ -24,12 +24,12 @@ Première étape s'isoler :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
unshare --pid --fork /bin/bash
|
42sh# unshare --pid --fork /bin/bash
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Nous utilisons ici l'option `-f`, pour que le passage dans le nouvel espace de
|
Nous utilisons ici l'option `-f`, pour que le passage dans le nouvel espace de
|
||||||
noms des PID soit effectif (cf. Introduction).
|
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
|
seul processus ... pourtant, il n'en est rien, ces deux commandes continuent
|
||||||
|
@ -39,10 +39,10 @@ Cela est dû au fait que ces deux programmes, sous Linux, se basent sur le
|
||||||
contenu de `/proc`. D'ailleurs, si l'on affiche le PID du processus courant
|
contenu de `/proc`. D'ailleurs, si l'on affiche le PID du processus courant
|
||||||
`echo $$`, on obtient bien 1.
|
`echo $$`, on obtient bien 1.
|
||||||
|
|
||||||
En l'état, beaucoup d'information fuitent. Mais il n'est pas possible de monter
|
En l'état, beaucoup d'informations sont divulguées. Mais il n'est pas possible
|
||||||
le bon `/proc` car il serait également monté pour les processus de notre
|
de monter le bon `/proc` car il serait également monté pour les processus de
|
||||||
système initial. Pour s'en sortir, il est nécessaire de s'isoler du *namespace*
|
notre système initial. Pour s'en sortir, il est nécessaire de s'isoler du
|
||||||
`mount`.
|
*namespace* `mount`.
|
||||||
|
|
||||||
|
|
||||||
### Double isolation : ajout du *namespace* `mount`
|
### Double isolation : ajout du *namespace* `mount`
|
||||||
|
@ -51,7 +51,7 @@ Voici la nouvelle ligne de commande que l'on va utiliser :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```shell
|
```shell
|
||||||
unshare --pid --mount --fork --mount-proc /bin/bash
|
42sh# unshare --pid --mount --fork --mount-proc /bin/bash
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -64,16 +64,15 @@ Cette fois, `top` et `ps` nous rapportent bien que l'on est seul dans notre
|
||||||
|
|
||||||
## Arborescence à l'extérieur du *namespace*
|
## Arborescence à l'extérieur du *namespace*
|
||||||
|
|
||||||
Vous l'avez sans doute remarqué lors de votre première tentative de `top`,
|
Lors de notre première tentative de `top`, lorsque `/proc` était encore monté
|
||||||
lorsque le `/proc` était encore monté sur le procfs de l'espace initial : votre
|
sur le `procfs` de l'espace de noms initial : votre processus (au PID 1 dans
|
||||||
processus au PID 1 dans son nouveau *namespace* était présent dans
|
son nouveau *namespace*) était présent dans l'arborescence de l'espace initial
|
||||||
l'arborescence de l'espace initial avec un PID dans la continuité des autres
|
avec un PID dans la continuité des autres processus, étonnant !
|
||||||
processus, étonnant !
|
|
||||||
|
|
||||||
En fait, l'isolation consiste en une virtualisation des numéros du processus :
|
En fait, l'isolation consiste en une virtualisation des numéros du processus :
|
||||||
la plupart des processus du système intial ne sont pas accessibles, et ceux qui
|
la plupart des processus du système intial ne sont pas accessibles, et ceux qui
|
||||||
font parti de l'espace de noms créé disposent d'une nouvelle numérotation. Et
|
font partie de l'espace de noms créé disposent d'une nouvelle numérotation. Et
|
||||||
c'est cette nouvelle numérotation qui est montré au processus.
|
c'est cette nouvelle numérotation qui est montrée au processus.
|
||||||
|
|
||||||
Si l'on veut interagir avec ce processus depuis un de ses espaces de noms
|
Si l'on veut interagir avec ce processus depuis un de ses espaces de noms
|
||||||
parent, il faut le faire avec son identifiant de processus du même *namespace*
|
parent, il faut le faire avec son identifiant de processus du même *namespace*
|
||||||
|
@ -89,13 +88,13 @@ Si un processus est orphelin, il est donc affiché comme étant fils du PID 1
|
||||||
dans son *namespace* ; il n'est pas sorti de l'espace de nom.
|
dans son *namespace* ; il n'est pas sorti de l'espace de nom.
|
||||||
|
|
||||||
Lorsque l'on lance un processus via `nsenter(1)` ou `setns(2)`, cela crée un
|
Lorsque l'on lance un processus via `nsenter(1)` ou `setns(2)`, cela crée un
|
||||||
processus qui n'est sans doute pas un fils direct du processus d'init de notre
|
processus qui n'est sans doute pas un fils direct du processus d'`init` de
|
||||||
conteneur. Malgré tout, même s'il est affiché comme n'étant pas un fils à
|
notre conteneur. Malgré tout, même s'il est affiché comme n'étant pas un fils à
|
||||||
l'extérieur du conteneur, les propriétés d'init sont biens appliquées à
|
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é à ce *namespace* :
|
N'hésitez pas à jeter un œil à la page de manuel consacré à cet espace de
|
||||||
`pid_namespaces(7)`.
|
noms : `pid_namespaces(7)`.
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
## Modalité 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 les
|
Un service automatique s'occupe de réceptionner vos rendus, de faire les
|
||||||
vérifications nécessaires et de vous envoyer un accusé de réception (ou de
|
vérifications nécessaires et de vous envoyer un accusé de réception (ou de
|
||||||
rejet).
|
rejet).
|
||||||
|
|
||||||
Ce service écoute sur l'adresse <virli@nemunai.re>, c'est donc à cette adresse
|
Ce service écoute sur l'adresse <virli@nemunai.re>. C'est donc à cette adresse
|
||||||
et exclusivement à celle-ci que vous devez envoyer vos rendus. Tout rendu
|
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.
|
||||||
|
|
||||||
|
|
||||||
## Tarball
|
Tarball
|
||||||
|
-------
|
||||||
|
|
||||||
Le projet à rendre pour ce cours est à placer dans une tarball (pas d'archive
|
Le projet à rendre pour ce cours est à placer dans une tarball (pas d'archive
|
||||||
ZIP, RAR, ...).
|
ZIP, RAR, ...).
|
||||||
|
|
47
tutorial/4/rendu.md
Normal file
47
tutorial/4/rendu.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
Projet et rendu
|
||||||
|
===============
|
||||||
|
|
||||||
|
Projet
|
||||||
|
------
|
||||||
|
|
||||||
|
[Le sujet complet du projet est disponible ici](https://virli.nemunai.re/project-2.pdf). Il
|
||||||
|
n'est pas à rendre en même temps que le TP. Consultez ses modalités de rendu
|
||||||
|
pour plus d'informations.
|
||||||
|
|
||||||
|
|
||||||
|
Modalités de rendu
|
||||||
|
------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
rejet).
|
||||||
|
|
||||||
|
Ce service écoute sur l'adresse <virli@nemunai.re>. C'est donc à cette adresse
|
||||||
|
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
|
||||||
|
sera pas pris en compte.
|
||||||
|
|
||||||
|
Par ailleurs, n'oubliez pas de répondre à
|
||||||
|
[l'évaluation du cours](https://www.epitaf.fr/moodle/mod/quiz/view.php?id=42).
|
||||||
|
|
||||||
|
Afin de m'aider à améliorer ce cours, je vous invite à remplir
|
||||||
|
[ce sondage anonyme](https://www.epitaf.fr/moodle/mod/quiz/view.php?id=44).
|
||||||
|
|
||||||
|
|
||||||
|
Tarball
|
||||||
|
-------
|
||||||
|
|
||||||
|
Tous les exercices de ce TP sont à placer dans une tarball (pas d'archive ZIP,
|
||||||
|
RAR, ...).
|
||||||
|
|
||||||
|
Voici une arborescence type :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
login_x-TP4/cmpns.sh
|
||||||
|
login_x-TP4/mydocker_exec.sh
|
||||||
|
login_x-TP4/myswitch_root.sh
|
||||||
|
```
|
||||||
|
</div>
|
110
tutorial/4/setup.md
Normal file
110
tutorial/4/setup.md
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
Mise en place
|
||||||
|
=============
|
||||||
|
|
||||||
|
Noyau Linux
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Ce TP requiert un noyau Linux, dans sa version 3.12 au minimum. Il doit de plus
|
||||||
|
être compilé avec les options suivantes (lorsqu'elles sont disponibles pour
|
||||||
|
votre version) :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
General setup --->
|
||||||
|
[*] Control Group support --->
|
||||||
|
-*- Namespaces support
|
||||||
|
[*] UTS namespace
|
||||||
|
[*] IPC namespace
|
||||||
|
[*] User namespace
|
||||||
|
[*] PID Namespaces
|
||||||
|
[*] Network namespace
|
||||||
|
[*] Networking support --->
|
||||||
|
Networking options --->
|
||||||
|
<M> 802.1d Ethernet Bridging
|
||||||
|
Device Drivers --->
|
||||||
|
[*] Network device support --->
|
||||||
|
<M> MAC-VLAN support
|
||||||
|
<M> Virtual ethernet pair device
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Les variables de configuration correspondantes sont :
|
||||||
|
|
||||||
|
<div lang="en-US">
|
||||||
|
```
|
||||||
|
CONFIG_CGROUPS=y
|
||||||
|
|
||||||
|
CONFIG_NAMESPACES=y
|
||||||
|
CONFIG_UTS_NS=y
|
||||||
|
CONFIG_IPC_NS=y
|
||||||
|
CONFIG_USER_NS=y
|
||||||
|
CONFIG_PID_NS=y
|
||||||
|
CONFIG_NET_NS=y
|
||||||
|
|
||||||
|
CONFIG_NET=y
|
||||||
|
CONFIG_BRIDGE=m
|
||||||
|
|
||||||
|
CONFIG_NETDEVICES=y
|
||||||
|
CONFIG_MACVLAN=m
|
||||||
|
CONFIG_VETH=m
|
||||||
|
```
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Référez-vous, si besoin, au TP précédent pour la marche à suivre.
|
||||||
|
|
||||||
|
|
||||||
|
Paquets
|
||||||
|
-------
|
||||||
|
|
||||||
|
Nous allons utiliser des programmes issus des
|
||||||
|
[`util-linux`](https://www.kernel.org/pub/linux/utils/util-linux/), de
|
||||||
|
[`procps-ng`](https://gitlab.com/procps-ng/procps) ainsi que ceux de la
|
||||||
|
[`libcap`](http://www.friedhoff.org/posixfilecaps.html).
|
||||||
|
|
||||||
|
Sous Debian et ses dérivés, ses paquets sont respectivement :
|
||||||
|
|
||||||
|
* `util-linux`
|
||||||
|
* `procps`
|
||||||
|
* `libcap2-bin`
|
||||||
|
|
||||||
|
|
||||||
|
À propos de la sécurité de l'espace de nom `user`
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|
* [Anatomy of a user namespaces vulnerability](https://lwn.net/Articles/543273/) ;
|
||||||
|
* <http://marc.info/?l=linux-kernel&m=135543612731939&w=2> ;
|
||||||
|
* <http://marc.info/?l=linux-kernel&m=135545831607095&w=2>.
|
||||||
|
|
||||||
|
De nombreux projets ont choisi de ne pas autoriser l'utilisation de cet espace
|
||||||
|
de noms sans disposer de certaines *capabilities*[^userns-caps].
|
||||||
|
|
||||||
|
[^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
|
||||||
|
noyau pour adapter le comportement.
|
||||||
|
|
||||||
|
|
||||||
|
### Debian et ses dérivées {.unnumbered}
|
||||||
|
|
||||||
|
Si vous utilisez Debian ou l'un de ses dérivés, vous devrez autoriser
|
||||||
|
explicitement cette utilisation non-privilégiée :
|
||||||
|
|
||||||
|
```shell
|
||||||
|
42sh# sysctl -w kernel.unprivileged_userns_clone=1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Grsecurity {.unnumbered}
|
||||||
|
|
||||||
|
D'autres patchs, tels que
|
||||||
|
[*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
|
||||||
|
réactiver éventuellement à l'exécution. Pour avoir un comportement identique à
|
||||||
|
celui de Debian, vous pouvez
|
||||||
|
[appliquer ce patch](https://virli.nemunai.re/grsec-enable-user-ns.patch), sur
|
||||||
|
vos sources incluant le patch de *grsecurity*.
|
|
@ -1,25 +1,23 @@
|
||||||
---
|
---
|
||||||
title: Virtualisation légère -- TP n^o^ 4
|
title: Virtualisation légère -- TP n^o^ 4
|
||||||
subtitle: Linux Internals partie 2
|
subtitle: Linux Internals partie 2
|
||||||
author: Pierre-Olivier *Nemunaire* Mercier
|
author: Pierre-Olivier *nemunaire* Mercier
|
||||||
institute: EPITA
|
institute: EPITA
|
||||||
date: Jeudi 20 octobre 2016
|
date: Jeudi 2 novembre 2017
|
||||||
...
|
...
|
||||||
|
|
||||||
Le but de ce second TP sur les mécanismes internes du noyau va nous permettre
|
Le but de ce second TP sur les mécanismes internes du noyau va nous permettre
|
||||||
d'utiliser les commandes et les appels systèmes relatifs aux *namespaces* ainsi
|
d'utiliser les commandes et les appels systèmes relatifs aux *namespaces* ainsi
|
||||||
que d'appréhender la complexité des sytèmes de fichiers.
|
que d'appréhender la complexité des systèmes de fichiers.
|
||||||
|
|
||||||
Le projet du cours est à rendre à <virli@nemunai.re> au plus tard le dimanche
|
Tous les exercices de ce TP sont à rendre à <virli@nemunai.re> au plus tard le
|
||||||
20 novembre 2016 à 23 h 42.
|
jeudi 9 novembre 2017 à 8 h 42.
|
||||||
|
|
||||||
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 à
|
||||||
[me](https://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire
|
[me](https://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire
|
||||||
signer votre clef et n'hésitez pas à
|
signer votre clef et n'hésitez pas à
|
||||||
[faire signer votre clef](https://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
|
[faire signer votre clef](https://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
|
||||||
Vous pouvez utiliser l'adresse <signcheck@nemunai.re> pour savoir si vous vous
|
|
||||||
y prenez correctement.
|
|
||||||
|
|
||||||
\hypersetup{linkcolor=black}
|
\hypersetup{linkcolor=black}
|
||||||
\tableofcontents
|
\tableofcontents
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
Le *namespace* `user`
|
Le *namespace* `user` {#user-ns}
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Le *namespace* `user` est plutôt pratiquxe 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.
|
||||||
|
|
||||||
Par exemple, on va pouvoir entrer dans un conteneur en tant que
|
Par exemple, on va pouvoir entrer dans un conteneur en tant que
|
||||||
|
@ -31,14 +31,14 @@ repose entièrement sur cela.
|
||||||
|
|
||||||
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 si besoin au passage.
|
processus, en les renumérotant au passage si besoin.
|
||||||
|
|
||||||
|
|
||||||
### l'utilisateur -1 : *nobody*
|
### L'utilisateur -1 : *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 est -1 ; valeur qui correspond en toute circonstance à
|
renvoyés par le noyau sont à -1 ; valeur qui correspond en toute circonstance à
|
||||||
l'utilisateur *nobody* et au groupe *nogroup*.
|
l'utilisateur *nobody* et au groupe *nogroup*.
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ l'utilisateur *nobody* et au groupe *nogroup*.
|
||||||
|
|
||||||
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
|
||||||
écrit une fois.
|
écrits une fois.
|
||||||
|
|
||||||
Sur chaque ligne, on doit indiquer :
|
Sur chaque ligne, on doit indiquer :
|
||||||
|
|
||||||
|
@ -59,22 +59,24 @@ Sur chaque ligne, on doit indiquer :
|
||||||
- La taille de la page.
|
- La taille de la page.
|
||||||
|
|
||||||
|
|
||||||
Par exemple, le *namespace* `user` initial défini le correspondance suivante :
|
Par exemple, le *namespace* `user` initial défini la correspondance suivante :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
|
42sh$ cat /proc/self/uid_map
|
||||||
0 0 4294967295
|
0 0 4294967295
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Cela signifie que les utilisateurs dont l'identifiant court de 0 à -2 inclu,
|
Cela signifie que les utilisateurs dont l'identifiant court de 0 à `MAX_INT -
|
||||||
dans cet espace de noms, correspond aux utilisateurs allant de 0 à -1 inclu,
|
2` inclu, dans cet espace de noms, correspondent aux utilisateurs allant de 0 à
|
||||||
pour le processus affichant ce fichier.
|
`MAX_INT - 1` inclu, 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 :
|
||||||
|
|
||||||
<div lang="en-US">
|
<div lang="en-US">
|
||||||
```
|
```
|
||||||
|
42sh$ cat /proc/self/uid_map
|
||||||
0 1000 1
|
0 1000 1
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,14 +100,14 @@ des groupes au lieu des utilisateurs.
|
||||||
```
|
```
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Un `capsh --print` nous montre que l'on est bien root et que l'on possède
|
Un `capsh --print` nous montre que l'on est bien `root` et que l'on possède
|
||||||
toutes les capabilities. Cependant, cela ne signifie pas que l'on a tous les
|
toutes les *capabilities*. Cependant, cela ne signifie pas que l'on a tous les
|
||||||
droits sur le système ; il y a plusieurs niveau de validation qui entrent en
|
droits sur le système ; il y a plusieurs niveaux de validation qui entrent en
|
||||||
jeu. L'idée étant que l'on a été désigné root dans son conteneur, on devrait
|
jeu. L'idée étant que l'on a été désigné root dans son conteneur, on devrait
|
||||||
pouvoir y faire ce que l'on veut, tant que l'on 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 de manuel consacré à ce *namespace* :
|
N'hésitez pas à jeter un œil à la page du manuel consacré à ce *namespace* :
|
||||||
`user_namespaces(7)`.
|
`user_namespaces(7)`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue