Work on TP4

This commit is contained in:
nemunaire 2016-10-19 05:24:05 +02:00
parent 6fd83df1fd
commit 58a228ef2d
13 changed files with 694 additions and 97 deletions

View File

@ -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/)

View File

@ -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,

View File

@ -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
View 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
View 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).

View File

@ -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
View 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
View 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
View 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
View 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"
```

View File

@ -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
View 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