Work on TP4
This commit is contained in:
parent
6fd83df1fd
commit
58a228ef2d
@ -178,6 +178,7 @@ Je vous recommande la lecture des *man* suivants :
|
||||
|
||||
Et de ces quelques articles :
|
||||
|
||||
* [Secure Your Containers with this One Weird Trick](http://rhelblog.redhat.com/2016/10/17/secure-your-containers-with-this-one-weird-trick/)
|
||||
* [Guidelines for extended attributes](https://www.freedesktop.org/wiki/CommonExtendedAttributes/)
|
||||
* [File-based capabilities](https://lwn.net/Articles/211883/)
|
||||
* [A bid to resurrect Linux capabilities](https://lwn.net/Articles/199004/)
|
||||
|
@ -22,7 +22,7 @@ utiliser du C, du C++, du Python, etc.
|
||||
|
||||
L'usage de bibliothèques **non relatives** au projet est autorisé : le but de
|
||||
ce sujet est d'évaluer votre compréhension et votre utilisation de la
|
||||
tuyauterie bas-niveau du noyau liée à la virtualisation légère, à partir du
|
||||
tuyauterie bas-niveau du noyau liée à la virtualisation légère. À partir du
|
||||
moment où vous n'utilisez pas une bibliothèque qui abstrait complètement cette
|
||||
plomberie, n'hésitez pas à l'utiliser !
|
||||
|
||||
@ -30,7 +30,7 @@ Gardez en tête que ce projet sera à continuer au prochain TP, où il sera
|
||||
principalement question de faire des appels système.
|
||||
|
||||
|
||||
### Stage 1 : Restreindre l'environnement
|
||||
### Stage 1 : Restreindre l'environnement (2 points)
|
||||
|
||||
Après avoir mis en place les bases de votre programme, commencez par créer les
|
||||
différentes hiérarchies (si vous avez un noyau récent, vous pouvez utiliser les
|
||||
@ -49,7 +49,7 @@ moulinette ne possède pas tous ces *CGroup*s, au lieu de planter, ne rien faire
|
||||
n'est pas forcément une mauvaise solution.
|
||||
|
||||
|
||||
### Stage 2 : Réduire les *capabilities*
|
||||
### Stage 2 : Réduire les *capabilities* (2 points)
|
||||
|
||||
Réduisez au maximum les capabilities, de telle sorte qu'il ne soit pas possible
|
||||
de faire un ping dans l'environnement restreint :
|
||||
@ -78,7 +78,7 @@ Aidez-vous du visualisateur de *capabilities* de la partie 4, pour voir si vous
|
||||
êtes sur la bonne voie.
|
||||
|
||||
|
||||
### Stage 3 : Utilisable par un utilisateur
|
||||
### Stage 3 : Utilisable par un utilisateur (1 point)
|
||||
|
||||
Jouez avec les attributs étendus pour qu'un utilisateur non-privilégié puisse
|
||||
exécuter votre moulinette. Ajouter la/les commande(s) à votre Makefile ou
|
||||
@ -96,7 +96,7 @@ dans la partie sur les *chroot*.
|
||||
sera seulement utile pour faire des tests.**
|
||||
|
||||
|
||||
### Stage 4 : Isolation du pauvre
|
||||
### Stage 4 : Isolation du pauvre (1 point)
|
||||
|
||||
Nous n'avons pas encore vu de meilleure méthode pour mieux isoler
|
||||
l'environnement que de faire un `chroot`, ajouter à votre programme cette
|
||||
@ -104,7 +104,7 @@ isolation rudimentaire. Et rendez-vous au prochain cours pour avoir une
|
||||
meilleure d'isolation !
|
||||
|
||||
|
||||
### Stage 5 (bonus) : automatisation de la création de l'environnement
|
||||
### Stage 5 (bonus) : automatisation de la création de l'environnement (5 points)
|
||||
|
||||
Pour moulinéter plusieurs étudiants en parallèle, vous allez avoir besoin de
|
||||
plusieurs environnements identiques. Plutôt que de recopier cet environnement,
|
||||
|
@ -1,13 +1,16 @@
|
||||
SOURCES = tutorial.md installation.md lxc.md cgroups.md namespaces.md
|
||||
TEMPLATE = ../../template.tex
|
||||
SOURCES = tutorial.md mount.md namespaces.md networkns.md userns.md pidns.md clone.md seccomp.md project.md
|
||||
PANDOCOPTS = --latex-engine=xelatex \
|
||||
--standalone \
|
||||
--normalize \
|
||||
--number-sections \
|
||||
-M lang=frenchb \
|
||||
--smart \
|
||||
-M lang=french \
|
||||
-M fontsize=12pt \
|
||||
-M papersize=a4paper \
|
||||
--template=${TEMPLATE}
|
||||
-M mainfont="Linux Libertine O" \
|
||||
-M monofont="Inconsolata" \
|
||||
-M sansfont="Linux Biolinum O" \
|
||||
--include-in-header=../header.tex
|
||||
|
||||
|
||||
all: tutorial.pdf
|
||||
|
19
tutorial/4/clone.md
Normal file
19
tutorial/4/clone.md
Normal file
@ -0,0 +1,19 @@
|
||||
\newpage
|
||||
|
||||
Plus bas !
|
||||
==========
|
||||
|
||||
## `clone(2)`
|
||||
|
||||
* `CLONE_NEWNS`
|
||||
* `CLONE_NEWUTS`
|
||||
* `CLONE_NEWIPC`
|
||||
* `CLONE_NEWPID`
|
||||
* `CLONE_NEWNET`
|
||||
* `CLONE_NEWUSER`
|
||||
* `CLONE_NEWCGROUP`
|
||||
|
||||
|
||||
## Aller plus loin
|
||||
|
||||
* [](http://crosbymichael.com/creating-containers-part-1.html)
|
22
tutorial/4/mount.md
Normal file
22
tutorial/4/mount.md
Normal file
@ -0,0 +1,22 @@
|
||||
\newpage
|
||||
|
||||
Des particularités de `mount`
|
||||
=============================
|
||||
|
||||
## Les points de montage
|
||||
|
||||
## `bind`
|
||||
|
||||
## `shared`
|
||||
|
||||
## `switch_root` ou `pivot_root`
|
||||
|
||||
|
||||
## Aller plus loin
|
||||
|
||||
Voici quelques articles qui valent de détour :
|
||||
|
||||
* [Shared subtree](https://lwn.net/Articles/159077) et la
|
||||
[documentation du noyau associée](https://kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) ;
|
||||
* [Mount namespaces and shared subtrees](https://lwn.net/Articles/689856) ;
|
||||
* [Mount namespaces, mount propagation, and unbindable mounts](https://lwn.net/Articles/690679).
|
@ -1,17 +1,163 @@
|
||||
\newpage
|
||||
|
||||
# Utiliser les *namespaces*
|
||||
Les *namespaces*
|
||||
================
|
||||
|
||||
## Introduction
|
||||
|
||||
Les espaces de noms du noyau, *namespaces*, permettent de dupliquer certaines
|
||||
structures du 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`,
|
||||
`user` et `UTS`.
|
||||
|
||||
|
||||
### `mount` *namespaces*
|
||||
|
||||
Depuis Linux 2.4.19.
|
||||
|
||||
Isole la liste des points de montage.
|
||||
|
||||
Chaque processus d'un namespace différent peut monter, démonter et réorganiser
|
||||
à sa guise les points de montage. Une partition ne sera donc pas nécessairement
|
||||
démonté après un appel à `umount(2)`, elle le sera lorsqu'elle aura
|
||||
effectivement été démontée de chaque *namespace* dans lequel elle était montée.
|
||||
|
||||
|
||||
### `UTS` *namespaces*
|
||||
|
||||
Depuis Linux 2.6.19.
|
||||
|
||||
Isole le nom de machine et son domaine NIS.
|
||||
|
||||
|
||||
### `IPC` *namespaces*
|
||||
|
||||
Depuis Linux 2.6.19.
|
||||
|
||||
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 *namespace*.
|
||||
|
||||
|
||||
### `PID` *namespaces*
|
||||
|
||||
Depuis Linux 2.6.24.
|
||||
|
||||
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é ; 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.
|
||||
|
||||
|
||||
### `network` *namespaces*
|
||||
|
||||
Depuis Linux 2.6.29.
|
||||
|
||||
Fourni une isolation pour toutes les ressources associées aux réseaux : les
|
||||
interfaces, les piles protocolaires IPv4 et IPv6, les tables de routage,
|
||||
pare-feu, ports numérotés, etc.
|
||||
|
||||
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.
|
||||
|
||||
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 (et non pas dans l'espace parent, en cas
|
||||
d'imbrication).
|
||||
|
||||
|
||||
### `user` *namespaces*
|
||||
|
||||
Depuis Linux 3.8.
|
||||
|
||||
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érent entre l'intérieur et
|
||||
l'extérieur du conteneur. Il est alors possible, alors que l'on est un simple
|
||||
utilisateur à l'extérieur du *namespace*, d'avoir l'UID 0 dans le conteneur.
|
||||
|
||||
|
||||
### `CGroup` *namespaces*
|
||||
|
||||
Depuis Linux 4.6.
|
||||
|
||||
Isole la vue de la racine des *Control Group* en la plaçant sur un
|
||||
sous-groupe de l'arborescence.
|
||||
|
||||
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'arborescences des cgroups n'a alors
|
||||
plus d'importance car le processus ne voit que son groupe.
|
||||
|
||||
|
||||
|
||||
|
||||
## Partir dans un nouveau *namespace*
|
||||
|
||||
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 appeler 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é dans un autre *namespace* `UTS` :
|
||||
|
||||
```shell
|
||||
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
|
||||
```
|
||||
|
||||
Nous avons pu ici modifier le nom de machine, sans que cela n'affecte notre
|
||||
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`.
|
||||
|
||||
|
||||
## Comparaison de *namespace*
|
||||
|
||||
Écrivez un script ou un programme, `cmpns`, dans le langage courant de votre
|
||||
choix, permettant de déterminer si deux programmes s'exécutent dans les mêmes
|
||||
*namespaces*.
|
||||
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
|
||||
|
||||
```sh
|
||||
42sh$ cmpns $(pgrep influxdb) $(pgrep init)
|
||||
42sh$ ./cmpns $(pgrep influxdb) $(pgrep init)
|
||||
- cgroup: differ
|
||||
- ipc: differ
|
||||
- mnt: differ
|
||||
- net: differ
|
||||
@ -21,7 +167,8 @@ choix, permettant de déterminer si deux programmes s'exécutent dans les mêmes
|
||||
```
|
||||
|
||||
```sh
|
||||
42sh$ cmpns $(pgrep init) self
|
||||
42sh$ ./cmpns $(pgrep init) self
|
||||
- cgroup: same
|
||||
- ipc: same
|
||||
- mnt: same
|
||||
- net: same
|
||||
@ -32,102 +179,119 @@ choix, permettant de déterminer si deux programmes s'exécutent dans les mêmes
|
||||
|
||||
Ici, `self` fait référence au processus actuellement exécuté.
|
||||
|
||||
Et pourquoi pas :
|
||||
|
||||
```sh
|
||||
42sh$ unshare -m ./cmpns $$ self
|
||||
- cgroup: same
|
||||
- ipc: same
|
||||
- mnt: differ
|
||||
- net: same
|
||||
- pid: same
|
||||
- user: same
|
||||
- uts: same
|
||||
```
|
||||
|
||||
|
||||
## Rejoindre un *namespace*
|
||||
|
||||
Dans le langage courant de votre choix, écrivez un programme : `setns`,
|
||||
permettant, à la manière de `unshare(1)` et `unshare(2)`, d'utiliser `setns(2)`
|
||||
via votre interpréteur.
|
||||
Rejoindre un *namespace* se fait en utilisant l'appel système `setns(2)`,
|
||||
auquel on passe le *file descriptor* d'un des liens du dossier
|
||||
`/proc/<PID>/ns/` :
|
||||
|
||||
Les options attendues sont :
|
||||
```c
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
* rejoindre un *namespace* IPC : `-i`, `--ipc` ;
|
||||
* rejoindre un *namespace* mount : `-m`, `--mount` ;
|
||||
* rejoindre un *namespace* net : `-n`, `--net` ;
|
||||
* rejoindre un *namespace* PID : `-p`, `--pid` ;
|
||||
* rejoindre un *namespace* UTS : `-u`, `--uts` ;
|
||||
* rejoindre un *namespace* user : `-U`, `--user`.
|
||||
// ./a.out /proc/PID/ns/FILE cmd args...
|
||||
|
||||
### Exemples
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd = open(argv[1], O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror("open");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
```sh
|
||||
42sh# setns /bin/bash
|
||||
bash# _
|
||||
if (setns(fd, 0) == -1)
|
||||
{
|
||||
perror("setns");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
execvp(argv[2], &argv[2]);
|
||||
|
||||
perror("execve");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
```
|
||||
|
||||
#### IPC and PID Namespaces
|
||||
Dans un shell, on utilisera la commande `nsenter(1)` :
|
||||
|
||||
```sh
|
||||
42sh# setns --ipc=/proc/42/ns/ipc -p /proc/42/ns/pid /bin/echo toto
|
||||
toto
|
||||
```
|
||||
|
||||
#### Net Namespace
|
||||
|
||||
```sh
|
||||
42sh# setns --net=/proc/42/ns/net ip a
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00::00
|
||||
inet 127.0.0.1/8 brd 127.255.255.255 scope lo
|
||||
valid_lft forever preferred_lft
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
#### UTS Namespace
|
||||
|
||||
```sh
|
||||
42sh# hostname --fqdn
|
||||
koala.zoo.paris
|
||||
42sh# setns --uts=/proc/42/ns/uts hostname --fqdn
|
||||
lynx.zoo.paris
|
||||
```shell
|
||||
42sh# nsenter --uts=/proc/42/ns/uts /bin/bash
|
||||
```
|
||||
|
||||
|
||||
## My Little Container
|
||||
### `docker exec`
|
||||
|
||||
En utilisant le langage courant de votre choix, concevez l'exécutable `mlc`,
|
||||
permettant de lancer une application dans un environnement différent (comme un
|
||||
`chroot`, mais sans permettre de s'en échapper) et avec des privilèges réduits.
|
||||
Si vous avez bien suivi jusque là, vous avez dû comprendre qu'un `docker exec`,
|
||||
n'était donc rien de plus qu'un `nsenter(1)`.
|
||||
|
||||
Votre solution doit créer au moins un nouveau *namespace* mount et PID.
|
||||
Réécrivons, en quelques lignes de shell, la commande `docker exec` ! Pour
|
||||
savoir si vous avez réussi, comparez les sorties des commandes :
|
||||
|
||||
Vous aurez sans doute besoin de : `clone(2)`, `capabilities(7)`, `capset(2)`, `pivot_root(2)`,
|
||||
- `ip address` ;
|
||||
- `hostname` ;
|
||||
- `mount` ;
|
||||
- `pa -aux` ;
|
||||
- ...
|
||||
|
||||
### Exemples
|
||||
|
||||
```sh
|
||||
42sh# ls newroot
|
||||
bin etc home usr root proc var
|
||||
## Durée de vie d'un *namespace*
|
||||
|
||||
42sh# mlc newroot/ /bin/bash
|
||||
bash# ls ../../../
|
||||
bin etc home usr root proc var
|
||||
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 à
|
||||
l'espace de noms initial, ...
|
||||
|
||||
bash# escape_chroot ls
|
||||
bin etc home usr root proc var
|
||||
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.
|
||||
|
||||
bash# ls -ld /proc/[0-9]* | wc -l
|
||||
2
|
||||
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` :
|
||||
|
||||
bash# curl http://www.linuxcontainers.org/ | md5sum
|
||||
0123456789abcdef
|
||||
|
||||
bash# ping 8.8.8.8
|
||||
Operation not permitted
|
||||
```shell
|
||||
42sh# touch /tmp/ns/myrefns
|
||||
42sh# mount --bind /proc/<PID>/ns/mount /tmp/ns/myrefns
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Rendu
|
||||
On peut très bien utiliser directement ce fichier pour obtenir un descripteur
|
||||
de fichier valide vers le *namespace* (pour passer à `setns(2)`).
|
||||
|
||||
Pour chaque exercice de cette partie, vous pouvez rendre un seul fichier s'il
|
||||
s'agit d'un script ; sinon, vous devez rendre une tarball contenant un
|
||||
`Makefile` permettant de générer les éventuels exécutables et/ou un `README`
|
||||
expliquant comment s'en servir.
|
||||
### Faire persister un *namespace*
|
||||
|
||||
Vous devez donc rendre 3 fichiers : `cmpns` ou `cmpns.tar.bz2`, `setns` ou
|
||||
`setns.tar.bz2` et `mlc` ou `mlc.tar.bz2`.
|
||||
Il n'est pas possible de faire persister un namespace d'un reboot à l'autre.
|
||||
|
||||
\vspace{3em}
|
||||
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.
|
||||
|
||||
Bon courage !
|
||||
|
||||
## 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/).
|
||||
|
175
tutorial/4/networkns.md
Normal file
175
tutorial/4/networkns.md
Normal file
@ -0,0 +1,175 @@
|
||||
\newpage
|
||||
|
||||
Le *namespace* `network`
|
||||
========================
|
||||
|
||||
## Introduction
|
||||
|
||||
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
|
||||
règles de filtrage, etc.
|
||||
|
||||
En entrant dans un nouvel espace de nom `network`, on se retrouve dans un
|
||||
environnement qui n'a plus que l'interface de loopback :
|
||||
|
||||
```shell
|
||||
42sh# unshare -n ip a
|
||||
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
```
|
||||
|
||||
Qui dit nouvelle pile réseau, dit également que les ports qui étaient assignés
|
||||
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
|
||||
autre espace de noms.
|
||||
|
||||
|
||||
## Premiers pas avec `ip netns`
|
||||
|
||||
La suite d'outils `iproute2` propose une interface simplifiée pour utiliser le
|
||||
*namespace* `network` : `ip netns`.
|
||||
|
||||
Tout d'abord, nous allons créer un nouvel espace de nom :
|
||||
|
||||
```shell
|
||||
42sh$ ip netns add virli
|
||||
```
|
||||
|
||||
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
|
||||
`mount --bind` 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 :
|
||||
|
||||
```sh
|
||||
42sh$ ip netns exec virli ip link
|
||||
```
|
||||
|
||||
Cette commande ne devrait vous montrer que l'interface de *loopback*, car nous
|
||||
n'avons pour l'instant pas encore attaché la moindre interface.
|
||||
|
||||
D'ailleurs, cette interface est rapportée comme étant désactivée, activons-là
|
||||
via la commande :
|
||||
|
||||
```shell
|
||||
42sh$ ip netns exec virli ip link set dev lo up
|
||||
```
|
||||
|
||||
Si tout se passe bien, vous devriez maintenant pouvoir lancer un `ping` sur
|
||||
cette interface :
|
||||
|
||||
```shell
|
||||
42sh$ ip netns exec virli ping 127.0.0.1
|
||||
```
|
||||
|
||||
|
||||
## *Virtual Ethernet*
|
||||
|
||||
É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
|
||||
interface physique par conteneur, d'autant plus si l'on a plusieurs
|
||||
centaines de conteneurs à gérer.
|
||||
|
||||
Une technique couramment employée consiste à créer une interface virtuelle de
|
||||
type `veth` :
|
||||
|
||||
```
|
||||
ip link add veth0 type veth peer name veth1
|
||||
```
|
||||
|
||||
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
|
||||
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`.
|
||||
|
||||
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
|
||||
devient alors possible de réaliser un échange de paquet entre les deux.
|
||||
|
||||
Pour déplacer `veth1` dans notre *namespace* `virli` :
|
||||
|
||||
```shell
|
||||
42sh# ip link set veth1 netns virli
|
||||
```
|
||||
|
||||
Il ne reste plus maintenant qu'à assigner une IP à chacune des interfaces :
|
||||
|
||||
```shell
|
||||
42sh# ip netns exec virli ip a add 10.10.10.42/24 dev veth1
|
||||
42sh# ip a add 10.10.10.41/24 dev veth0
|
||||
```
|
||||
|
||||
Testons maintenant que la communication entre les deux passe bient :
|
||||
|
||||
```shell
|
||||
42sh# ping 10.10.10.42
|
||||
- et -
|
||||
42sh# ip netns exec virli ping 10.10.10.41
|
||||
```
|
||||
|
||||
Il ne reste donc pas grand chose à faire pour fournir Internet à notre
|
||||
conteneur, via un peu de NAT ou grâce à un pont Ethernet.
|
||||
|
||||
|
||||
## Les autres types d'interfaces
|
||||
|
||||
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
|
||||
partout, mais c'est loin d'être la technique la plus rapide ou la moins
|
||||
gourmande.
|
||||
|
||||
|
||||
### VLAN
|
||||
|
||||
Il est possible d'attribuer juste une interface de VLAN, si l'on a switch les
|
||||
supportant.
|
||||
|
||||
```
|
||||
42sh# ip link add link eth0 name eth0.100 type vlan id 100
|
||||
42sh# ip link set dev eth0.100 up
|
||||
42sh# ip link set eth0.100 netns virli
|
||||
```
|
||||
|
||||
|
||||
### MACVLAN
|
||||
|
||||
Lorsque l'on 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
|
||||
des deux modes : VEPA ou *bridge*.
|
||||
|
||||
#### VEPA
|
||||
|
||||
Dans ce mode, tous les paquets sortant sont directement envoyé sur l'interface
|
||||
ethernet de sortie, sans qu'aucun routage préalable n'est été effectué. Ainsi,
|
||||
si un paquet est à destination d'un des autres conteneur de la machine, c'est à
|
||||
l'équipement réseau derrière la machine de rerouter le paquet vers la machine
|
||||
émetrice.
|
||||
|
||||
Pour construire une nouvelle interface de ce type :
|
||||
|
||||
```
|
||||
ip link add link eth0 mac0 type macvlan mode vepa
|
||||
```
|
||||
|
||||
|
||||
#### *Bridge*
|
||||
|
||||
À l'inverse du mode *VEPA*, les paquets sont routés selon leur adresse MAC : si
|
||||
jamais une adresse MAC est connuee, le paquet est délivré à l'interface MACVLAN
|
||||
correspondante ; dans le cas contraire, le paquet est envoyé sur l'interface de
|
||||
sortie.
|
||||
|
||||
Pour construire une nouvelle interface de ce type :
|
||||
|
||||
```
|
||||
ip link add link eth0 mac1 type macvlan mode bridge
|
||||
```
|
||||
|
||||
|
||||
## Aller plus loin
|
||||
|
||||
Pour approfondir les différentes techniques de routage, je vous
|
||||
recommande cet article :
|
||||
[Linux Containers and Networking](https://blog.flameeyes.eu/2010/09/linux-containers-and-networking).
|
13
tutorial/4/pidns.md
Normal file
13
tutorial/4/pidns.md
Normal file
@ -0,0 +1,13 @@
|
||||
\newpage
|
||||
|
||||
Le *namespace* `PID`
|
||||
=====================
|
||||
|
||||
## Introduction
|
||||
|
||||
## Remonter `/proc`
|
||||
|
||||
## Besoin du *namespace* `mount`
|
||||
|
||||
|
||||
## Aller plus loin
|
152
tutorial/4/project.md
Normal file
152
tutorial/4/project.md
Normal file
@ -0,0 +1,152 @@
|
||||
\newpage
|
||||
|
||||
Projet et rendu
|
||||
===============
|
||||
|
||||
## Sujet
|
||||
|
||||
**Ce projet, étalé sur ce TP et le TP précédent, constitue le cœur de la
|
||||
notation de ce cours.**
|
||||
|
||||
Vous allez continuer aujourd'hui le projet qui s'étendra depuis le TP précédent
|
||||
et qui consistera à réaliser la partie d'isolation de la moulinette des ACUs !
|
||||
|
||||
Cette semaine, il faudra faire en sorte de restreindre un groupe de processus
|
||||
pour qu'il s'exécute indépendemment de votre système.
|
||||
|
||||
Il n'y a pas de restriction sur le langage utilisé, vous pouvez tout aussi bien
|
||||
utiliser du C, du C++, du Python, etc.
|
||||
|
||||
L'usage de bibliothèques **non relatives** au projet est autorisé : le but de
|
||||
ce sujet est d'évaluer votre compréhension et votre utilisation de la
|
||||
tuyauterie bas-niveau du noyau liée à la virtualisation légère. À partir du
|
||||
moment où vous n'utilisez pas une bibliothèque qui abstrait complètement cette
|
||||
plomberie, n'hésitez pas à l'utiliser !
|
||||
|
||||
|
||||
### Stage 5 : Une vraie isolation
|
||||
|
||||
En plus du `chroot`, assignez de nouveaux namespaces au processus que vous
|
||||
allez lancer : CGroups, IPC, mount, net, PID, UTS, user.
|
||||
|
||||
Il est requis que le nouveau processus ne puisse pas s'échapper de ses
|
||||
namespaces !
|
||||
|
||||
Astuce : `clone(2)`.
|
||||
|
||||
|
||||
### Stage 6 : Empêcher les fuites d'information
|
||||
|
||||
Démonter tous les sytèmes de fichiers qui ne sont pas nécessaire au
|
||||
fonctionnement de votre conteneur et remontez les partitions
|
||||
|
||||
N'oubliez pas de remonter les systèmes de fichiers pour lequel cette opération
|
||||
est nécessaire pour terminer l'étape d'isolation.
|
||||
|
||||
Astuce : `mount(2)`.
|
||||
|
||||
|
||||
### Stage 7 : Identification du conteneur
|
||||
|
||||
Maintenant que vous avez votre conteneur, personalisez-le un peu en lui donnant
|
||||
un nom unique.
|
||||
|
||||
Astuce : `sethostname(2)`
|
||||
|
||||
|
||||
### Stage 8 : `pivot_root`
|
||||
|
||||
Effectuer un `pivot_root(2)` de telle sorte qu'il ne reste plus de trace du
|
||||
système de fichiers hôte.
|
||||
|
||||
Astuce : `pivot_root(2)`, `umount(2)`.
|
||||
|
||||
|
||||
### Stage 9 : Bac à sable connecté
|
||||
|
||||
Partant d'une liste d'interfaces sur la machine hôte similaire à :
|
||||
|
||||
```
|
||||
42sh$ ip link
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
|
||||
link/ether 90:2b:34:5e:fa:a7 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
Vous devrez pouvoir `ping` votre conteneur depuis votre hôte :
|
||||
|
||||
```
|
||||
42sh$ ip address
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
|
||||
[...]
|
||||
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
[...]
|
||||
3: veth3e06cad@if82: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
|
||||
link/ether 42:a2:a0:89:54:ef brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.10.10.41/24 brd 10.10.10.255 scope global veth3e06cad
|
||||
valid_lft forever preferred_lft forever
|
||||
42sh$ ping 10.10.10.42
|
||||
PING 10.10.10.42 (10.10.10.42) 56(84) bytes of data.
|
||||
64 bytes from 10.10.10.42: icmp_seq=1 ttl=56 time=3.90 ms
|
||||
64 bytes from 10.10.10.42: icmp_seq=2 ttl=56 time=3.78 ms
|
||||
^C
|
||||
--- 10.10.10.42 ping statistics ---
|
||||
2 packets transmitted, 2 received, 0% packet loss, time 1003ms
|
||||
rtt min/avg/max/mdev = 3.789/3.847/3.906/0.085 ms
|
||||
```
|
||||
|
||||
Dans l'exemple ci-dessus, l'interface dans le conteneur a l'IP `10.10.10.42`,
|
||||
tandis que la machine hôte a l'IP `10.10.10.41`.
|
||||
|
||||
Astuces : vous pouvez utiliser la `libnetlink(3)` ou même faire des appels aux
|
||||
programmes `ip(8)`, `brctl(8)`, ...
|
||||
|
||||
<https://github.com/shemminger/iproute2/blob/master/ip/ipnetns.c>
|
||||
|
||||
|
||||
### Stage 10 (bonus) : seccomp
|
||||
|
||||
Filtrez les appels systèmes de telle sorte qu'aucun programme exécuté dans
|
||||
votre bac à sable ne puisse plus appeler les appels systèmes suivants :
|
||||
|
||||
* `nfsservctl(2)` ;
|
||||
* `personality(2)` ;
|
||||
* `pivot_root(2)`.
|
||||
|
||||
Astuce : `seccomp(2)`.
|
||||
|
||||
|
||||
## Modalité de rendu
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
## Tarball
|
||||
|
||||
Tous les fichiers identifiés comme étant à rendre pour ce TP sont à
|
||||
placer dans une tarball (pas d'archive ZIP, RAR, ...).
|
||||
|
||||
Les réponses aux questions sont à regrouper dans un fichier `questions.txt` à
|
||||
placer à la racine de votre rendu.
|
||||
|
||||
Voici une arborescence type:
|
||||
|
||||
```
|
||||
login_x-TP4/questions.txt
|
||||
login_x-TP4/chroot/escape.c
|
||||
login_x-TP4/pseudofs/rev_kdb_leds.sh
|
||||
login_x-TP4/pseudofs/procinfo
|
||||
login_x-TP4/caps/view_caps
|
||||
login_x-TP4/cgroups/monitor
|
||||
login_x-TP4/cgroups/monitor_init
|
||||
login_x-TP4/mymoulette/README
|
||||
login_x-TP4/mymoulette/...
|
||||
```
|
29
tutorial/4/seccomp.md
Normal file
29
tutorial/4/seccomp.md
Normal file
@ -0,0 +1,29 @@
|
||||
\newpage
|
||||
|
||||
Encore plus sûr ?
|
||||
=================
|
||||
|
||||
## Introduction
|
||||
|
||||
Filtrage des appels systèmes.
|
||||
|
||||
|
||||
## Avec Docker
|
||||
|
||||
Documentation : <https://docs.docker.com/engine/security/seccomp/>
|
||||
|
||||
|
||||
## Avec LXC
|
||||
|
||||
Comme d'habitude avec LXC, il suffit d'ajouter une ligne à notre fichier de
|
||||
configuration de conteneur, pointant vers un fichier de politique :
|
||||
|
||||
```conf
|
||||
1
|
||||
whitelist
|
||||
103
|
||||
```
|
||||
|
||||
```conf
|
||||
lxc.seccomp = "/path/to/seccomp/profile"
|
||||
```
|
@ -1,21 +1,25 @@
|
||||
% Virtualisation légère -- TP n^o^ 1
|
||||
% Pierre-Olivier *nemunaire* Mercier
|
||||
% Jeudi 8 octobre 2015
|
||||
---
|
||||
title: Virtualisation légère -- TP n^o^ 4
|
||||
subtitle: Linux Internals partie 2
|
||||
author: Pierre-Olivier *Nemunaire* Mercier
|
||||
institute: EPITA
|
||||
date: Jeudi 20 octobre 2016
|
||||
...
|
||||
|
||||
Le but de ce premier TP est d'utiliser les commandes et les appels systèmes vu
|
||||
durant le cours.
|
||||
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
|
||||
que d'appréhender la complexité des sytèmes de fichiers.
|
||||
|
||||
Tous les éléments de ce TP (exercices et questions) sont à rendre à
|
||||
<virli@nemunai.re> au plus tard le **mercredi 21 octobre 2015 à 23
|
||||
h 42**. Consultez la dernière section de chaque partie pour plus d'information
|
||||
sur les éléments à rendre. Vous pouvez placer les réponses aux questions dans
|
||||
le corps du courriel ou dans un fichier texte joint.
|
||||
Le projet du cours est à rendre à <virli@nemunai.re> au plus tard le dimanche
|
||||
20 novembre 2016 à 23 h 42.
|
||||
|
||||
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
||||
vous devriez 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](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire
|
||||
signer votre clef et n'hésitez pas à
|
||||
[faire signer votre clef](http://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}
|
||||
\tableofcontents
|
||||
|
15
tutorial/4/userns.md
Normal file
15
tutorial/4/userns.md
Normal file
@ -0,0 +1,15 @@
|
||||
\newpage
|
||||
|
||||
Le *namespace* `user`
|
||||
=====================
|
||||
|
||||
## Introduction
|
||||
|
||||
## mapping inside/outside users/groups
|
||||
|
||||
## l'utilisateur -1 : nobody
|
||||
|
||||
## blabla sur le comportement vis-à-vis des autres namespaces
|
||||
|
||||
|
||||
## Aller plus loin
|
Loading…
Reference in New Issue
Block a user