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 :
|
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/)
|
* [Guidelines for extended attributes](https://www.freedesktop.org/wiki/CommonExtendedAttributes/)
|
||||||
* [File-based capabilities](https://lwn.net/Articles/211883/)
|
* [File-based capabilities](https://lwn.net/Articles/211883/)
|
||||||
* [A bid to resurrect Linux capabilities](https://lwn.net/Articles/199004/)
|
* [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
|
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
|
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
|
moment où vous n'utilisez pas une bibliothèque qui abstrait complètement cette
|
||||||
plomberie, n'hésitez pas à l'utiliser !
|
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.
|
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
|
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
|
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.
|
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
|
Réduisez au maximum les capabilities, de telle sorte qu'il ne soit pas possible
|
||||||
de faire un ping dans l'environnement restreint :
|
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.
|
ê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
|
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
|
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.**
|
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
|
Nous n'avons pas encore vu de meilleure méthode pour mieux isoler
|
||||||
l'environnement que de faire un `chroot`, ajouter à votre programme cette
|
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 !
|
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
|
Pour moulinéter plusieurs étudiants en parallèle, vous allez avoir besoin de
|
||||||
plusieurs environnements identiques. Plutôt que de recopier cet environnement,
|
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
|
SOURCES = tutorial.md mount.md namespaces.md networkns.md userns.md pidns.md clone.md seccomp.md project.md
|
||||||
TEMPLATE = ../../template.tex
|
|
||||||
PANDOCOPTS = --latex-engine=xelatex \
|
PANDOCOPTS = --latex-engine=xelatex \
|
||||||
--standalone \
|
--standalone \
|
||||||
--normalize \
|
--normalize \
|
||||||
--number-sections \
|
--number-sections \
|
||||||
-M lang=frenchb \
|
--smart \
|
||||||
|
-M lang=french \
|
||||||
-M fontsize=12pt \
|
-M fontsize=12pt \
|
||||||
-M papersize=a4paper \
|
-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
|
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
|
\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*
|
## Comparaison de *namespace*
|
||||||
|
|
||||||
Écrivez un script ou un programme, `cmpns`, dans le langage courant de votre
|
Les *namespaces* d'un programme sont exposés sous forme de liens symboliques
|
||||||
choix, permettant de déterminer si deux programmes s'exécutent dans les mêmes
|
dans le répertoire `/proc/<PID>/ns/`.
|
||||||
*namespaces*.
|
|
||||||
|
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
|
### Exemples
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
42sh$ cmpns $(pgrep influxdb) $(pgrep init)
|
42sh$ ./cmpns $(pgrep influxdb) $(pgrep init)
|
||||||
|
- cgroup: differ
|
||||||
- ipc: differ
|
- ipc: differ
|
||||||
- mnt: differ
|
- mnt: differ
|
||||||
- net: differ
|
- net: differ
|
||||||
@ -21,7 +167,8 @@ choix, permettant de déterminer si deux programmes s'exécutent dans les mêmes
|
|||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
42sh$ cmpns $(pgrep init) self
|
42sh$ ./cmpns $(pgrep init) self
|
||||||
|
- cgroup: same
|
||||||
- ipc: same
|
- ipc: same
|
||||||
- mnt: same
|
- mnt: same
|
||||||
- net: 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é.
|
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*
|
## Rejoindre un *namespace*
|
||||||
|
|
||||||
Dans le langage courant de votre choix, écrivez un programme : `setns`,
|
Rejoindre un *namespace* se fait en utilisant l'appel système `setns(2)`,
|
||||||
permettant, à la manière de `unshare(1)` et `unshare(2)`, d'utiliser `setns(2)`
|
auquel on passe le *file descriptor* d'un des liens du dossier
|
||||||
via votre interpréteur.
|
`/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` ;
|
// ./a.out /proc/PID/ns/FILE cmd args...
|
||||||
* 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
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd = open(argv[1], O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
perror("open");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
```sh
|
if (setns(fd, 0) == -1)
|
||||||
42sh# setns /bin/bash
|
{
|
||||||
bash# _
|
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
|
```shell
|
||||||
42sh# setns --ipc=/proc/42/ns/ipc -p /proc/42/ns/pid /bin/echo toto
|
42sh# nsenter --uts=/proc/42/ns/uts /bin/bash
|
||||||
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
|
### `docker exec`
|
||||||
|
|
||||||
En utilisant le langage courant de votre choix, concevez l'exécutable `mlc`,
|
Si vous avez bien suivi jusque là, vous avez dû comprendre qu'un `docker exec`,
|
||||||
permettant de lancer une application dans un environnement différent (comme un
|
n'était donc rien de plus qu'un `nsenter(1)`.
|
||||||
`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.
|
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
|
## Durée de vie d'un *namespace*
|
||||||
42sh# ls newroot
|
|
||||||
bin etc home usr root proc var
|
|
||||||
|
|
||||||
42sh# mlc newroot/ /bin/bash
|
Le noyau tient à jour un compteur de référence pour chaque *namespace*. Dès
|
||||||
bash# ls ../../../
|
qu'une référence tombe à 0, le *namespace* est automatiquement libéré, les
|
||||||
bin etc home usr root proc var
|
points de montage sont démontés, les interfaces réseaux sont réattribués à
|
||||||
|
l'espace de noms initial, ...
|
||||||
|
|
||||||
bash# escape_chroot ls
|
Ce compteur évolue selon plusieurs critères, et principalement selon le nombre
|
||||||
bin etc home usr root proc var
|
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
|
Lorsque l'on a besoin de référencer un *namespace* (par exemple pour le faire
|
||||||
2
|
persister après le dernier processus), on peut utiliser un `mount bind` :
|
||||||
|
|
||||||
bash# curl http://www.linuxcontainers.org/ | md5sum
|
```shell
|
||||||
0123456789abcdef
|
42sh# touch /tmp/ns/myrefns
|
||||||
|
42sh# mount --bind /proc/<PID>/ns/mount /tmp/ns/myrefns
|
||||||
bash# ping 8.8.8.8
|
|
||||||
Operation not permitted
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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
|
### Faire persister un *namespace*
|
||||||
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
|
Il n'est pas possible de faire persister un namespace d'un reboot à l'autre.
|
||||||
`setns.tar.bz2` et `mlc` ou `mlc.tar.bz2`.
|
|
||||||
|
|
||||||
\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
|
title: Virtualisation légère -- TP n^o^ 4
|
||||||
% Jeudi 8 octobre 2015
|
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
|
Le but de ce second TP sur les mécanismes internes du noyau va nous permettre
|
||||||
durant le cours.
|
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 à
|
Le projet du cours est à rendre à <virli@nemunai.re> au plus tard le dimanche
|
||||||
<virli@nemunai.re> au plus tard le **mercredi 21 octobre 2015 à 23
|
20 novembre 2016 à 23 h 42.
|
||||||
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.
|
|
||||||
|
|
||||||
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
||||||
vous 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
|
[me](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire
|
||||||
signer votre clef et n'hésitez pas à
|
signer votre clef et n'hésitez pas à
|
||||||
[faire signer votre clef](http://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
|
[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}
|
\hypersetup{linkcolor=black}
|
||||||
\tableofcontents
|
\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