Work on tuto 3

This commit is contained in:
nemunaire 2016-10-05 22:49:08 +02:00
parent 07195409eb
commit b459443d9e
9 changed files with 325 additions and 656 deletions

View File

@ -5,13 +5,156 @@ Les capabilities
## Présentation
## La `libcap`
Historiquement, dans la tradition UNIX, on distingue deux catégories de
processus :
## Les attributs étendus
* les processus *privilégiés* : dont l'identifiant de son utilisateur est 0 ;
* les processus *non-privilégiés* : dont l'identifiant de son utilisateur n'est
pas 0.
Lors des différents tests de permission fait par le noyau, les processus
privilégiés outrepassaient ces tests, tandis que les autres devaient passer les
tests de l'effective UID, effective GID, et autres groupes supplémentaires...
Depuis Linux 2.2 (en 1998), les processus privilégiés peuvent activer ou
désactiver des *capabilities*, chacune donnant accès à un groupe d'actions
privilégiées au sein du noyau.
On trouve par exemple :
* `CAP_CHOWN` : permet de modifier le propriétaire d'un fichier de manière
arbitraire ;
* `CAP_KILL` : permet de tuer n'importe quel processus ;
* `CAP_SYS_BOOT` : permet d'arrêter ou de redémarrer la machine ;
* `CAP_SYS_MODULE` : permet de charger et décharger des modules ;
* et beaucoup d'autres, il y en a environ 39 en tout !
### `ping`
Pour émettre un ping, il est nécessaire d'envoyer des paquets ICMP. À la
différence des datagrammes UDP ou des segments TCP, il n'existe pas d'interface
exposée par le noyau aux utilisateurs pour envoyer des paquets ICMP. Pour le
faire, il est nécessaire de pouvoir écrire directement sur l'interface ; ça,
seul le super-utilisateur peut le faire.
Pour permettre à tous les utilisateurs de pouvoir envoyer des ping, le
programme est donc généralement Setuid root. Cela permet à n'importe quel
utilisateur de prendre les droits du super-utilisateur, le temps de l'exécution
du programme.
Les problèmes surviennent lorsque l'on découvre des vulnérabilités dans les
programmes Setuid root. En effet, s'il devient possible à un utilisateur
d'exécuter du code arbitraire, ce code sera exécuté avec les privilèges de
l'utilisateur root ! Dans le cas de ping, on se retrouverait alors à pouvoir
lire l'intégralité de la mémoire, alors que l'on avait juste besoin d'écrire
sur une interface réseau.
C'est donc à ce moment que les *capabilities* entrent en jeu : un processus (ou
même un thread) privilégié peut décider, à son lancement, de réduire ses
*capabilities*, pour ne garder que celles dont il a réellement besoin. Ainsi,
`ping` pourrait se contenter de `CAP_NET_RAW`.
## Les attributs de fichier étendus
Une grosse majorité des systèmes de fichiers (ext[234], XFS, btrfs, ...)
permettent d'enregistrer, pour chaque fichier, des attributs (dits attributs
*étendus*, par opposition aux attributs *réguliers* qui sont réservés à l'usage
du système de fichiers).
Sous Linux, les attributs sont regroupés dans des espaces de noms :
* *security* : espace utilisé par les modules de sécurité du noyau, tel que
SELinux, ... ;
* *system* : espace utilisé par le noyau pour stocker des objets système, tels
que les ACL POSIX ;
* *trusted*: espace dont la lecture et l'écriture est limité au
super-utilisateur ;
* *user* : modifiable sans restriction, à partir du moment où l'on est le
propriétaire du fichier.
Par exemple, on peut définir un attribut sur un fichier comme cela :
```shell
42sh$ echo 'Hello World!' > toto
42sh$ setfattr -n user.foo -v bar toto
42sh$ getfattr -d toto
# file: toto
user.foo="bar"
```
Encore plus fort, vous pouvez utiliser les ACL POSIX :
```shell
42sh$ sudo chown root:root toto && sudo chmod o-r toto
42sh$ cat toto
cat: toto: Permission denied
42sh$ sudo setfattr -m u:$USER:r toto
42sh$ cat toto
Hello World!
```
Bien que les droits UNIX traditionnels ne vous donne pas accès au fichier, les
ACL POSIX vous autorisent à lire le contenu du fichier.
Vous pouvez voir ces attributs avec la commande :
```shell
42sh$ getfattr -d -m "^system" toto
# file: toto
system.posix_acl_access=0sgAAEAD/////AgAEOgDAEAA/////xAABAD////8=
```
### `ping`
De la même manière que l'on peut définir de manière plus fine les droits
d'accès par utilisateur, un attribut de l'espace de nom *security* peut être
définit pour accroître les *capabilities* d'un processus lorsqu'il est lancé
par un utilisateur non-privilégié. On peut alors voir le Setuid root comme
l'utilisation de cet attribut auquel on accroîtrait l'ensemble des
*capabilities*.
Si votre distribution profite de ces attributs étendus, vous devriez obtenir :
```shell
42sh$ getfattr -d -m "^security" $(which ping)
# file: bin/ping
security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=
```
Ou, dans sa version plus lisible :
```shell
42sh$ getcap $(which ping)
/bin/ping = cap_net_raw+ep
```
`getcap` et `setcap` sur `ping`
## Exercice : visualisateur de capabilities d'un processus
Écrivons maintenant un script permettant de voir les *capabilities* d'un
processus :
```shell
```
## Rendu
## Aller plus loin
Je vous recommande la lecture des *man* suivants :
* `capabilities(7)` : énumérant tous les capabilities, leur utilisation, etc. ;
* `xattrs(7)` : à propos des attributs étendus.
Et de ces quelques articles :
* [https://www.freedesktop.org/wiki/CommonExtendedAttributes/](Guidelines for
extended attributes)
* [https://lwn.net/Articles/211883/](File-based capabilities)
* [http://lwn.net/Articles/199004/](A bid to resurrect Linux capabilities)

View File

@ -14,11 +14,12 @@ de ressources ou altérer leurs priorités.
Nous allons commencer par faire quelques tests avec le *cgroup* freezer, qui
permet d'interrompre l'exécution d'un groupe de processus et de la reprendre.
### Montage du *cgroup*
En fonction de la configuration de votre système, il est possible que les
*cgroup*s ne soient pas montés au démarrage dans `/sys/fs/cgroup/`. Si vous n'avez
pas de dossier `freezer` ou si celui-ci est vide, monter-le en suivant la
pas de dossier `freezer` ou si celui-ci est vide, montez-le en suivant la
procédure suivante :
```
@ -27,8 +28,9 @@ mount -t cgroup -o freezer none /sys/fs/cgroup/freezer/
```
Cette dernière commande monte le groupe de processus racine, pour le *cgroup*
freezer. Tous les dossiers contenu dans cette racine sont des sous-groupes de
cette dernière.
freezer. Tous les dossiers contenu dans cette racine sont donc des
sous-groupes.
### Création d'un nouveau groupe
@ -47,6 +49,7 @@ Vous avez maintenant un nouveau groupe de processus `virli` dans le *cgroup*
Freezer. Comme il s'agit d'une hiérarchie, le groupe `virli` hérite des
propriétés de son (ses) père(s).
### Rattachement de processus
Pour le moment, ce nouveau groupe ne contient aucune tâche.
@ -64,22 +67,24 @@ echo $PID > /sys/fs/cgroup/freezer/virli/tasks
Il faut ici remplacer `$PID` par le PID du shell que l'on a relevé juste avant.
En validant cette commande, vous avez déplacé le processus dans ce groupe, il
n'est alors plus dans aucun autre groupe (dans ce *cgroup*, il ne bouge pas
n'est alors plus dans aucun autre groupe (pour ce *cgroup*, il ne bouge pas
dans les autres *cgroup*s).
### Consultation de l'état
En affichant le contenu du dossier `virli`, nous avons pu constater que
celui-ci contenait déjà un certain nombre de fichiers. Certain d'entre-eux sont
en lecture seule et permettent de lire des statistiques instantanées sur le
groupe ; tandis que d'autres sont des propriétés que vous pouvez modifier.
En affichant le contenu du dossier `virli`, nous pouvons constater que celui-ci
contenait déjà un certain nombre de fichiers. Certain d'entre-eux sont en
lecture seule et permettent de lire des statistiques instantanées sur le groupe
; tandis que d'autres sont des propriétés que vous pouvez modifier.
Nous pouvons consulter l'état de gel du groupe en affichant le contenu du
fichier\newline `/sys/fs/cgroup/freezer/virli/freezer.state`.
Pour plus d'information sur les différents fichiers présents dans ce *cgroup*,
consulter la documentation, accessible ici :
<https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt>
consulter
[la documentation associée](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt).
### Changement d'état
@ -105,11 +110,28 @@ echo THAWED > /sys/fs/cgroup/freezer/virli/freezer.state
```
## Script de monitoring
## Exercice : script de monitoring
À nous maintenant de concevoir un script qui va enregistrer vers une base de
données des statistiques issues des *cgroup*s.
### Rappel d'InfluxDB
Commençons par lancer le conteneur Docker d'InfluxDB (pour éviter de
l'installer sur notre machine) :
```shell
docker run -d -p 8086:8086 -p 8083:8083 influxdb
```
Il nous faut ensuite créer une base de données pour y stocker les métriques,
rendez-vous à <http://localhost:8083/> puis entrez la requête :
```sql
CREATE DATABASE metrics;
```
À nous maintenant de concevoir un script qui va enregistrer vers la base de
données créée (*metrics*) dans la partie précédente, des statistiques issues
des *cgroup*s.
### Monitoring instantané vers la console
@ -125,30 +147,29 @@ mémoire utilisée par le groupe monitoré.
Vous pouvez utiliser un programme comme `memhog` pour remplir rapidement votre
mémoire.
Si vous n'avez pas le *cgroup* memory, il est possible qu'il ne soit
pas activé par défaut par votre système. Si vous êtes dans ce cas, essayez d'ajouter
Si vous n'avez pas le *cgroup* memory, il est possible qu'il ne soit pas activé
par défaut par votre système. Si vous êtes dans ce cas, essayez d'ajouter
`cgroup_enable=memory` à la ligne de commande de votre noyau.
```
cgroup_enable=memory
```
### Monitoring vers InfluxDB
Maintenant, envoyons nos données vers la base
<https://influxdb.com/docs/v0.9/guides/writing_data.html> :
<https://docs.influxdata.com/influxdb/v1.0/guides/writing_data/> :
```
curl -i -XPOST 'http://172.23.42.2:8086/write?db=metrics' --data-binary \
curl -i -XPOST 'http://localhost:8086/write?db=metrics' --data-binary \
"$my_cgroup_name memory.usage_in_bytes=$(cat .../my_cgroup_name/memory.usage_in_bytes)"
```
Pour vérifier que les données sont bien ajoutées, vous pouvez effectuez la
Pour vérifier que les données ont bien été ajoutées, vous pouvez effectuez la
requête suivante dans l'interface web d'InfluxDB :
```
SELECT * from "$my_cgroup_name";
```
### Monitorer davantage de données
Liste non exhaustive de données à monitorer :
@ -159,7 +180,8 @@ Liste non exhaustive de données à monitorer :
* trafic réseau généré ;
* ...
<https://www.kernel.org/doc/Documentation/cgroups/>
<https://www.kernel.org/doc/Documentation/cgroup-v1/>
### Permettre à l'utilisateur de monitorer des process
@ -172,7 +194,7 @@ bons droits, tandis que le deuxième va utiliser effectuer le monitoring, sans
#### Exemple
```
42sh# ./monitor_init my_cgroup_name
42sh$ sudo ./monitor_init my_cgroup_name
42sh$ ./monitor my_cgroup_name memhog 500
```
@ -181,8 +203,7 @@ bons droits, tandis que le deuxième va utiliser effectuer le monitoring, sans
### Script de monitoring
Rendez la révision la plus avancée de vos scripts de monitoring de process via
les *cgroup*s.
Rendez la révision la plus avancée de vos scripts de monitoring de processus.
### Questions
@ -194,3 +215,19 @@ les *cgroup*s.
1. Actuellement, comment peut-on limiter le nombre de processus lancés par un
utilisateur ou un groupe ?
## Pour aller plus loin
Depuis les noyaux 4.5, il est possible d'utiliser la nouvelle version du
pseudo système de fichiers des *CGroup*s. Le principal changement vient du
regroupement au sein d'une seule hiérarchie des différents *CGroup*s que l'on
avait dans la v1. Davantage d'informations sont disponibles :
* [https://lwn.net/Articles/679786/](Understanding the new control groups API)
;
* [https://www.kernel.org/doc/Documentation/cgroup-v2.txt](Kernel Document
about Control Group v2).
Pour tout connaître en détails, [la série d'articles de Neil Brown sur les
Control groups](https://lwn.net/Articles/604609/) est excellente !

View File

@ -1,7 +1,7 @@
\newpage
L'isolation du pauvre
=====================
L'isolation ... du pauvre
=========================
Depuis les premières version d'Unix, il est possible de changer le répertoire
vu comme étant la racine du système de fichiers.
@ -74,7 +74,7 @@ Dans le nouvel environnement, vous ne devriez pas pouvoir faire :
cat ../foo
```
Mais une fois votre programme exécuté, vous devriez pouvoir !
Mais une fois votre programme `escape` exécuté, vous devriez pouvoir !
```shell
./escape

View File

@ -1,214 +0,0 @@
\newpage
# Utiliser `lxc`
Le but de cette première partie est d'appréhender la virtualisation légère au
travers d'un programme, `lxc`, qui va mettre en place pour nous tout un
environnement distinct.
## Lancer un conteneur
Avec le paquet `lxc` que nous avons précédemment installé, nous avons également
récupéré un certain nombre de *modèles* de système (souvent installés dans le
dossier `/usr/share/lxc/templates/`) : il s'agit d'une suite de commandes
(principalement des `wget`, `chroot` ou `debootstrap`) permettant d'obtenir un
système basic fonctionnel, en suivant les étapes d'installation habituelle de
la distribution.
La méthode la plus simple pour lancer un conteneur `lxc` est d'utiliser l'un de
ces modèles pour obtenir un nouveau système. On utilise pour cela la commande
`lxc-create` :
```
lxc-create --name toto_first --template debian
```
Ce modèle va créer un dossier dans `/var/lib/lxc/` (pouvant varier d'une
distribution à l'autre) portant le nom que nous avons précisé. Ce dossier va
contenir la configuration `lxc` du conteneur (`config`), la table des
partitions (`fstab`) s'il y a besoin de faire des montages particuliers et
enfin le dossier `rootfs` contenant le système en lui-même.
Une fois l'installation terminée, on peut démarrer le conteneur :
```
lxc-start --name toto_first
```
`lxc` va appeler `/sbin/init` et démarrer tous les services que l'on peut
s'attendre à trouver dans n'importe quelle machine virtuelle (et même physique)
plus classique (la seule différence réside donc dans le fait que le noyau est
partagé avec l'hôte).
Généralement on lance `lxc-start` avec l'option `--daemon`, car on n'a pas
vraiment envie d'avoir un conteneur bloquant un terminal. En mode daemon, on va
utiliser la commande `lxc-console` pour nous attacher aux conteneurs. À tout
moment, nous pouvons nous détacher de la console (sans que cela n'affecte
l'état du conteneur) en pressant les touches : `^A q`.
Connectons-nous, lancons quelques commandes puis éteignons la machine en
lançant la commande `poweroff` dans le conteneur. Il est également possible de
lancer la commande `lxc-stop --name toto_first` dans un autre terminal, depuis
la machine hôte.
## Le réseau
Le modèle *Debian*, que nous avons utilisé, préremplit un fichier de
configuration sans définir de paramètre pour le réseau. Il n'y a donc pas
d'interface dans le conteneur pour le connecter :
```
lxc.network.type = empty
```
Un excellent article détaillant les différents types de configuration réseau
est accessible à
<https://blog.flameeyes.eu/2010/09/linux-containers-and-networking>.
N'ayant qu'une seule interface physique sur la machine et n'ayant pas accès à
la configuration des VLAN de la pièce, il ne nous reste que deux méthodes pour
obtenir du réseau dans nos conteneurs : Virtual Ethernet ou MACVLAN.
### MACVLAN
Cette méthode est la plus simple : le noyau va orienter les paquets en fonction
de leur adresse MAC de destination. Le conteneur sera donc comme une machine
supplémentaire sur le réseau.
Modifions notre fichier de configuration afin qu'il ressemble à quelque chose
comme :
```
lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.flags = up
lxc.network.link = eth0
```
Après avoir démarré le conteneur, il devrait avoir obtenu une IP du serveur
DHCP de l'école. L'inconvénient dans cette configuration est qu'il faille un
client netsoul dans chaque conteneur, puisque chacun est considéré comme une
machine différente aux yeux du routeur.
### Virtual Ethernet
Virtual Ethernet est la configuration la moins optimale, mais sans doute la
plus flexible.
Voici un extrait de configuration correspondant au paramétrage d'une interface
virtuelle pour un conteneur donné :
```
lxc.network.type = veth
lxc.network.ipv4 = 172.23.42.2/24
lxc.network.flags = up
```
Dans cette situation, au démarrage du conteneur, `lxc` va créer une interface
veth, avec un côté placé dans la machine hôte et l'autre côté placé dans le
conteneur. `lxc` configure l'interface dans le conteneur, il nous appartient
ensuite de configurer la machine hôte.
Commençons par attribuer une IP à cette nouvelle interface, en adaptant à votre
identifiant d'interface :
```
ip addr add 172.23.42.1/24 dev vethYJWD6R
```
À partir de là, nous devrions pouvoir pinger notre conteneur depuis notre
machine hôte : `ping 172.23.42.2`.
Notre conteneur ne peut cependant pas encore accéder à Internet. Pour cela, la
machine hôte doit faire office de routeur et donc router les paquets d'un
réseau à l'autre : en l'occurence, du réseau 172.23.42.1 vers Internet
via 10.0.0.0/8, le réseau de l'école.
Pour que notre machine hôte route les paquets, exécuter la commande :
```
sysctl -w net.ipv4.ip_forward=1
```
Cette variable, que nous retrouvons dans `/proc/sys/net/ipv4/ip_forward`,
indique au noyau qu'il peut faire passer les paquets réseau d'une interface à
l'autre. Sans plus de directives, les paquets vont conserver leur adresse
source (172.23.42.2 pour les paquets en provenance du conteneur). Cette adresse
est une adresse privée, non routable sur Internet, ni même par le bocal. Il
faut donc ajouter une couche de NAT/PAT pour réécrire les adresses sources
avant d'envoyer les paquets sur internet :
```
iptables -t nat -A POSTROUTING ! -o vethYJWD6R -s 172.23.42.0/24 -j MASQUERADE
```
Dernière étape, dans notre conteneur, nous devons indiquer la route à utiliser
pour accéder à internet :
```
ip route add default via 172.23.42.1
```
Nous avons maintenant internet dans notre conteneur !
## Utilisation du conteneur
### Installation de InfluxDB
```
apt-get update
apt-get install wget
wget https://s3.amazonaws.com/influxdb/influxdb_0.9.4.2_amd64.deb
dpkg -i influxdb_0.9.4.2_amd64.deb
```
### Test de l'installation
```
/opt/influxdb/influxd
```
Une fois que le service est démarré, vous devriez pouvoir accéder à l'interface
à : <http://172.23.42.2:8083/>
Créons une nouvelle base de données "metrics", elle nous servira dans la partie suivante.
## Rendu
### Configuration du conteneur
En plus des modifications que vous avez effectuées durant le TP, modifiez la
configuration du conteneur afin qu'il ne puisse pas utiliser plus que 256 MB de
RAM et 512 MB de swap.
Limitez ensuite les `capabilities(7)` de ce conteneur afin qu'il s'exécute avec
le strict minimum de droits, nécessaire au bon fonctionnement des programmes
installés.
**Rendez le fichier `config` de ce premier conteneur.** N'hésitez pas à laisser
des commentaires justifiant vos éventuels choix.
### Questions
1. Quels sont les autres types de virtualisation réseau existants ? Expliquez
en chacun une phrase leurs particularités.
1. Quel fichier de configuration devriez-vous changer pour rendre persistante la
valeur d'`ip_forward` ?
1. Dans quel langage InfluxDB a-t-il été écrit ? Quelle est la particularité
des binaires générés par ce langage ?
1. Quels sont les avantages et les inconvénients associés au linkage statique
et au linkage dynamique ? (pas forcément que dans le cadre de la
virtualisation légère).
1. J'ai utilisé la méthode *Virtual Ethernet* pour relier mes conteneurs à
Internet, via l'interface `br0`. Quelle(s) règle(s) `iptables` devrais-je
écrire sur mon hôte afin de permettre l'accès à InfluxDB depuis une autre
machine ?

View File

@ -1,133 +0,0 @@
\newpage
# Utiliser les *namespaces*
## 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*.
### Exemples
```sh
42sh$ cmpns $(pgrep influxdb) $(pgrep init)
- ipc: differ
- mnt: differ
- net: differ
- pid: differ
- user: same
- uts: same
```
```sh
42sh$ cmpns $(pgrep init) self
- ipc: same
- mnt: same
- net: same
- pid: same
- user: same
- uts: same
```
Ici, `self` fait référence au processus actuellement exécuté.
## 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.
Les options attendues sont :
* 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`.
### Exemples
```sh
42sh# setns /bin/bash
bash# _
```
#### IPC and PID Namespaces
```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
```
## My Little Container
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.
Votre solution doit créer au moins un nouveau *namespace* mount et PID.
Vous aurez sans doute besoin de : `clone(2)`, `capabilities(7)`, `capset(2)`, `pivot_root(2)`,
### Exemples
```sh
42sh# ls newroot
bin etc home usr root proc var
42sh# mlc newroot/ /bin/bash
bash# ls ../../../
bin etc home usr root proc var
bash# escape_chroot ls
bin etc home usr root proc var
bash# ls -ld /proc/[0-9]* | wc -l
2
bash# curl http://www.linuxcontainers.org/ | md5sum
0123456789abcdef
bash# ping 8.8.8.8
Operation not permitted
```
## Rendu
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.
Vous devez donc rendre 3 fichiers : `cmpns` ou `cmpns.tar.bz2`, `setns` ou
`setns.tar.bz2` et `mlc` ou `mlc.tar.bz2`.
\vspace{3em}
Bon courage !

View File

@ -5,6 +5,9 @@ Projet et rendu
## Sujet
**Ce projet, étalé sur ce TP et le TP suivant, constitue le cœur de la notation
de ce cours.**
Vous allez commencer aujourd'hui un projet qui s'étendra au prochain TP et qui
consistera à réaliser la partie d'isolation de la moulinette des ACUs !
@ -26,36 +29,82 @@ principalement question de faire des appels système.
### Stage 1 : Restreindre l'environnement
Après avoir mis en place les bases de votre programme,
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 les
cgroups-v2) dont vous allez avoir besoin pour limiter l'utilisation de
ressources.
Puis, mettez en place ces limites :
* pas plus d'1 GB de mémoire utilisée ;
* 1 seul CPU au maximum ;
* ...
### Stage 2 : Réduire les capabilities
### Stage 2 : Réduire les *capabilities*
Réduisez au maximum les capabilities, de telle sorte qu'il ne soit pas possible
de faire un ping dans l'environnement restreint :
```shell
42sh# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=56 time=3.93 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=56 time=3.97 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 3.931/3.954/3.978/0.067 ms
42sh# ./mymoulette /bin/bash
bash# curl http://www.linuxcontainers.org/ | md5sum
59e714c4331e71ac3529a6502994ef1d
bash# ping 8.8.8.8
Operation not permitted
```
Astuces : `prctl(2)`, `capabilities(7)`, `capget(2)`, `capset(2)`, ...
### Stage 3 : Script d'initialisation
### Stage 3 : Utilisable par un utilisateur
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
script d'installation.
### Stage 4 : Utilisable par un utilisateur
### Création d'un environnement d'exécution minimal
Une autre solution
Plutôt que d'utiliser votre système hôte au complet, avec tous ses programmes
et toutes ses bibliothèques, il faudrait utiliser un système contenant le
strict minimum. Recréez un environnement minimaliste, comme on a pu en voir
dans la partie sur les *chroot*.
**Ne rendez pas cet environnement, il vous sera seulement utile pour faire des
tests.**
### Stage 5 : Création de l'environnement d'exécution
Plutôt que d'utiliser votre système hôte complet, il faudrait utiliser un
système contenant le struct minimum.
### Stage 6 : Isolation du pauvre
### Stage 4 : Isolation du pauvre
Nous n'avons pas encore vu de meilleure méthode pour mieux isoler
l'environnement que de faire un `chroot`, ajouter à votre programme cette
isolation rudimentaire.
isolation rudimentaire. Et rendez-vous au prochain cours pour avoir de
moyen d'isolation !
### Stage 7 (bonus) : automatisation de l'environnement
### Stage 5 (bonus) : automatisation de la création de l'environnement
LVM
Pour moulinéter plusieurs étudiants en parallèle, vous allez avoir besoin de
plusieurs environnements identiques. Plutôt que de recopier cet environnement,
de le nettoyer, de le recréer, pour chaque étudiant, ajoutez à votre moulinette
un support pour LVM : utiliser des *snapshots* pour figer votre environnement
et le dupliquer facilement pour chaque étudiant.
L'usage est laissé à votre discrétion : vous pouvez ajouter un/des paramètres à
votre moulette pour indiquer le volume LVM à utiliser ou le définir en dur ou
encore séparer la création de l'environnement et de la snapshot initiale dans
un programme distinct.
## Modalité de rendu
@ -82,8 +131,12 @@ Voici une arborescence type:
```
login_x-TP3/questions.txt
login_x-TP3/
login_x-TP3/chroot/escape.c
login_x-TP3/pseudofs/rev_kdb_leds.sh
login_x-TP3/pseudofs/procinfo
login_x-TP3/caps/view_caps
login_x-TP3/cgroups/monitor
login_x-TP3/cgroups/monitor_init
login_x-TP3/mymoulette/README
login_x-TP3/mymoulette/...
```
## Astuces

View File

@ -53,6 +53,32 @@ exemple, pour modifier les paramètres du noyau, on passe par le fichier
## Exercice
### `rev_kdb_leds.sh`
Explorons le pseudo système de fichiers `/sys` pour écrire un script
qui va inverser l'état des diodes de notre clavier.
Si vous avez :
* numlock On,
* capslock Off,
* scrolllock Off ;
Après avoir exécuté le script, nous devrions avoir :
* numlock Off,
* capslock On,
* scrolllock On.
Voici un exemple d'utilisation :
```shell
42sh$ ./rev_kdb_leds.sh input20
```
### `procinfo`
Explorons le pseudo système de fichiers `/proc` pour écrire un script qui va
afficher des informations sur un processus donné :
@ -60,7 +86,7 @@ afficher des informations sur un processus donné :
42sh$ ./procinfo $$
PID: 4242
Path: /bin/bash
Arguments:
Command line: bash
Working directory: /home/nemunaire/virli/
Root: /
State: S (sleeping)
@ -83,11 +109,18 @@ CGroups
Namespaces
==========
cgroup -> cgroup:[4026531835]
ipc -> ipc:[4026531839]
mnt -> mnt:[4026531840]
net -> net:[4026531969]
pid -> pid:[4026531836]
user -> user:[4026531837]
uts -> uts:[4026531838]
cgroup:[4026531835]
ipc:[4026531839]
mnt:[4026531840]
net:[4026531969]
pid:[4026531836]
user:[4026531837]
uts:[4026531838]
```
## Rendu
### Fichiers
Rendez vos scripts `rev_kdb_leds.sh` et `procinfo`.

View File

@ -1,195 +0,0 @@
\newpage
# Utiliser les *cgroup*s
Les *cgroup*s (pour *Control Group*s) permettent de collecter des statistiques
sur des groupes de processus (appelés tâches) et de leur attribuer des
propriétés, comme par exemple pour leur imposer des limitations d'utilisation
de ressources ou altérer leurs priorités.
## Premiers tests
Nous allons commencer par faire quelques tests avec le *cgroup* freezer, qui
permet d'interrompre l'exécution d'un groupe de processus et de la reprendre.
### Montage du *cgroup*
En fonction de la configuration de votre système, il est possible que les
*cgroup*s ne soient pas montés au démarrage dans `/sys/fs/cgroup/`. Si vous n'avez
pas de dossier `freezer` ou si celui-ci est vide, monter-le en suivant la
procédure suivante :
```
mkdir /sys/fs/cgroup/freezer/
mount -t cgroup -o freezer none /sys/fs/cgroup/freezer/
```
Cette dernière commande monte le groupe de processus racine, pour le *cgroup*
freezer. Tous les dossiers contenu dans cette racine sont des sous-groupes de
cette dernière.
### Création d'un nouveau groupe
La première étape dans l'utilisation d'un *cgroup* est de créer un nouveau
groupe.
Pour créer un groupe, il suffit de créer un nouveau dossier dans un groupe
existant, par exemple la racine :
```
mkdir /sys/fs/cgroup/freezer/virli/
ls /sys/fs/cgroup/freezer/virli/
```
Vous avez maintenant un nouveau groupe de processus `virli` dans le *cgroup*
Freezer. Comme il s'agit d'une hiérarchie, le groupe `virli` hérite des
propriétés de son (ses) père(s).
### Rattachement de processus
Pour le moment, ce nouveau groupe ne contient aucune tâche.
Ouvrons un nouveau terminal (c'est lui que l'on va freezer), et récupérons son
PID : `echo $$`.
La liste des processus rattachés à un *cgroup* se trouve dans le fichier `task`
du groupe. Pour ajouter une tâche à ce groupe, cela se passe de cette manière :
```
echo $PID > /sys/fs/cgroup/freezer/virli/tasks
```
Il faut ici remplacer `$PID` par le PID du shell que l'on a relevé juste avant.
En validant cette commande, vous avez déplacé le processus dans ce groupe, il
n'est alors plus dans aucun autre groupe (dans ce *cgroup*, il ne bouge pas
dans les autres *cgroup*s).
### Consultation de l'état
En affichant le contenu du dossier `virli`, nous avons pu constater que
celui-ci contenait déjà un certain nombre de fichiers. Certain d'entre-eux sont
en lecture seule et permettent de lire des statistiques instantanées sur le
groupe ; tandis que d'autres sont des propriétés que vous pouvez modifier.
Nous pouvons consulter l'état de gel du groupe en affichant le contenu du
fichier\newline `/sys/fs/cgroup/freezer/virli/freezer.state`.
Pour plus d'information sur les différents fichiers présents dans ce *cgroup*,
consulter la documentation, accessible ici :
<https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt>
### Changement d'état
Faisons exécuter à notre interpréteur une commande pour voir effectivement
l'exécution s'arrêter. Si vous manquez d'inspiration, utilisez :
```
for i in $(seq 9999); do echo -n $i; sleep .1; echo -n " - "; sleep .1; done
```
Maintenant, nous avons donné l'ordre au noyau de ne plus allouer de temps de
calcul à notre shell et ses fils :
```
echo FROZEN > /sys/fs/cgroup/freezer/virli/freezer.state
```
À cet instant, vous devriez voir votre compteur s'arrêter. Pour reprendre
l'exécution :
```
echo THAWED > /sys/fs/cgroup/freezer/virli/freezer.state
```
## Script de monitoring
À nous maintenant de concevoir un script qui va enregistrer vers la base de
données créée (*metrics*) dans la partie précédente, des statistiques issues
des *cgroup*s.
### Monitoring instantané vers la console
Dans un premier temps, commençons par afficher dans la console la quantité de
mémoire utilisée par le groupe monitoré.
* Arguments de la ligne de commande :
- premier fils à lancer dans le groupe,
- intervalle de temps entre deux rafraîchissement ;
* *cgroup* `memory`;
* `memory.usage_in_bytes`.
Vous pouvez utiliser un programme comme `memhog` pour remplir rapidement votre
mémoire.
Si vous n'avez pas le *cgroup* memory, il est possible qu'il ne soit
pas activé par défaut par votre système. Si vous êtes dans ce cas, essayez d'ajouter
```
cgroup_enable=memory
```
### Monitoring vers InfluxDB
Maintenant, envoyons nos données vers la base
<https://influxdb.com/docs/v0.9/guides/writing_data.html> :
```
curl -i -XPOST 'http://172.23.42.2:8086/write?db=metrics' --data-binary \
"$my_cgroup_name memory.usage_in_bytes=$(cat .../my_cgroup_name/memory.usage_in_bytes)"
```
Pour vérifier que les données sont bien ajoutées, vous pouvez effectuez la
requête suivante dans l'interface web d'InfluxDB :
```
SELECT * from "$my_cgroup_name";
```
### Monitorer davantage de données
Liste non exhaustive de données à monitorer :
* Nombre d'IOs effectué ;
* nombre d'octets lu/écrit sur les disques ;
* temps de calcul utilisé ;
* trafic réseau généré ;
* ...
<https://www.kernel.org/doc/Documentation/cgroups/>
### Permettre à l'utilisateur de monitorer des process
Maintenant, séparer votre script en deux parties afin qu'un utilisateur normal
(non-root) puisse utiliser la partie monitoring de notre script.
Un premier script doit s'occuper de créer le(s) *cgroup*s et lui attribuer les
bons droits, tandis que le deuxième va utiliser effectuer le monitoring, sans
#### Exemple
```
42sh# ./monitor_init my_cgroup_name
42sh$ ./monitor my_cgroup_name memhog 500
```
## Rendu
### Script de monitoring
Rendez la révision la plus avancée de vos scripts de monitoring de process via
les *cgroup*s.
### Questions
1. Un même processus peut-il être dans plusieurs *cgroup*s de type différents
(freezer et cpuacct par exemple) ?
1. Que sera-t-il possible de limiter via un nouveau *cgroup* dans la prochaine
version du noyau (4.3) ?
1. Actuellement, comment peut-on limiter le nombre de processus lancés par un
utilisateur ou un groupe ?

View File

@ -1,55 +0,0 @@
\newpage
# Installation
## Noyau Linux
Ce TP requiert un noyau Linux 3.8 au minimum. De plus, il doit être
compilé avec les options suivantes :
```
General setup --->
[*] Control Group support --->
[*] Freezer cgroup subsystem
[*] Device controller for cgroups
[*] Cpuset support
[*] Include legacy /proc/<pid>/cpuset file
[*] Simple CPU accounting cgroup subsystem
[*] Group CPU scheduler --->
[*] Group scheduling for SCHED_OTHER
[*] Group scheduling for SCHED_RR/FIFO
<*> Block IO controller
-*- Namespaces support
[*] UTS namespace
[*] IPC namespace
[*] User namespace
[*] PID Namespaces
[*] Network namespace
[*] Networking support --->
Networking options --->
<*> 802.1d Ethernet Bridging
<M> 802.1Q VLAN Support
[*] Network priority cgroup
[*] Network classid cgroup
Device Drivers --->
[*] Network device support --->
<M> MAC-VLAN support
<*> Virtual ethernet pair device
Character devices --->
-*- Unix98 PTY support
[*] Support multiple instances of devpts
```
Une fois que vous aurez installé LXC, vous pouvez vérifier la compatibilité de
la configuration de votre noyau en utilisant la commande `lxc-checkconfig`.
## LXC
Pour installer LXC, utilisez le gestionnaire de paquets de votre
distribution. Toute les bonnes distributions fournissent un paquet
`lxc`. Vérifiez que la version installée est au moins la 1.0.
Aucune configuration ne devrait vous être demandé durant l'installation. Une
fois installé, exécutez la commande `lxc-checkconfig` pour vérifier que votre
noyau possède bien toutes les options nécessaires.