Compare commits

...

11 commits

70 changed files with 2126 additions and 228 deletions

View file

@ -14,7 +14,7 @@ privilégiés outrepassaient ces tests, tandis que les autres devaient passer le
tests de l'*effective UID*, *effective GID*, et autres groupes
supplémentaires...
Dans les années 90, ce système s'est rélévé être un peu trop basique et
Dans les années 90, ce système s'est révélé être un peu trop basique et
conduisait régulièrement à des abus, au moyen de vulnérabilités trouvées dans
les programmes *setuid root*.
@ -41,7 +41,7 @@ contient les hashs des mots de passe).
C'est ainsi qu'est apparu le `suid-bit` parmi les modes de fichiers. Lorsque
ce bit est défini sur un binaire exécutable, au moment de l'exécution, le
contexte passe à celui du propriétaire du fichier (`root` si le propriétaire
est `root`, mais cela fonctionne quelque soit le propriétaire du fichier : on
est `root`, mais cela fonctionne quel que soit le propriétaire du fichier : on
ne devient pas `root`, mais bien l'utilisateur propriétaire).\
@ -158,7 +158,7 @@ Tout d'abord, il faut noter que chaque *thread* dispose de 5 ensembles de
- ***inheritable*** (I) : est utilisé au moment de la résolution des *capabilities*
lors de l'exécution d'un nouveau processus. Il s'agit des *capabilities* qui
seront transmises au processus fil. À moins d'avoir la *capability*
seront transmises au processus fils. À moins d'avoir la *capability*
`CAP_SETPCAP`, cet ensemble ne peut pas avoir plus de *capability* que celles
présentent dans l'ensemble *permitted* ;
@ -209,7 +209,7 @@ intervenir et par exemple les ACL POSIX[^ACLPOSIX] (espace *system*) :
[^ACLPOSIX]: Les ACL POSIX sont des permissions supplémentaires qui viennent
s'ajouter aux modes standards du fichier (propriétaire, groupe, reste du
monde). Avec les ACL POSIX, on peut doonner des droits à un ou plusieurs
monde). Avec les ACL POSIX, on peut donner des droits à un ou plusieurs
utilisateurs ou groupe, de manière spécifique.
<div lang="en-US">
@ -331,7 +331,7 @@ struct vfs_cap_data {
La valeur `magic` contient la version sur 1 octet, puis 3 octets sont réservés
pour des *flags*. Actuellement un seul *flag* existe, il s'agit de
`VFS_CAP_FLAGS_EFFECTIVE` qui détermine si la liste effective de *capabilities*
du programme doit être remplie avec les *capabilities* *permitted* si elle doit
du programme doit être remplie avec les *capabilities* *permitted* ou si elle doit
rester vide (auquel cas ce sera au programme de s'ajouter les *capabilities* au
cours de l'exécution).\
@ -698,5 +698,5 @@ Et de ces quelques articles :
<https://forums.grsecurity.net/viewtopic.php?f=7&t=2522#p10271>
* [Linux Capabilities on HackTricks](https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities) :\
<https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities>
- [POSIX Access Control Lists on Linux](https://www.usenix.org/legacy/publications/library/proceedings/usenix03/tech/freenix03/full_papers/gruenbacher/gruenbacher_html/main.html) :\
* [POSIX Access Control Lists on Linux](https://www.usenix.org/legacy/publications/library/proceedings/usenix03/tech/freenix03/full_papers/gruenbacher/gruenbacher_html/main.html) :\
<https://www.usenix.org/legacy/publications/library/proceedings/usenix03/tech/freenix03/full_papers/gruenbacher/gruenbacher_html/main.html>

View file

@ -88,7 +88,7 @@ La principale différence entre les deux est la fusion des différents
sous-systèmes au sein d'une même arborescence. Dans la première version, chaque
sous-système disposait de sa propre arborescence et il fallait créer les
groupes et associer les tâches pour chaque sous-système. Avec la seconde
version, une seule création est nécessaire, quelque soit le nombre de
version, une seule création est nécessaire, quel que soit le nombre de
sous-systèmes que l'on souhaite utiliser.
:::::
@ -331,18 +331,18 @@ quantité de ressources mises à disposition à un groupe de processus.
Pour définir une limite, nous allons écrire la valeur dans le fichier
correspondant à une valeur limite, comme par exemple
`memory.max_usage_in_bytes` (v1) ou `memory.max` (v2), qui limite le nombre
`memory.limit_in_bytes` (v1) ou `memory.max` (v2), qui limite le nombre
d'octets que notre groupe de processus va pouvoir allouer au maximum :
<div lang="en-US">
```
# cgroup v1
42sh$ cat /sys/fs/cgroup/memory/virli/memory.max_usage_in_bytes
42sh$ cat /sys/fs/cgroup/memory/virli/memory.limit_in_bytes
0
# 0 = Aucune limite
42sh$ echo 4M > /sys/fs/cgroup/memory/virli/memory.max_usage_in_bytes
42sh$ echo 4M > /sys/fs/cgroup/memory/virli/memory.limit_in_bytes
# Maintenant, la limite est à 4MB, vérifions...
42sh$ cat /sys/fs/cgroup/memory/virli/memory.max_usage_in_bytes
42sh$ cat /sys/fs/cgroup/memory/virli/memory.limit_in_bytes
4194304
```
</div>
@ -360,7 +360,7 @@ max
```
</div>
Chaque *cgroup*s définit de nombreux indicateurs et possède de nombreux
Chaque *cgroup* définit de nombreux indicateurs et possède de nombreux
limiteurs, n'hésitez pas à consulter la documentation associée à chaque
*cgroup*.
@ -372,7 +372,7 @@ limites que vous avez définies :
<div lang="en-US">
```
42sh# mkdir /sys/fs/cgroup...
42sh# echo 512M > /sys/fs/cgroup.../memory.max_usage_in_bytes
42sh# echo 512M > /sys/fs/cgroup.../memory.limit_in_bytes
42sh# ./monitor group_name memhog 500
~~~ 13595 ~~~ Current memory usage: 75194368/550502400 (13%)
~~~ 13595 ~~~ Current memory usage: 150290432/550502400 (27%)

View file

@ -58,7 +58,7 @@ d'avoir de quoi bidouiller : un shell sera amplement suffisant pour commencer.
### `busybox`
Queques mots, pour commencer, à propos du projet Busybox : c'est un programme
Quelques mots, pour commencer, à propos du projet Busybox : c'est un programme
couteau-suisse qui implémente tous les binaires vitaux pour avoir un système
fonctionnel et utilisable : `ls`, `sh`, `cat`, mais aussi `init`, `mdev` (un
`udev`-like, cela permet de découvrir les périphériques attachés afin de les
@ -171,7 +171,7 @@ chroot newroot/ bash
<div lang="en-US">
```bash
tar xpf alpine-minirootfs-*.tar.xz -C newroot/
tar xpf alpine-minirootfs-*.tar.gz -C newroot/
```
</div>

View file

@ -6,7 +6,7 @@ Gestion de la mémoire
Linux a une gestion de la mémoire bien particulière[^vm-overcommit] : en effet,
par défaut, `malloc(3)` ne retournera jamais `NULL`. En se basant sur
l'euristique qu'un bloc mémoire demandé ne sera pas utilisé directement et que
de nombreux process ne feront pas un usage total des blocs qu'ils ont alloués,
de nombreux processus ne feront pas un usage total des blocs qu'ils ont alloués,
le noyau permet d'allouer plus de mémoire qu'il n'y en a réellement
disponible. La mémoire est ainsi utilisée de manière plus efficace.
@ -21,7 +21,7 @@ trouve dans l'impossibilité d'attribuer un bloc physiquement disponible, car il
n'y en a tout simplement plus (y compris via le swap).
Puisque le noyau ne peut pas honorer sa promesse et qu'il n'a plus la
possibilité de retourner `NULL` au programme qui réclamme sa mémoire (il s'agit
possibilité de retourner `NULL` au programme qui réclame sa mémoire (il s'agit
sans doute d'une simple assignation de variable à ce stade), il faut trouver
une solution si l'on veut pouvoir continuer l'exécution du programme.
@ -107,7 +107,7 @@ mémoire autorisée au sein du `cgroup` ?
:::::
Eh oui, l'OOM-killer passe également lorsqu'un `cgroup` atteint la limite de
mémoire qui lui est réservé. Dans ce cas évidemment, les processus pris en
mémoire qui lui est réservée. Dans ce cas évidemment, les processus pris en
compte sont ceux contenus dans le `cgroup`.

View file

@ -5,7 +5,7 @@ Rendu
Est attendu d'ici le TP suivant :
- le rendu des exercice de ce TP ;
- le rendu des exercices de ce TP ;
- vos réponses à [l'évaluation du cours](https://virli.nemunai.re/quiz/14).
Pour les GISTRE (et en bonus pour les SRS), [un

View file

@ -69,4 +69,4 @@ sur `rendu3`, ... ce qui vous permet d'avoir une arborescence
correspondant à ce qui est demandé, sans pour autant perdre votre
travail (ou le rendre plus difficile d'accès).
::::
:::::

305
tutorial/4/cgroupns.md Normal file
View file

@ -0,0 +1,305 @@
Le *namespace* `cgroup` {#cgroupns}
-----------------------
### Introduction
L'espace de noms `cgroup`, introduit dans Linux 4.6, permet de virtualiser la
vue d'un processus sur l'arborescence des *Control Groups*. Au sein d'un
*namespace* `cgroup`, la racine vue par le processus correspond en fait à un
sous-groupe de l'arborescence globale.
### Pourquoi virtualiser les *cgroups* ?
Sans le *namespace* `cgroup`, un processus dans un conteneur pourrait :
- Voir l'arborescence complète des *cgroups* du système hôte
- Obtenir des informations sur d'autres conteneurs
- Comprendre la structure organisationnelle du système
Le *namespace* `cgroup` permet de :
- Masquer l'arborescence globale des *cgroups*
- Faciliter la migration de conteneurs (l'arborescence reste cohérente)
- Renforcer l'isolation entre conteneurs
### Utilisation pratique
#### Observer les *cgroups* sans isolation\
Commençons par observer notre position dans l'arborescence des *cgroups* :
<div lang="en-US">
```bash
42sh$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-3.scope
```
</div>
Cette ligne nous indique que nous nous trouvons dans le *cgroup* unifié
`/user.slice/user-1000.slice/session-3.scope`.
Nous pouvons également explorer l'arborescence complète :
<div lang="en-US">
```bash
42sh$ ls /sys/fs/cgroup/
cgroup.controllers cpuset.cpus.effective memory.pressure
cgroup.max.depth cpuset.mems.effective sys-fs-fuse-connections.mount
cgroup.max.descendants dev-hugepages.mount sys-kernel-config.mount
cgroup.procs dev-mqueue.mount sys-kernel-debug.mount
cgroup.stat init.scope sys-kernel-tracing.mount
cgroup.subtree_control io.pressure system.slice
cgroup.threads memory.numa_stat user.slice
cpu.pressure memory.stat
```
</div>
#### S'isoler dans un nouveau *namespace* `cgroup`\
Créons maintenant un nouveau *namespace* `cgroup` :
<div lang="en-US">
```bash
42sh$ unshare --cgroup /bin/bash
incgroupns$ cat /proc/self/cgroup
0::/
```
</div>
Notre processus se voit maintenant à la racine de l'arborescence des *cgroups* !
En réalité, il se trouve toujours au même endroit dans l'arborescence globale,
mais sa vue a été virtualisée.
Si nous regardons le contenu de `/sys/fs/cgroup/` :
<div lang="en-US">
```bash
incgroupns$ ls /sys/fs/cgroup/
cgroup.controllers cgroup.threads io.pressure
cgroup.events cpu.pressure memory.numa_stat
cgroup.freeze cpu.stat memory.pressure
cgroup.max.depth cpuset.cpus memory.stat
cgroup.max.descendants cpuset.cpus.effective
cgroup.procs cpuset.mems
cgroup.stat cpuset.mems.effective
cgroup.subtree_control io.stat
```
</div>
Nous voyons seulement notre sous-arbre, pas l'arborescence complète du système.
#### Créer des sous-*cgroups*\
Dans notre nouveau *namespace*, nous pouvons créer des sous-groupes :
<div lang="en-US">
```bash
incgroupns$ mkdir /sys/fs/cgroup/mysubgroup
incgroupns$ ls /sys/fs/cgroup/
cgroup.controllers cgroup.threads mysubgroup
cgroup.events cpu.pressure ...
...
```
</div>
Ces sous-groupes sont en réalité créés dans notre branche de l'arborescence
globale. Depuis l'extérieur du *namespace*, ils apparaîtraient sous un chemin
différent.
### Exemple avec Docker
Docker utilise le *namespace* `cgroup` pour isoler la vue des *cgroups* dans
les conteneurs. Observons cela :
<div lang="en-US">
```bash
42sh$ docker run --rm -it debian /bin/bash
indocker# cat /proc/self/cgroup
0::/
indocker# ls /sys/fs/cgroup/
cgroup.controllers cpu.max memory.events
cgroup.events cpu.pressure memory.high
cgroup.freeze cpu.stat memory.low
cgroup.kill cpu.weight memory.max
cgroup.max.depth io.max memory.min
cgroup.max.descendants io.pressure memory.numa_stat
cgroup.procs io.stat memory.oom.group
cgroup.stat io.weight memory.pressure
cgroup.subtree_control memory.current memory.stat
cgroup.threads memory.events.local memory.swap.current
cgroup.type ...
```
</div>
Le conteneur se voit à la racine, bien qu'il soit en réalité placé dans un
sous-groupe dédié de l'arborescence globale.
### Vérifier la position réelle dans l'arborescence
Depuis l'hôte, nous pouvons voir la véritable position d'un conteneur :
<div lang="en-US">
```bash
42sh$ docker inspect mycontainer | grep -A5 CgroupParent
"CgroupParent": "",
"CgroupDriver": "systemd",
42sh$ ps aux | grep containerd-shim
root 12345 /usr/bin/containerd-shim-runc-v2 ...
42sh$ cat /proc/12345/cgroup
0::/system.slice/docker-abc123def456.scope
```
</div>
Le processus du conteneur se trouve dans un sous-groupe bien spécifique, mais
grâce au *namespace* `cgroup`, il se perçoit à la racine.
### Combinaison avec le *namespace* `mount`
Pour que l'isolation soit complète, il faut généralement combiner le *namespace*
`cgroup` avec le *namespace* `mount` afin de remonter `/sys/fs/cgroup` :
<div lang="en-US">
```bash
42sh# unshare --cgroup --mount /bin/bash
incgroupns# mount -t cgroup2 none /sys/fs/cgroup
incgroupns# ls /sys/fs/cgroup/
# Vue limitée à notre sous-arbre
```
</div>
Sans remonter le *cgroup*, nous verrions toujours l'arborescence complète via
l'ancien point de montage.
### Cas d'usage
#### Migration de conteneurs\
Lors de la migration d'un conteneur avec des outils comme CRIU (Checkpoint/Restore
In Userspace), le *namespace* `cgroup` garantit que l'arborescence des *cgroups*
reste cohérente du point de vue du processus, même si la structure de l'hôte
cible est différente.
#### Isolation de sécurité\
Sans *namespace* `cgroup`, un processus malveillant dans un conteneur pourrait :
- Lire `/sys/fs/cgroup/` pour découvrir d'autres conteneurs
- Analyser les métriques d'autres processus
- Déduire des informations sur l'architecture du système
#### Délégation de contrôle\
Le *namespace* `cgroup` permet de donner à un utilisateur non-privilégié le
contrôle d'une sous-arborescence de *cgroups*, sans lui donner accès à
l'arborescence globale.
### Exemple en C
Voici un exemple de création d'un *namespace* `cgroup` en C :
<div lang="en-US">
```c
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static int child_func(void *arg)
{
FILE *fp;
char line[256];
printf("=== Dans le namespace cgroup ===\n");
fp = fopen("/proc/self/cgroup", "r");
if (fp == NULL) {
perror("fopen");
return EXIT_FAILURE;
}
while (fgets(line, sizeof(line), fp) != NULL) {
printf(" %s", line);
}
fclose(fp);
return 0;
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int main(void)
{
pid_t pid;
FILE *fp;
char line[256];
printf("=== Avant le namespace cgroup ===\n");
fp = fopen("/proc/self/cgroup", "r");
if (fp != NULL) {
while (fgets(line, sizeof(line), fp) != NULL) {
printf(" %s", line);
}
fclose(fp);
}
printf("\n");
pid = clone(child_func, child_stack + STACK_SIZE,
CLONE_NEWCGROUP | SIGCHLD, NULL);
if (pid == -1) {
perror("clone");
exit(EXIT_FAILURE);
}
waitpid(pid, NULL, 0);
return 0;
}
```
</div>
::::: {.code}
Pour compiler et exécuter :
<div lang="en-US">
```bash
42sh$ gcc -o cgroup_ns_demo cgroup_ns_demo.c
42sh$ ./cgroup_ns_demo
=== Avant le namespace cgroup ===
0::/user.slice/user-1000.slice/session-3.scope
=== Dans le namespace cgroup ===
0::/
```
</div>
:::::
### Aller plus loin {-}
Pour plus d'informations :
- `cgroup_namespaces(7)` - page de manuel
- [Cgroup Namespace](https://lwn.net/Articles/621006/) : <https://lwn.net/Articles/621006/>
- [Control Group v2](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html) : Documentation du noyau sur cgroup v2

View file

@ -31,7 +31,7 @@ combinaison de chaque couche.
### Historique
Les premières implémentations de ce type de systèmes de fichiers est apparu
Les premières implémentations de ce type de systèmes de fichiers sont apparues
avec les LiveCD : on disposait d'une distribution Linux complètement
opérationnelle sur un support en lecture seule, mais on pouvait dédier un
espace de stockage sur son disque dur (ou en RAM, au travers d'un `tmpfs`) pour
@ -42,7 +42,7 @@ Historiquement, le noyau Linux devait être *patché* pour supporter ce type de
système de fichiers (que ce soit `unionfs` ou `aufs`, les deux principaux
*patch* apportant cette fonctionnalité). Les systèmes BSD disposent d'une
implémentation depuis au moins 1995 et c'est SunOS qui fut le premier OS à
développer cette technique dès 1986 (pour un système de fichier appelé
développer cette technique dès 1986 (pour un système de fichiers appelé
*Translucent File Service*). Pour Linux, il aura fallu attendre 2014 pour voir
l'arrivée du système de fichier OverlayFS dans un noyau sans *patch*.

View file

@ -79,7 +79,7 @@ processus se fait en fonction des *flags* qui sont passés. On retrouve donc :
Le nom du *flag* `CLONE_NEWNS` est historique et assez peu explicite
contrairement aux autres : il désigne en fait l'espace de nom `mount`.
Au départ, les *namespace*s ont étés pensés pour former un tout : une couche
Au départ, les *namespace*s ont été pensés pour former un tout : une couche
d'isolation complète pour les processus. Mais lors des développements suivants,
il s'est avéré pratique de pouvoir choisir finement de quels aspects on
souhaitait se dissocier.
@ -108,7 +108,7 @@ semblable à :
#define STACKSIZE (1024 * 1024)
static char child_stack[STACKSIZE];
int clone_flags = CLONE_CGROUP | CLONE_NEWNET | SIGCHLD;
int clone_flags = CLONE_NEWCGROUP | CLONE_NEWNET | SIGCHLD;
pid_t pid = clone(do_execvp, // First function executed by child
child_stack + STACKSIZE, // Assume stack grows downward
@ -121,13 +121,41 @@ pid_t pid = clone(do_execvp, // First function executed by child
Dans cet exemple, le processus fils créé disposera d'un nouvel espace de noms
pour les *CGroups* et disposera d'une nouvelle pile réseau.
::::: {.more}
#### Compilation des exemples C {-}
\
Pour compiler les programmes C utilisant les namespaces, aucune bibliothèque
spéciale n'est requise au-delà de la bibliothèque C standard :
<div lang="en-US">
```bash
42sh$ gcc -o mon_programme mon_programme.c
```
</div>
Notez que certains appels système utilisés avec les namespaces nécessitent la
définition de `_GNU_SOURCE` au début du fichier source pour accéder aux
extensions GNU :
<div lang="en-US">
```c
#define _GNU_SOURCE
#include <sched.h>
// ...
```
</div>
:::::
::::: {.question}
#### Quel est le rôle du *flag* `SIGCHLD` ? {-}
\
Lorsque l'on crée un nouveau processus, on ajoute l'option `SIGCHLD` afin
d'être notifié par signal lorsque notre processus fil a terminé son
d'être notifié par signal lorsque notre processus fils a terminé son
exécution. Cela permet d'être réveillé de notre `wait(2)`.
:::::

View file

@ -89,7 +89,7 @@ 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.
#### L'espace de nom `network` {.unnumbered}
#### L'espace de noms `network` {.unnumbered}
Depuis Linux 2.6.29.
@ -149,9 +149,9 @@ Lorsque l'on souhaite mesurer un écoulement de temps, la méthode naïve consis
soustraction avec l'heure de fin. Cette technique fonctionne bien, à partir du
moment où l'on est sûr que l'horloge ne remontera pas dans le temps, parce
qu'elle se synchronise ou que le changement d'heure été/hiver
intervient, ... Pour palier ces situations imprévisibles, le noyau expose une
intervient, ... Pour pallier ces situations imprévisibles, le noyau expose une
horloge dite monotone (`CLOCK_MONOTONIC`) : cette horloge démarre à un entier
abstrait et s'incrèmente chaque seconde qui passe, sans jamais sauter de
abstrait et s'incrémente chaque seconde qui passe, sans jamais sauter de
secondes, ni revenir en arrière. C'est une horloge fiable pour calculer des
intervalles de temps.

514
tutorial/4/ipcns.md Normal file
View file

@ -0,0 +1,514 @@
Le *namespace* `IPC` {#ipc-ns}
-------------------
### Introduction
L'espace de noms `IPC`, introduit dans Linux 2.6.19, isole les objets de
communication inter-processus (IPC) System V et les files de messages POSIX.
Les objets isolés incluent :
- Les files de messages System V (`msgget`, `msgsnd`, `msgrcv`)
- Les sémaphores System V (`semget`, `semop`)
- Les segments de mémoire partagée System V (`shmget`, `shmat`)
- Les files de messages POSIX (`mq_open`, `mq_send`, `mq_receive`)
Une fois dans un *namespace* IPC différent, un processus ne peut communiquer
qu'avec les processus du même *namespace* via ces mécanismes IPC.
### Pourquoi isoler les IPC ?
Sans isolation IPC, des processus dans différents conteneurs pourraient :
- Communiquer entre eux via des files de messages partagées
- Accéder à la mémoire partagée d'autres conteneurs
- Interférer avec les sémaphores d'autres applications
L'isolation IPC garantit que chaque conteneur dispose de son propre ensemble
d'objets IPC, renforçant ainsi la sécurité et l'isolation.
### Utilisation pratique
#### Observer les objets IPC du système\
La commande `ipcs` permet de lister tous les objets IPC du système :
<div lang="en-US">
```bash
42sh$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 32768 alice 600 524288 2 dest
------ Semaphore Arrays --------
key semid owner perms nsems
0x12345678 0 bob 666 1
```
</div>
Cette commande nous montre tous les objets IPC visibles dans notre *namespace*
actuel.
#### Créer une file de messages System V\
Créons d'abord une file de messages dans notre *namespace* initial :
<div lang="en-US">
```c
// ipc_create.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
int main(void)
{
key_t key;
int msgid;
// Créer une clé unique
key = ftok("/tmp", 'A');
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
// Créer une file de messages
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
printf("File de messages créée avec l'ID : %d\n", msgid);
printf("Clé : 0x%08x\n", key);
return 0;
}
```
</div>
::::: {.code}
Compilation et exécution :
<div lang="en-US">
```bash
42sh$ gcc -o ipc_create ipc_create.c
42sh$ touch /tmp/ipc_key_file
42sh$ ./ipc_create
File de messages créée avec l'ID : 0
Clé : 0x41000001
42sh$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x41000001 0 alice 666 0 0
```
</div>
:::::
#### S'isoler dans un nouveau *namespace* IPC\
Maintenant, créons un nouveau *namespace* IPC :
<div lang="en-US">
```bash
42sh$ unshare --ipc /bin/bash
inipcns$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
```
</div>
Notre nouveau *namespace* ne contient aucun objet IPC ! La file de messages
créée précédemment n'est pas visible ici.
Si nous créons une nouvelle file de messages dans ce *namespace* :
<div lang="en-US">
```bash
inipcns$ ./ipc_create
File de messages créée avec l'ID : 0
Clé : 0x41000001
inipcns$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x41000001 0 alice 666 0 0
```
</div>
Cette nouvelle file a le même ID (0) et la même clé que celle créée dans le
*namespace* initial, mais il s'agit d'un objet complètement distinct.
#### Communication entre processus dans le même *namespace*\
Créons un exemple de communication par file de messages :
<div lang="en-US">
```c
// ipc_send.c
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
struct message {
long mtype;
char mtext[100];
};
int main(void)
{
key_t key;
int msgid;
struct message msg;
key = ftok("/tmp", 'A');
msgid = msgget(key, 0666 | IPC_CREAT);
msg.mtype = 1;
snprintf(msg.mtext, sizeof(msg.mtext), "Hello from PID %d", getpid());
if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd");
exit(EXIT_FAILURE);
}
printf("Message envoyé : %s\n", msg.mtext);
return 0;
}
```
</div>
<div lang="en-US">
```c
// ipc_receive.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
struct message {
long mtype;
char mtext[100];
};
int main(void)
{
key_t key;
int msgid;
struct message msg;
key = ftok("/tmp", 'A');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
perror("msgrcv");
exit(EXIT_FAILURE);
}
printf("Message reçu : %s\n", msg.mtext);
return 0;
}
```
</div>
::::: {.code}
Compilation :
<div lang="en-US">
```bash
42sh$ gcc -o ipc_send ipc_send.c
42sh$ gcc -o ipc_receive ipc_receive.c
```
</div>
Test dans le même *namespace* :
<div lang="en-US">
```bash
# Terminal 1
42sh$ ./ipc_receive
# Attend un message...
# Terminal 2
42sh$ ./ipc_send
Message envoyé : Hello from PID 12345
# Terminal 1 reçoit :
Message reçu : Hello from PID 12345
```
</div>
:::::
#### Isolation entre *namespaces*\
Maintenant, testons avec deux *namespaces* IPC différents :
<div lang="en-US">
```bash
# Terminal 1 - namespace IPC A
42sh$ unshare --ipc /bin/bash
ipcns-A$ ./ipc_receive
# Attend un message...
# Terminal 2 - namespace IPC B (différent)
42sh$ unshare --ipc /bin/bash
ipcns-B$ ./ipc_send
Message envoyé : Hello from PID 23456
# Terminal 1 ne reçoit RIEN car les namespaces sont différents
```
</div>
Les deux processus utilisent la même clé IPC, mais comme ils sont dans des
*namespaces* différents, ils ne peuvent pas communiquer.
### Exemple avec la mémoire partagée
Voici un exemple utilisant la mémoire partagée System V :
<div lang="en-US">
```c
// shm_demo.c
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
key_t key;
int shmid;
char *data;
// Créer une clé
key = ftok("/tmp", 'S');
// Créer un segment de mémoire partagée de 1024 octets
shmid = shmget(key, 1024, 0644 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
// Attacher le segment
data = shmat(shmid, NULL, 0);
if (data == (char *)(-1)) {
perror("shmat");
exit(EXIT_FAILURE);
}
printf("Segment de mémoire partagée attaché\n");
printf("Écriture dans la mémoire partagée...\n");
sprintf(data, "Hello from PID %d in namespace", getpid());
printf("Contenu : %s\n", data);
printf("Appuyez sur Entrée pour détacher...\n");
getchar();
// Détacher
shmdt(data);
printf("Voulez-vous supprimer le segment ? (o/n) : ");
char choice = getchar();
if (choice == 'o' || choice == 'O') {
shmctl(shmid, IPC_RMID, NULL);
printf("Segment supprimé\n");
}
return 0;
}
```
</div>
::::: {.code}
Compilation et test :
<div lang="en-US">
```bash
42sh$ gcc -o shm_demo shm_demo.c
# Dans le namespace initial
42sh$ ./shm_demo &
Segment de mémoire partagée attaché
Écriture dans la mémoire partagée...
Contenu : Hello from PID 12345 in namespace
42sh$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x53000001 0 alice 644 1024 1
# Dans un nouveau namespace IPC
42sh$ unshare --ipc /bin/bash
inipcns$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
# Aucun segment visible !
```
</div>
:::::
### Files de messages POSIX
Le *namespace* IPC isole également les files de messages POSIX (qui sont
différentes des files System V) :
<div lang="en-US">
```c
// mqueue_demo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
int main(void)
{
mqd_t mq;
struct mq_attr attr;
char buffer[1024];
// Configurer les attributs de la file
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 1024;
attr.mq_curmsgs = 0;
// Créer ou ouvrir une file de messages POSIX
mq = mq_open("/myqueue", O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
printf("File de messages POSIX créée : /myqueue\n");
// Envoyer un message
sprintf(buffer, "Hello from PID %d", getpid());
if (mq_send(mq, buffer, strlen(buffer) + 1, 0) == -1) {
perror("mq_send");
exit(EXIT_FAILURE);
}
printf("Message envoyé : %s\n", buffer);
// Recevoir le message
if (mq_receive(mq, buffer, 1024, NULL) == -1) {
perror("mq_receive");
exit(EXIT_FAILURE);
}
printf("Message reçu : %s\n", buffer);
mq_close(mq);
mq_unlink("/myqueue");
return 0;
}
```
</div>
::::: {.code}
Pour compiler (nécessite la bibliothèque `librt`) :
<div lang="en-US">
```bash
42sh$ gcc -o mqueue_demo mqueue_demo.c -lrt
42sh$ ./mqueue_demo
File de messages POSIX créée : /myqueue
Message envoyé : Hello from PID 12345
Message reçu : Hello from PID 12345
```
</div>
Les files de messages POSIX peuvent être listées dans `/dev/mqueue/` :
<div lang="en-US">
```bash
42sh$ ls -l /dev/mqueue/
total 0
-rw-r--r-- 1 alice alice 80 Nov 15 10:30 myqueue
```
</div>
Dans un nouveau *namespace* IPC, ce répertoire sera vide.
:::::
### Combinaison avec d'autres *namespaces*
En pratique, le *namespace* IPC est souvent combiné avec d'autres *namespaces*
pour une isolation complète :
<div lang="en-US">
```bash
42sh# unshare --ipc --pid --mount --fork --mount-proc /bin/bash
incontainer# # Conteneur isolé avec IPC, PID et mount namespaces
```
</div>
### Nettoyage des objets IPC
Les objets IPC persistent généralement après la terminaison des processus qui
les ont créés. Pour les nettoyer :
<div lang="en-US">
```bash
# Supprimer une file de messages
42sh$ ipcrm -q <msqid>
# Supprimer un segment de mémoire partagée
42sh$ ipcrm -m <shmid>
# Supprimer un sémaphore
42sh$ ipcrm -s <semid>
# Ou supprimer par clé
42sh$ ipcrm -Q 0x41000001
```
</div>
Lorsqu'un *namespace* IPC est détruit (tous les processus terminés), tous ses
objets IPC sont automatiquement nettoyés.
### Aller plus loin {-}
Pour plus d'informations :
- `ipc_namespaces(7)` - page de manuel
- `ipcs(1)` - lister les objets IPC
- `ipcrm(1)` - supprimer les objets IPC
- `svipc(7)` - aperçu de System V IPC
- `mq_overview(7)` - aperçu des files de messages POSIX

View file

@ -8,5 +8,5 @@ abstract: |
Le but de cette seconde partie sur les mécanismes internes du noyau
va nous permettre d'utiliser les commandes et les appels système
relatifs aux espaces de noms du noyau Linux ainsi que d'appréhender
la complexité des sytèmes de fichiers.
la complexité des systèmes de fichiers.
...

View file

@ -40,7 +40,7 @@ Cette commande nous dévoile déjà de nombreuses choses :
contenerisé » (dans un *namespace*). Le processus initial de la machine se
retrouve donc dans des espaces de nom, tout comme les processus d'un conteneur.
- On aperçoit un genre de hiérarchie dans certain cas.
- On aperçoit un genre de hiérarchie dans certains cas.
- La première colonne nous renseigne sur l'identifiant du *namespace*.

View file

@ -168,7 +168,7 @@ esclave ne propagera pas ses nouveaux points de montage à son *maître*.
<div lang="en-US">
```bash
# Suite de l'exemple précédent
cd /mnt/test-slave
mkdir /mnt/test-slave
# Duplication de l'accroche, sans s'occuper des éventuels sous-accroches
mount --bind /mnt/test-shared /mnt/test-slave

View file

@ -189,7 +189,7 @@ On considère préalablement que l'environnement est propice à la réalisation
```
42sh# mkdir -p /mnt/newroot
42sh# mount -t tmpfs none /mnt/newroot
42sh# wget https://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-minirootfs-3.14.8-x86_64.tar.gz
42sh# wget https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-minirootfs-3.20.3-x86_64.tar.gz
42sh# tar xpf alpine-minirootfs-*.tar.gz -C /mnt/newroot
42sh# cd /
```

View file

@ -0,0 +1,532 @@
\newpage
Récapitulatif des *namespaces*
===============================
Nous venons d'explorer en détail les huit types d'espaces de noms disponibles
dans le noyau Linux. Avant de conclure cette partie, récapitulons les concepts
clés et les meilleures pratiques pour utiliser efficacement ces mécanismes
d'isolation.
## Tableau comparatif des *namespaces*
Le tableau suivant résume les caractéristiques principales de chaque type
d'espace de noms :
| *Namespace* | Flag `clone()` | Depuis | Isole | Privilèges requis* |
|-------------|----------------|--------|-------|-------------------|
| **mount** | `CLONE_NEWNS` | 2.4.19 | Points de montage | Oui |
| **UTS** | `CLONE_NEWUTS` | 2.6.19 | Hostname, domaine NIS | Oui |
| **IPC** | `CLONE_NEWIPC` | 2.6.19 | Objets IPC System V et POSIX | Oui |
| **PID** | `CLONE_NEWPID` | 2.6.24 | Arbre de processus | Oui |
| **network** | `CLONE_NEWNET` | 2.6.29 | Interfaces, routes, ports | Oui |
| **user** | `CLONE_NEWUSER` | 3.8 | UIDs, GIDs, capabilities | **Non** |
| **cgroup** | `CLONE_NEWCGROUP` | 4.6 | Vue arborescence cgroup | Oui |
| **time** | `CLONE_NEWTIME` | 5.6 | Horloges monotones | Oui |
*\* Sauf si combiné avec un *namespace* `user`*
## Ce que chaque *namespace* vous permet de faire
### `mount` -- Systèmes de fichiers isolés
**Fonctionnalité :** Isole l'arborescence des points de montage.
**Permet de :**
- Créer un système de fichiers racine différent (`pivot_root`, `switch_root`)
- Monter/démonter des partitions sans affecter l'hôte
- Implémenter un `chroot` sécurisé (impossible de s'échapper)
- Utiliser des systèmes de fichiers en couches (OverlayFS)
**Cas d'usage typiques :**
- Conteneurs avec leur propre filesystem
- Environnements de build isolés
- Systèmes d'installation et de récupération
### `UTS` -- Identité système
**Fonctionnalité :** Isole le nom d'hôte et le domaine NIS.
**Permet de :**
- Donner un nom unique à chaque conteneur
- Éviter les conflits de nommage
- Faciliter l'identification des conteneurs
**Cas d'usage typiques :**
- Conteneurs avec des noms de machine distincts
- Environnements multi-tenants
- Tests de configurations réseau
### `IPC` -- Communication inter-processus
**Fonctionnalité :** Isole les objets IPC (files de messages, sémaphores,
mémoire partagée).
**Permet de :**
- Empêcher la communication IPC entre conteneurs
- Isoler les applications utilisant IPC System V ou POSIX
- Protéger contre les fuites d'informations via IPC
**Cas d'usage typiques :**
- Isolation de sécurité entre conteneurs
- Applications legacy utilisant IPC System V
- Environnements de test pour applications IPC
### `PID` -- Arbre de processus virtualisé
**Fonctionnalité :** Isole l'arbre des processus avec renumérotation.
**Permet de :**
- Avoir un processus `init` (PID 1) par conteneur
- Masquer les processus de l'hôte et des autres conteneurs
- Gérer le cycle de vie des processus indépendamment
- Récolter les processus orphelins proprement
**Cas d'usage typiques :**
- Conteneurs avec gestion complète du cycle de vie
- Isolation de sécurité (masquage des processus)
- Environnements avec plusieurs niveaux d'`init`
**Particularité :** Le processus doit forker après `unshare(CLONE_NEWPID)`
pour que l'isolation soit effective.
### `network` -- Pile réseau dédiée
**Fonctionnalité :** Isole les interfaces réseau, routes, règles de pare-feu et
ports.
**Permet de :**
- Donner une pile réseau complète à chaque conteneur
- Réutiliser les mêmes ports dans différents conteneurs
- Configurer des routes et pare-feu indépendants
- Créer des topologies réseau complexes (bridges, VLAN, MACVLAN)
**Cas d'usage typiques :**
- Conteneurs avec leur propre IP
- Tests réseau isolés
- Micro-services avec ports identiques
- Simulations de topologies réseau
**Note :** Nécessite généralement des interfaces virtuelles (`veth`, `macvlan`,
etc.) pour connecter les conteneurs.
### `user` -- Privilèges virtualisés
**Fonctionnalité :** Isole les UIDs, GIDs et les capabilities.
**Permet de :**
- Être root dans le conteneur sans être root sur l'hôte
- Lancer des conteneurs sans privilèges (rootless containers)
- Mapper des plages d'UIDs entre conteneur et hôte
- Déléguer des capacités limitées
**Cas d'usage typiques :**
- Conteneurs rootless (podman, systemd-nspawn)
- Environnements multi-utilisateurs sécurisés
- Délégation contrôlée de privilèges
**Particularité :** Seul *namespace* créable sans privilèges. Permet ensuite de
créer tous les autres types de *namespaces*.
**Attention :** Historiquement source de vulnérabilités ; à utiliser avec
précaution.
### `cgroup` -- Vue filtrée des control groups
**Fonctionnalité :** Virtualise la vue de l'arborescence des cgroups.
**Permet de :**
- Masquer l'organisation des cgroups de l'hôte
- Faciliter la migration de conteneurs
- Empêcher la fuite d'informations système via `/sys/fs/cgroup`
- Déléguer la gestion de sous-arbres de cgroups
**Cas d'usage typiques :**
- Renforcement de l'isolation de sécurité
- Migration de conteneurs (CRIU)
- Systèmes multi-tenants
**Note :** Souvent combiné avec un *namespace* `mount` pour remonter
`/sys/fs/cgroup`.
### `time` -- Horloges virtualisées
**Fonctionnalité :** Virtualise `CLOCK_MONOTONIC` et `CLOCK_BOOTTIME`.
**Permet de :**
- Décaler les horloges monotones pour un conteneur
- Préserver l'état temporel lors de migrations
- Accélérer le temps pour les tests
**Cas d'usage typiques :**
- Migration de conteneurs avec CRIU
- Tests d'applications sensibles au temps
- Simulations temporelles
**Limitation :** Ne virtualise **pas** l'heure système (CLOCK_REALTIME).
**Particularité :** Les offsets doivent être configurés avant de créer le
premier processus enfant.
## Combinaisons recommandées
Les *namespaces* sont rarement utilisés seuls. Voici les combinaisons courantes
selon les besoins d'isolation :
### Isolation minimale (développement)
Pour un environnement de développement simple avec un filesystem isolé :
<div lang="en-US">
```bash
unshare --mount --pid --fork --mount-proc /bin/bash
```
</div>
**Isolation :** Filesystem et processus
**Avantages :** Rapide, simple, suffisant pour tester des installations
**Limitations :** Partage le réseau, les utilisateurs, IPC avec l'hôte
### Conteneur standard (production light)
Pour une isolation raisonnable sans réseau séparé :
<div lang="en-US">
```bash
unshare --mount --uts --ipc --pid --fork --mount-proc /bin/bash
```
</div>
**Isolation :** Filesystem, processus, IPC, hostname
**Avantages :** Bonne isolation, partage le réseau de l'hôte
**Cas d'usage :** Services système, environnements de build
### Conteneur avec réseau isolé
Pour une isolation complète incluant le réseau :
<div lang="en-US">
```bash
unshare --mount --uts --ipc --net --pid --fork --mount-proc /bin/bash
```
</div>
**Isolation :** Filesystem, processus, IPC, hostname, réseau
**Avantages :** Isolation réseau complète
**Note :** Nécessite configuration réseau supplémentaire (veth, bridge, etc.)
### Conteneur rootless (sans privilèges)
Pour lancer un conteneur en tant qu'utilisateur normal :
<div lang="en-US">
```bash
unshare --user --map-root-user \
--mount --uts --ipc --net --pid \
--fork --mount-proc /bin/bash
```
</div>
**Isolation :** Complète + virtualisation des privilèges
**Avantages :** Aucun privilège requis, sécurité renforcée
**Cas d'usage :** Environnements multi-utilisateurs, CI/CD non-privilégiée
### Conteneur type Docker (isolation maximale)
Pour une isolation complète similaire à Docker :
<div lang="en-US">
```bash
unshare --user --map-root-user \
--mount --uts --ipc --net --pid --cgroup \
--fork --mount-proc /bin/bash
```
</div>
**Isolation :** Tous les namespaces (sauf `time`)
**Avantages :** Isolation maximale
**Note :** Équivalent approximatif de `docker run --rm -it`
## Ordre de création des *namespaces*
L'ordre dans lequel les *namespaces* sont créés peut être important :
1. **`user` en premier** : Si vous voulez créer des *namespaces* sans
privilèges, commencez toujours par le *namespace* `user`.
2. **`mount` avant `pid`** : Pour pouvoir remonter `/proc` correctement avec
l'option `--mount-proc`.
3. **`pid` avec `--fork`** : N'oubliez jamais l'option `--fork` avec le
*namespace* `pid`, sinon le processus actuel ne sera pas réellement isolé.
4. **`time` et `cgroup` avant le fork** : Ces deux namespaces nécessitent des
configurations avant la création du premier processus enfant.
## Commandes essentielles
Voici un récapitulatif des commandes les plus utiles pour travailler avec les
*namespaces* :
### Création et entrée dans des *namespaces*
<div lang="en-US">
```bash
# Créer de nouveaux namespaces et exécuter une commande
unshare [options] [commande]
# Principales options :
# -m, --mount Namespace mount
# -u, --uts Namespace UTS
# -i, --ipc Namespace IPC
# -n, --net Namespace network
# -p, --pid Namespace PID
# -U, --user Namespace user
# -C, --cgroup Namespace cgroup
# -T, --time Namespace time
# -f, --fork Fork avant d'exécuter la commande (requis avec --pid)
# --mount-proc Monter /proc après création du namespace
# Entrer dans les namespaces d'un processus existant
nsenter [options] [commande]
nsenter --target <PID> --all [commande]
```
</div>
### Inspection des *namespaces*
<div lang="en-US">
```bash
# Lister tous les namespaces du système
lsns
# Lister les namespaces d'un processus spécifique
lsns -p <PID>
# Voir les namespaces d'un processus
ls -l /proc/<PID>/ns/
# Comparer les namespaces de deux processus
readlink /proc/<PID1>/ns/net
readlink /proc/<PID2>/ns/net
```
</div>
### Gestion réseau (namespace network)
<div lang="en-US">
```bash
# Créer un namespace network nommé
ip netns add <nom>
# Lister les namespaces network
ip netns list
# Exécuter une commande dans un namespace network
ip netns exec <nom> <commande>
# Créer une paire veth
ip link add veth0 type veth peer name veth1
# Déplacer une interface dans un namespace
ip link set veth1 netns <nom>
```
</div>
## Appels système
Pour les développeurs, voici les appels système principaux :
<div lang="en-US">
```c
// Créer un nouveau processus dans de nouveaux namespaces
pid_t clone(int (*fn)(void *), void *stack, int flags, void *arg);
// flags: CLONE_NEWNS, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWPID,
// CLONE_NEWNET, CLONE_NEWUSER, CLONE_NEWCGROUP, CLONE_NEWTIME
// Se dissocier de namespaces actuels
int unshare(int flags);
// Rejoindre les namespaces d'un autre processus
int setns(int fd, int nstype);
// fd: file descriptor de /proc/<PID>/ns/<type>
// nstype: 0 ou CLONE_NEW* pour vérification de type
// Obtenir un file descriptor vers un processus (pour setns)
int pidfd_open(pid_t pid, unsigned int flags);
```
</div>
## Bonnes pratiques
### Sécurité
1. **Privilèges minimaux** : Utilisez le *namespace* `user` quand possible pour
éviter d'exécuter des conteneurs en tant que root.
2. **Combinaison d'isolations** : Un seul namespace ne suffit généralement pas
pour une isolation sécurisée. Combinez plusieurs namespaces.
3. **User namespace avec précaution** : Bien que pratique, le *namespace*
`user` a été source de nombreuses vulnérabilités. Assurez-vous d'utiliser un
noyau récent et correctement patché.
4. **Nettoyage des ressources** : Les namespaces persistent tant qu'ils sont
référencés. Utilisez `umount` pour les bind mounts qui maintiennent des
namespaces actifs.
5. **Capabilities** : Même dans un *namespace* `user`, les capabilities sont
limitées à l'intérieur du namespace. Ne comptez pas sur elles pour des
actions hors namespace.
### Performance
1. **Coût minimal** : La création de namespaces a un coût négligeable en termes
de performance. N'hésitez pas à les utiliser.
2. **Réseau** : Le type d'interface virtuelle utilisé (`veth`, `macvlan`,
`ipvlan`) peut avoir un impact significatif sur les performances réseau.
3. **OverlayFS vs device-mapper** : Pour le stockage, OverlayFS est
généralement plus performant que les snapshots LVM (device-mapper).
### Débogage
1. **`lsns` est votre ami** : Utilisez `lsns` pour comprendre rapidement
l'organisation des namespaces sur votre système.
2. **Inspecter /proc** : Les fichiers dans `/proc/<PID>/ns/` sont essentiels
pour comprendre dans quels namespaces se trouve un processus.
3. **Bind mounts pour persistence** : Utilisez `mount --bind` sur les fichiers
de `/proc/<PID>/ns/` pour maintenir un namespace actif même après la
terminaison du processus.
4. **Logs et `strace`** : En cas de problème, `strace` peut vous montrer
exactement quels appels système échouent et pourquoi.
## Limitations et pièges courants
### Namespace `PID`
- **Oubli du `--fork`** : Sans fork, le processus actuel reste dans l'ancien
namespace PID.
- **Bash et PID 1** : Bash peut avoir des comportements inattendus en tant que
PID 1 (voir pidns.md pour les détails).
- **`/proc` doit être remonté** : Sinon `ps`, `top` montrent tous les processus
du système.
### Namespace `mount`
- **Propagation des montages** : Attention aux politiques `shared`, `slave`,
`private`. Par défaut, utilisez `mount --make-rslave /` pour éviter les
fuites.
- **`pivot_root` vs `chroot`** : `pivot_root` est plus sûr mais plus complexe à
mettre en œuvre.
### Namespace `network`
- **Pas d'interface par défaut** : Un nouveau namespace network ne contient
qu'une interface loopback désactivée. Il faut configurer le réseau
manuellement.
- **Complexité de la configuration** : Bridges, veth, routes, NAT... la
configuration réseau peut devenir complexe rapidement.
### Namespace `user`
- **MappingUID/GID limité** : Sans privilèges, vous ne pouvez mapper qu'un
seul UID (le vôtre vers root).
- **Fichiers setuid** : Ne fonctionnent pas correctement dans un namespace
user.
- **Restrictions par distribution** : Certaines distributions désactivent le
namespace user par défaut (voir setup.md).
### Namespace `time`
- **Configuration unique** : Les offsets ne peuvent être configurés qu'une fois,
avant le premier fork.
- **Pas d'heure réelle** : Ne virtualise pas `CLOCK_REALTIME`, seulement les
horloges monotones.
## Pour aller plus loin
### Documentation officielle
- Pages de manuel : `namespaces(7)`, `pid_namespaces(7)`, `user_namespaces(7)`,
`mount_namespaces(7)`, `network_namespaces(7)`, `ipc_namespaces(7)`,
`uts_namespaces(7)`, `cgroup_namespaces(7)`, `time_namespaces(7)`
- Documentation du noyau :
<https://www.kernel.org/doc/Documentation/admin-guide/namespaces/>
### Articles de référence
- [Namespaces in operation (série LWN)](https://lwn.net/Articles/531114/) :
série d'articles exhaustive par Michael Kerrisk
<https://lwn.net/Articles/531114/>
- [Anatomy of a user namespaces vulnerability](https://lwn.net/Articles/543273/) :
pour comprendre les enjeux de sécurité
<https://lwn.net/Articles/543273/>
### Projets utilisant les namespaces
- **Docker/Podman/containerd** : Containerisation d'applications
- **systemd-nspawn** : Conteneurs légers pour systemd
- **LXC/LXD** : Conteneurs système complets
- **Flatpak/Snap** : Sandboxing d'applications
- **Chrome/Firefox** : Sandboxing de processus de rendu
- **bubblewrap** : Outil de sandboxing générique
## Conclusion
Les *namespaces* Linux constituent une brique fondamentale de la virtualisation
légère moderne. Leur conception modulaire permet de créer des niveaux
d'isolation sur mesure, depuis un simple environnement de développement isolé
jusqu'à des conteneurs de production complets.
La maîtrise des *namespaces* nécessite de comprendre :
1. **Ce que chaque type isole** et dans quel contexte l'utiliser
2. **Comment les combiner** pour atteindre le niveau d'isolation souhaité
3. **Leurs interactions** et dépendances mutuelles
4. **Leurs limitations** et les cas particuliers à gérer
Combinés avec les *cgroups* (pour la limitation de ressources) et les *security
modules* (AppArmor, SELinux pour les politiques de sécurité), les *namespaces*
permettent de construire des environnements d'exécution sûrs, isolés et
performants.
Que vous développiez des outils de containerisation, construisiez des
plateformes cloud, ou cherchiez simplement à mieux isoler vos applications, les
*namespaces* sont un outil incontournable de l'écosystème Linux moderne.

View file

@ -30,7 +30,7 @@ l'autre.
:::::
Afin d'amener du réseau à notre nouvel espace de nom, il va falloir lui
attribuer des interface. En fait, nous allons pouvoir déplacer nos interfaces
attribuer des interfaces. En fait, nous allons pouvoir déplacer nos interfaces
réseaux, dans le *namespace* vers lequel elle doit être accessible. Une
interface donnée ne peut se trouver que dans un seul *namespace* à la fois.
@ -82,13 +82,13 @@ virli
42sh# ip netns
foo virli
42sh# ip netns exec foo ip link
1: lo: <LOOPBACK> mut 65536 qdisc noop state DOWN mode DEFAULT group default
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```
</div>
Les fichiers utilisés par `ip netns` ne sont donc rien de plus que des
*bind-mount*. Ce qui explique qu'ils soient persistant même sans processus
*bind-mount*. Ce qui explique qu'ils soient persistants même sans processus
s'exécutant à l'intérieur.
:::::
@ -99,7 +99,7 @@ des interfaces :
<div lang="en-US">
```
42sh# ip netns exec virli ip link
1: lo: <LOOPBACK> mut 65536 qdisc noop state DOWN mode DEFAULT group default
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```
</div>
@ -175,11 +175,13 @@ struct nlmsghdr {
```
</div>
Parmi les fonctionnalités de Netlink, nous allons utiliser le module NIS
(Network Interface Service)[^RFC3549NIS]. Il spécifie le format par lequel
Parmi les fonctionnalités de Netlink, nous allons utiliser RTNetlink
(Routing Netlink)[^RFC3549RTNETLINK]. Il spécifie le format par lequel
doivent commencer les données liées à l'administration d'interfaces réseau.
Le module RTNetlink utilise la famille `NETLINK_ROUTE` pour communiquer avec
le noyau concernant les interfaces, routes et règles de routage.
[^RFC3549NIS]: Network Interface Service Module <https://tools.ietf.org/html/rfc3549#section-2.3.3.1>
[^RFC3549RTNETLINK]: RTNetlink - Linux Routing Socket <https://tools.ietf.org/html/rfc3549#section-2.3.3.1>
<div lang="en-US">
```c

View file

@ -98,7 +98,7 @@ donc été alloué à un processus d'initialisation de `bash`, qui s'est termin
depuis.\
Le comportement du noyau, lorsque le PID 1 se termine, est de lancer un *kernel
panic* (car c'est un processus indispensable, notamment de part son rôle de
panic* (car c'est un processus indispensable, notamment de par son rôle de
parent pour tous les processus orphelin). Au sein d'un *namespace* `PID` qui
n'est pas le *namespace* racine, le noyau appelle la fonction
`disable_pid_allocation` qui retire le *flag* `PIDNS_HASH_ADDING` de l'espace

View file

@ -12,7 +12,7 @@ Vous allez continuer aujourd'hui le projet qui s'étendra depuis le TP précéde
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.
pour qu'il s'exécute indépendamment 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, du shell, etc.

View file

@ -62,4 +62,4 @@ sur `rendu4`, ... ce qui vous permet d'avoir une arborescence
correspondant à ce qui est demandé, sans pour autant perdre votre
travail (ou le rendre plus difficile d'accès).
::::
:::::

View file

@ -38,7 +38,7 @@ main(int argc, char *argv[])
Ce programme prend au minimum deux arguments :
- le chemin d'un fichier d'espace de nom que l'on souhaite rejoindre (le chemin
vers le lien symbolique donc) ;
- le programme (et ses arguments) que l'on souhaite souhaite exécuter une fois
- le programme (et ses arguments) que l'on souhaite exécuter une fois
que l'on a rejoint l'espace de noms ciblé.
Dans un premier temps, on ouvre le fichier passé en paramètre afin d'obtenir un

263
tutorial/4/timens.md Normal file
View file

@ -0,0 +1,263 @@
Le *namespace* `time` {#time-ns}
---------------------
### Introduction
L'espace de noms `time`, introduit dans Linux 5.6, permet de virtualiser les
horloges `CLOCK_MONOTONIC` et `CLOCK_BOOTTIME` pour des processus. Cela est
particulièrement utile pour la migration de conteneurs ou pour tester des
applications sensibles au temps.
::::: {.warning}
Contrairement à ce que son nom pourrait suggérer, le *namespace* `time` ne
permet **pas** de virtualiser l'heure système (l'horloge temps réel). Il ne
virtualise que les compteurs monotones.
:::::
### Utilisation pratique
Voyons comment utiliser le *namespace* `time` pour modifier le temps
d'exécution perçu par un processus.
#### Lire les horloges monotones\
Commençons par observer les valeurs actuelles de nos horloges :
<div lang="en-US">
```bash
42sh$ cat /proc/uptime
123456.78 987654.32
```
</div>
Le premier nombre représente le temps depuis le démarrage du système (équivalent
à `CLOCK_BOOTTIME`), et le second le temps CPU cumulé en mode idle.
On peut également utiliser un petit programme C pour afficher les horloges :
<div lang="en-US">
```c
#include <stdio.h>
#include <time.h>
int main(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("CLOCK_MONOTONIC: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_BOOTTIME, &ts);
printf("CLOCK_BOOTTIME: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);
return 0;
}
```
</div>
::::: {.code}
Pour compiler ce programme :
<div lang="en-US">
```bash
42sh$ gcc -o show_clocks show_clocks.c
```
</div>
:::::
#### Créer un *namespace* `time` avec décalage\
Pour créer un nouveau *namespace* `time`, il faut d'abord le dissocier puis
configurer les décalages (offsets) avant de lancer un processus enfant.
Le fichier `/proc/<PID>/timens_offsets` permet de configurer les décalages pour
les horloges. Le format est le suivant :
<div lang="en-US">
```
<clock-id> <offset-secs> <offset-nanosecs>
```
</div>
`<clock-id>` peut être :
- `monotonic` pour `CLOCK_MONOTONIC`
- `boottime` pour `CLOCK_BOOTTIME`
Voici un script shell pour créer un conteneur avec un temps décalé de 10 jours
dans le futur :
<div lang="en-US">
```bash
42sh# unshare --time /bin/bash
intimens# echo "monotonic 864000 0" > /proc/self/timens_offsets
intimens# echo "boottime 864000 0" > /proc/self/timens_offsets
intimens# exec /bin/bash
intimens-child# ./show_clocks
CLOCK_MONOTONIC: 987654.123456789
CLOCK_BOOTTIME: 987654.123456789
```
</div>
::::: {.warning}
**Important :** Les décalages doivent être configurés **avant** de créer le
premier processus enfant. Une fois qu'un processus enfant existe dans le
*namespace*, les offsets ne peuvent plus être modifiés. C'est pourquoi nous
utilisons `exec` pour remplacer le shell actuel.
:::::
#### Exemple en C\
Voici un exemple complet en C pour créer un *namespace* `time` avec un décalage :
<div lang="en-US">
```c
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
static int child_func(void *arg)
{
struct timespec ts;
// Attendre que le parent configure les offsets
sleep(1);
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("Child - CLOCK_MONOTONIC: %ld.%09ld\n",
ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_BOOTTIME, &ts);
printf("Child - CLOCK_BOOTTIME: %ld.%09ld\n",
ts.tv_sec, ts.tv_nsec);
return 0;
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int main(void)
{
pid_t pid;
int fd;
char path[256];
struct timespec ts;
// Afficher les horloges du parent
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("Parent - CLOCK_MONOTONIC: %ld.%09ld\n",
ts.tv_sec, ts.tv_nsec);
// Se dissocier du namespace time
if (unshare(CLONE_NEWTIME) == -1) {
perror("unshare");
exit(EXIT_FAILURE);
}
// Configurer les offsets (décalage de 1000 secondes)
fd = open("/proc/self/timens_offsets", O_WRONLY);
if (fd == -1) {
perror("open timens_offsets");
exit(EXIT_FAILURE);
}
if (write(fd, "monotonic 1000 0\n", 17) != 17) {
perror("write monotonic offset");
exit(EXIT_FAILURE);
}
if (write(fd, "boottime 1000 0\n", 16) != 16) {
perror("write boottime offset");
exit(EXIT_FAILURE);
}
close(fd);
// Créer un processus enfant qui verra les nouvelles horloges
pid = clone(child_func, child_stack + STACK_SIZE,
SIGCHLD, NULL);
if (pid == -1) {
perror("clone");
exit(EXIT_FAILURE);
}
waitpid(pid, NULL, 0);
return 0;
}
```
</div>
::::: {.code}
Pour compiler :
<div lang="en-US">
```bash
42sh$ gcc -o time_ns_demo time_ns_demo.c
42sh$ ./time_ns_demo
Parent - CLOCK_MONOTONIC: 123456.789012345
Child - CLOCK_MONOTONIC: 124456.789012345
Child - CLOCK_BOOTTIME: 124456.789012345
```
</div>
On voit bien le décalage de 1000 secondes entre le parent et l'enfant.
:::::
### Cas d'usage pratiques
#### Migration de conteneurs\
Lors de la migration d'un conteneur d'une machine à une autre, les processus
peuvent avoir des timers ou des alarmes basées sur `CLOCK_MONOTONIC`. Si on ne
virtualise pas ces horloges, tous ces timers seraient incorrects après la
migration.
Le *namespace* `time` permet de restaurer l'état des horloges monotones lors
de la restauration d'un conteneur.
#### Tests et simulation\
Pour tester des applications qui utilisent des timeouts ou des mécanismes de
retry avec backoff exponentiel, il peut être utile de virtualiser le temps pour
accélérer les tests sans modifier le code de l'application.
### Limitations
- Seules les horloges `CLOCK_MONOTONIC` et `CLOCK_BOOTTIME` peuvent être
virtualisées
- L'heure système (`CLOCK_REALTIME`) reste identique pour tous les processus
- Les offsets ne peuvent être configurés qu'une seule fois, avant la création
du premier processus enfant
- Les décalages ne peuvent pas être négatifs (on ne peut pas remonter dans le
temps)
### Aller plus loin {-}
Pour plus d'informations sur le *namespace* `time`, consultez :
- `time_namespaces(7)` - page de manuel
- [Time Namespace support](https://lwn.net/Articles/766089/) : <https://lwn.net/Articles/766089/>
- [Checkpoint/Restore and Time Namespace](https://lwn.net/Articles/801652/) : <https://lwn.net/Articles/801652/>

View file

@ -14,6 +14,6 @@ abstract: |
Les exercices de ce cours sont à rendre au plus tard le mardi 15
novembre 2022 à 23 h 42. Consultez les sections matérialisées par un
bandeau jaunes et un engrenage pour plus d'informations sur les
bandeau jaune et un engrenage pour plus d'informations sur les
éléments à rendre.
...

View file

@ -22,7 +22,7 @@ Il existe différentes typologies d'images:
MongoDB,...
- certaines contiennent un programme spécifique: on peut vouloir utiliser
`git`, le client `MySQL`, `emacs` ou bien encore le navigateur Chrome;
- d'autes enfin servent pour développer et étendre les possibilités. Ce sont
- d'autres enfin servent pour développer et étendre les possibilités. Ce sont
souvent des images utilisées comme bases pour les deux types que l'on a vus:
Debian, Alpine, Windows,...
@ -62,13 +62,13 @@ Les conteneurs
On parle d'un conteneur pour désigner une instance en cours d'exécution.
Lorsqu'on lance un conteneur, une copie de l'image est créée sur le disque,
puis le système crée une isolation, pour contenir et restreindre l'exécution
aux seules données de l'image.
Lorsqu'on lance un conteneur, une couche lecture/écriture est créée à partir
de l'image, puis le système crée une isolation, pour contenir et restreindre
l'exécution aux seules données de l'image.
Les conteneurs sont par nature **immuables**: on exécute le contenu d'une
image déjà construite, on n'y change pas le code qui est exécuté et on n'y fait
pas de changements qui pourrait altérer son fonctionnement. L'idée directrice dans
pas de changements qui pourraient altérer son fonctionnement. L'idée directrice dans
le design des conteneurs est de toujours être en mesure de pouvoir relancer un
conteneur ailleurs et s'attendre exactement au même comportement.
@ -90,6 +90,6 @@ avec son historique (la manière dont il a été installé, les mises à jour qu
ont été appliquées, les configurations modifiées et les données qui ont été
apportées au fil du temps ...). On ne peut pas dans ces circonstances
remplacer un conteneur système par un autre, tout comme on ne peut pas
remplacer une machine virtuelle que l'on a installée et configuré à la main.
remplacer une machine virtuelle que l'on a installée et configurée à la main.
:::::

View file

@ -3,7 +3,7 @@
#### Que fait Drone pour « surveiller » un dépôt ? {-}
\
Grâce aux permissions de que Drone a récupéré lors de la connexion OAuth à
Grâce aux permissions que Drone a récupéré lors de la connexion OAuth à
Gitea, il peut non seulement lire et récupérer le code des différents dépôts
auxquels vous avez accès, mais il peut aussi changer certains paramètres.
@ -17,5 +17,5 @@ dépôt, sous l'onglet *Déclencheurs Web*.
À chaque fois qu'un événement va se produire sur le dépôt, Gitea va prévenir
Drone qui décidera si l'évènement doit conduire à lancer l'intégration continue
ou non, selon les instructions qu'on lui a donné dans la configuration du
ou non, selon les instructions qu'on lui a données dans la configuration du
dépôt.

View file

@ -6,7 +6,7 @@ lors que la compilation est terminée, car nous n'en faisons rien.
::::: {.exercice}
Ajoutons donc une nouvelle règle à notre `.droneci.yml` pour placer le binaire
Ajoutons donc une nouvelle règle à notre `.drone.yml` pour placer le binaire
au sein de la liste des fichiers téléchargeables aux côtés des tags.
Vous aurez sans doute besoin de :

View file

@ -13,7 +13,7 @@ en tête.\
Ces contraintes : tant liées à la **sécurité** (il faut s'assurer
qu'un service n'utilise pas une bibliothèque vulnérable par exemple, donc soit
utilisé sur un système à jour, et qu'il ne tourne pas en `root`), qu'à la
**disponibilité** (si le service est mal codé est contient beaucoup de fuites
**disponibilité** (si le service est mal codé et contient beaucoup de fuites
mémoire, il ne faut pas que les autres services présents sur la même machine en
pâtissent).
@ -29,7 +29,7 @@ Le même principe est aussi valable pour Python, Ruby, ... : les développeurs
ont toujours eu tendance à vouloir utiliser les dernières améliorations d'un
langage, mais les administrateurs système n'ont alors pas de paquets stables
dans la distribution. En effet, les distributions stables telles que Debian,
RedHat ou CentOS ont des cycles de vie assez long et se concentrent plus sur la
RedHat ou CentOS ont des cycles de vie assez longs et se concentrent plus sur la
stabilité.\
Cette stabilité est obtenue grâce à l'utilisation de versions éprouvées des
langages et des bibliothèques, qui assurent un temps de maintenance et de
@ -197,6 +197,6 @@ conçu par Netflix, qui est un programme qui va casser de manière aléatoire de
éléments de l'environnement de production. Le but est de provoquer sciemment
des pannes, des latences, ... à n'importe quel niveau du produit, afin d'en
tester (brutalement certes) sa résilience. Cela oblige les développeurs, les
opérationnels et les architectes à concevoir des services hautement tolérant
opérationnels et les architectes à concevoir des services hautement tolérants
aux pannes, ce qui fait que le jour où une véritable panne survient, elle n'a
aucun impact sur la production (enfin on espère !).

View file

@ -1,4 +1,4 @@
### Et ensuite ?
Nous avons vu une manière possible de distribuer notre projet. Essayons-en une
autre parmis celles proposées.
autre parmi celles proposées.

View file

@ -7,7 +7,7 @@ Vous pouvez néanmoins tester les plugins
[`scp`](http://plugins.drone.io/appleboy/drone-scp/) ou
[`ansible`](http://plugins.drone.io/drone-plugins/drone-ansible/) si vous avez
une machine virtuelle avec une connexion SSH. N'hésitez pas à l'ajouter à votre
`.droneci.yml`.
`.drone.yml`.
### Profitons !

View file

@ -12,7 +12,7 @@ La commande que nous allons utiliser pour lancer Renovatebot est la suivante :
<div lang="en-US">
```shell
docker container run --name renovate --network my_ci_net \
-e RENOVATE_ENDPOINT="http://gitea:3000/api/v1/" RENOVATE_PLATFORM=gitea \
-e RENOVATE_ENDPOINT="http://gitea:3000/api/v1/" -e RENOVATE_PLATFORM=gitea \
-e RENOVATE_TOKEN -e RENOVATE_GIT_AUTHOR="Renovatebot <renovate@sample>" \
-e RENOVATE_AUTODISCOVER=true -e RENOVATE_LOG_LEVEL=info -d \
renovate/renovate

View file

@ -2,7 +2,7 @@ Dès que le conteneur sera lancé, nous devrions voir apparaître une ou plusieu
*pull-requests* pour le projet `youp0m`. Si votre CI est configurée
correctement, des tests automatiques seront lancés.
Le conteneur s'arrête dès qu'il a terminé d'analysé tous les dépôts. Vous
Le conteneur s'arrête dès qu'il a terminé d'analyser tous les dépôts. Vous
devrez le relancer si vous attendez une nouvelle action de la part de
Renovatebot. Il est courant de le lancer entre chaque heure et 2 ou 4 fois par
jour.
@ -18,7 +18,7 @@ consultez la liste des éléments de configuration :
Ne soyez pas effrayé par la liste interminable d'options. Il est vrai que la
première fois, on peut se sentir submergé de possibilités, mais il faut noter
que le projet arriver avec des options par défaut plutôt correctes, et que l'on
que le projet arrive avec des options par défaut plutôt correctes, et que l'on
peut facilement avoir une configuration commune pour tous nos dépôts, à travers
les *presets*.
@ -26,7 +26,7 @@ Un certain nombre de *presets* sont distribués par défaut, voici la liste
(humainement lisible cette fois) :
<https://docs.renovatebot.com/presets-default/>
Voici un exemple de configuration que vous pouvez utilisé comme base de tous
Voici un exemple de configuration que vous pouvez utiliser comme base de tous
vos projets :
<div lang="en-US">
@ -69,7 +69,7 @@ Voici un exemple de fichier `default.json` que vous pourriez vouloir utiliser :
Attention, on ne le répétera jamais assez, mais Renovatebot peut vite devenir
infernal, car il va créer de nombreuses *pull-requests*, inlassablement. Il
convient de rapidement activer la fusion automatique des mises à jour pour
lesquelles vous avez confiances et pour lesquelles vous ne feriez qu'appuyer
lesquelles vous avez confiance et pour lesquelles vous ne feriez qu'appuyer
sur le bouton de fusion, sans même tester vous-même. La fonctionnalité est
décrite en détail dans la documentation[^RENOVATE_AUTOMERGE] et explique les
différentes stratégies. Néanmoins, il est nécessaire d'avoir une bonne suite de

View file

@ -1,13 +1,13 @@
Autres outils indispensables
----------------------------
### Maintient à jour des dépendances
### Maintien à jour des dépendances
Une opération fastidieuse, souvent oubliée sitôt le projet envoyé en
production, c'est la mise à jour des dépendances applicatives. Fastidieux car
il faut d'une part être informé qu'une mise à jour est disponible, c'est-à-dire
qu'il faut suivre les mails, parfois nombreux, informant des nouvelles
*releases*, parfois il s'agir de newslettre, ou encore parfois aucune
*releases*, parfois il s'agit de newsletters, ou encore parfois aucune
notification ne peut être programmée, il faut se rendre régulièrement sur un
site pour savoir si oui ou non une mise à jour est disponible.

View file

@ -64,7 +64,7 @@ accès au périphérique correspondant au compteur.
### Conteneur LXC
LXC peut s'utiliser de multiple manière différentes, y compris avec des images
LXC peut s'utiliser de multiples manières différentes, y compris avec des images
OCI. Choisissez la méthode qui vous semble la plus appropriée, il est attendu
au moins un script pour lancer notre conteneur, s'il est différent d'un

View file

@ -2,7 +2,7 @@ Voici à quoi pourrait ressembler le playbook Ansible démarrant notre agent Dro
<div lang="en-US">
```yaml
- name: Launch drone runer
- name: Launch drone runner
docker_container:
name: droneci-runner
image: "drone/drone-runner-docker:1"

View file

@ -44,10 +44,10 @@ fundation*, notamment les [Compute Module 3 et
4](https://www.raspberrypi.com/products/compute-module-4/) et les [Raspberry Pi
Zero 2 W](https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/), ainsi
que de nombreux PCB fait par l'entreprise, à base de micro-contrôleurs AVR,
lorsqu'il est nécessaire de pour s'interfacer avec des équipements
lorsqu'il est nécessaire pour s'interfacer avec des équipements
propriétaires non prévu pour l'immotique.
Une grosse partie des travaux est donc réalisé avec un noyau Linux, sur du
Une grosse partie des travaux est donc réalisée avec un noyau Linux, sur du
matériel très performant, pour de l'embarqué.
\
@ -68,7 +68,7 @@ entraver la stabilité de la plate-forme en cas de déploiement d'un module
défaillant.
Vous êtes également chargés de jeter les bases du système d'intégration continu
des modules. (La partie déploiement continu, sera réalisé plus tard par
des modules. (La partie déploiement continu, sera réalisée plus tard par
l'équipe développant le nouveau système de base, suivant le meilleur outil que
vous retiendrez.)
\
@ -90,5 +90,5 @@ quelques tests automatiques. Puis nous publierons automatiquement le binaire
`linky2influx` comme fichier associé à un tag au sein de l'interface web du
gestionnaire de versions.
Nous testerons enfin différentes solution pour déployer notre binaire, afin
Nous testerons enfin différentes solutions pour déployer notre binaire, afin
d'établir quelle est la solution adéquate.

View file

@ -13,7 +13,7 @@ le projet `youp0m`, que l'on connaît déjà bien.
\
Dans un premier temps, on voudra juste compiler notre projet, pour s'assurer
que chaque *commmit* poussé ne contient pas d'erreur de compilation (dans
que chaque *commit* poussé ne contient pas d'erreur de compilation (dans
l'environnement défini comme étant celui de production, donc avec une version
précise des outils de compilation). Ensuite, nous ajouterons quelques tests
automatiques, puis nous publierons automatiquement le binaire `youp0m` comme

View file

@ -9,7 +9,6 @@ contiendra les paramètres d'exécution.
<div lang="en-US">
```yaml
version: "3.9"
services:
influxdb:
...
@ -30,11 +29,14 @@ Ce fichier est un condensé des options que nous passons habituellement au
#### `version`
Notons toutefois la présence d'une ligne `version` ; il ne s'agit pas de la
version de vos conteneurs, mais de la version du format de fichier
`docker-compose` qui sera utilisé. Sans indication de version, la version
originale sera utilisée, ne vous permettant pas d'utiliser les dernières
fonctionnalités de Docker.
Le champ `version` était utilisé pour spécifier la version du format de fichier
`docker-compose`. Depuis les versions récentes de Docker Compose, ce champ est
**déprécié** et n'est plus nécessaire. Docker Compose détecte automatiquement
les fonctionnalités utilisées dans le fichier.
Vous pouvez donc **omettre ce champ** dans vos nouveaux fichiers
`docker-compose.yml`. Si vous rencontrez d'anciens fichiers avec `version:
"3.9"` ou similaire, sachez qu'il peut être supprimé sans problème.
#### `services`

View file

@ -32,7 +32,7 @@ docker container run -p 8086:8086 -d --name mytsdb influxdb:1.8
::::: {.warning}
Remarquez que nous n'utilisons pas la version 2 d'InfluxDB. Sa mise en
place est plus contraignantes pour faire de simples tests. Si vous
place est plus contraignante pour faire de simples tests. Si vous
souhaitez tout de même utiliser la dernière version de la stack TICK,
vous pouvez consulter le `README` du conteneur sur le Docker Hub:\
<https://hub.docker.com/_/influxdb>
@ -147,6 +147,14 @@ tar xzv -C /tmp
```
</div>
::::: {.more}
Vous pouvez vérifier la dernière version disponible sur la
[page des releases](https://github.com/influxdata/telegraf/releases)
et ajuster la variable `V` en conséquence.
:::::
Puis, lançons *Telegraf*:
<div lang="en-US">
@ -205,7 +213,7 @@ lancé, celui-ci va régulièrement envoyer des métriques de cette machine.
### Afficher les données collectées
À vous de jouer pour lancer le conteneur
[*Chronograf*](https://store.docker.com/images/chronograf).
[*Chronograf*](https://hub.docker.com/_/chronograf).
::::: {.question}

View file

@ -11,7 +11,7 @@ monitoring, d'un simple:
<div lang="en-US">
```
42sh$ docker-compose up
42sh$ docker compose up
```
</div>

View file

@ -59,7 +59,7 @@ difficulté du rebond.
Un certain nombre de capabilities Linux sont retirées par Docker au
moment de l'exécution du conteneur, on peut utiliser les options
`--cap-add` et `--cap-drop` pour respectivement ajouter et retirer une
`--cap-add` et `--cap-drop` pour respectivement ajouter et retirer des
capabilities.
Notez que l'option `--privileged` ne retire aucune capabilities à
@ -114,3 +114,34 @@ On peut ensuite l'appliquer à un conteneur Docker:
sleep: cannot read realtime clock: Operation not permitted
```
</div>
::::: {.exercice}
## Sécuriser la stack TICK
Maintenant que vous connaissez les mécanismes de limitation de ressources et
de sécurisation, appliquez-les à votre stack de monitoring TICK.
### Limites de ressources
1. Ajoutez des limites mémoire à vos conteneurs dans le `docker-compose.yml`:
- InfluxDB: limiter à 512 Mo de RAM
- Chronograf: limiter à 256 Mo de RAM
- Telegraf: limiter à 128 Mo de RAM
2. Configurez les partages CPU pour que:
- InfluxDB obtienne 75% des ressources CPU disponibles (768)
- Chronograf et Telegraf se partagent les 25% restants (128 chacun)
### Sécurité
3. Identifiez quelles capabilities sont nécessaires pour Telegraf afin de
collecter les métriques système.
4. Testez le lancement de vos conteneurs avec l'option `--read-only` pour le
système de fichiers. Quels volumes devez-vous ajouter pour que les
conteneurs fonctionnent correctement en lecture seule?
:::::

View file

@ -12,7 +12,7 @@ communauté, et parfois même appropriées par Docker.
Dans cette partie, nous allons avoir besoin du plugin `docker-compose`.
L'équipe en charge du projet met à disposition un exécutable que nous pouvons
téléchargeant depuis <https://github.com/docker/compose/releases>.
télécharger depuis <https://github.com/docker/compose/releases>.
Ajoutez l'exécutable dans le dossier des plugins : `$HOME/.docker/cli-plugins`
(sans oublier de `chmod +x` !).
@ -21,7 +21,7 @@ Ajoutez l'exécutable dans le dossier des plugins : `$HOME/.docker/cli-plugins`
Autrefois, `docker-compose` était un script tiers que l'on utilisait
indépendamment de Docker. Le projet, historiquement écrit en Python, a été
entièrement réécrit récemment afin qu'il s'intégre mieux dans l'écosystème.
entièrement réécrit récemment afin qu'il s'intègre mieux dans l'écosystème.
Vous trouverez encore de nombreux articles vous incitant à utiliser
`docker-compose`. Dans la plupart des cas, vous pouvez simplement remplacer par

View file

@ -44,5 +44,5 @@ s'agit d'un mécanisme de séries temporelles (*Time Series*) moderne, que l'on
peut utiliser pour stocker toute sorte de données liées à un indice temporel.
La pile logicielle TICK propose de collecter des métriques, en les enregistrant
dans une base de données adaptées et permet ensuite de les ressortir sous
dans une base de données adaptée et permet ensuite de les ressortir sous
forme de graphiques ou de les utiliser pour faire des alertes intelligentes.

View file

@ -13,7 +13,7 @@ Consulter les journaux
----------------------
La première étape consiste bien souvent à regarder ce que le conteneur affiche
sur ses sorties standard et d'erreur. Lorsqu'il est lancé en monde *daemon*, il
sur ses sorties standard et d'erreur. Lorsqu'il est lancé en mode *daemon*, il
convient d'utiliser la commande:
<div lang="en-US">
@ -76,7 +76,7 @@ docker container top cntr_name
```
</div>
Cela liste tous les processus rattaché au conteneur nommé: à la fois les
Cela liste tous les processus rattachés au conteneur nommé: à la fois les
processus démarrés par le `run`, mais également les éventuels processus
rattachés par `exec`.

View file

@ -38,9 +38,9 @@ ici, puisque c'est ce volume qui assure la persistance des images.
:::::
Nos images sont bien persistantes d'une instance à l'autre de notre contenu.
Nos images sont bien persistantes d'une instance à l'autre de notre conteneur.
Nous voici prêt à déployer en production notre service, sans crainte de perdre
Nous voici prêts à déployer en production notre service, sans crainte de perdre
les jolies contributions. Mais... est-ce que ce sera suffisant pour répondre aux
milliers de visiteurs attendus?

View file

@ -42,16 +42,16 @@ docker container run --publish 8080:8080 registry.nemunai.re/youp0m
</div>
Cet argument va faire effectuer à Docker une étape supplémentaire lorsqu'il
démarerra le conteneur: il va devoir mettre en place une redirection du port
démarrera le conteneur: il va devoir mettre en place une redirection du port
de notre système local (8080) vers le port 8080 du conteneur.
![Le port 8080 est redirigé vers le conteneur](published-ports.png "Le port 8080 est redirigé vers le conteneur"){ width=70% }
::::: {.question}
#### Peut-on démarrer plusieurs conteurs utilisant le même port de notre système local? {-}
#### Peut-on démarrer plusieurs conteneurs utilisant le même port de notre système local? {-}
Si l'on essai de lancer deux fois la commande de notre dernier `run`, nous
Si l'on essaie de lancer deux fois la commande de notre dernier `run`, nous
obtenons l'erreur suivante:
<div lang="en-US">

View file

@ -33,7 +33,7 @@ Le daemon assemble ensuite l'image et met en place les différents éléments
d'isolation pour préparer l'exécution de notre conteneur.
Enfin, il lance la commande qui sera le premier processus du conteneur. Cette
commande fait parti des métadonnées de l'image. Le processus ainsi lancé est un
commande fait partie des métadonnées de l'image. Le processus ainsi lancé est un
peu particulier: il obtient les mêmes caractéristiques que le PID 1 de notre
système ('init').
@ -57,11 +57,12 @@ afficher beaucoup de contenu pour faire son choix. Aussi, il est souvent plus
pratique d'aller explorer les registres en passant directement par leur interface
web.
Vous trouverez forcément votre bonheur parmi les images proposées par les deux
Vous trouverez forcément votre bonheur parmi les images proposées par les trois
principaux registres :
- <https://hub.docker.com/>
- <https://quay.io/>
- <https://ghcr.io/> (GitHub Container Registry)
::::: {.warning}
@ -155,7 +156,7 @@ registre tiers :
<div lang="en-US">
```bash
docker container un registry.nemunai.ie/hello-world
docker container run registry.nemunai.re/hello-world
```
</div>
@ -164,9 +165,9 @@ nécessaire de récupérer l'image. Elle est ensuite exécutée. Vous devriez do
obtenir un résultat en deux parties, similaire à la sortie suivante :
<div lang="en-US">
```bash
```
Unable to find image 'registry.nemunai.re/hello-world:latest' locally
latest: Pulling from library/hello-world
latest: Pulling from hello-world
2db29710123e: Already exists
Digest: sha256:7d246653d0511db2a6b2e0436cfd0e52ac8c066000264b3ce63331ac66dca625
Status: Downloaded newer image for registry.nemunai.re/hello-world:latest
@ -357,7 +358,7 @@ c'est toujours le daemon qui exécute directement les commandes et gère les
entrées et sorties standards et d'erreur. Avec l'option `--interactive`, on
s'assure que l'entrée standard ne sera pas fermée (`close(2)`). Nous demandons
également l'allocation d'un TTY, sans quoi `bash` ne se lancera pas en mode
interractif[^bashnointer].
interactif[^bashnointer].
[^bashnointer]: Mais il sera possible de l'utiliser sans allouer de TTY, comme
dans cet exemple:

View file

@ -54,7 +54,7 @@ distribution:
### Sous Windows et macOS
Bien que les fonctionnalités de contenerisation de Docker que nous utiliserons
Bien que les fonctionnalités de conteneurisation de Docker que nous utiliserons
ne soient disponibles que sous Linux, il est possible d'utiliser Docker de
manière déportée: le daemon Docker tournera dans une machine virtuelle Linux,
mais vous pourrez interagir avec lui via votre ligne de commande habituelle.
@ -80,6 +80,12 @@ car la licence est gratuite pour un usage éducatif ou personnel.
Notez que cela ne concerne pas le projet ou le binaire Docker: ceux-ci restent
libres. Seules les applications Docker Desktop sont concernées.
Si vous souhaitez des alternatives libres, vous pouvez considérer :
- **Rancher Desktop** : <https://rancherdesktop.io/>
- **Podman Desktop** : <https://podman-desktop.io/>
- **Colima** (macOS uniquement) : <https://github.com/abiosoft/colima>
:::::
[^DockerSubscription]: <https://www.docker.com/blog/updating-product-subscriptions/>
@ -175,4 +181,12 @@ Cette action n'est pas anodine d'un point de vue de la sécurité:
<https://docs.docker.com/engine/security/#docker-daemon-attack-surface>
Les membres du groupe `docker` peuvent obtenir les privilèges root sur la
machine. Pour un environnement plus sécurisé, considérez :
- **Docker en mode rootless** : permet d'exécuter le daemon Docker sans
privilèges root
- **Podman** : alternative à Docker fonctionnant sans daemon et sans privilèges
root par défaut
:::::

View file

@ -8,7 +8,7 @@ indiquent que le service cherche à se connecter à une base de données. Il va
donc falloir lier notre interface d'administration à [un conteneur
MariaDB](https://hub.docker.com/_/mariadb).
Ne vous embêtez pas avec les mots de passes des services, initialisez la base
Ne vous embêtez pas avec les mots de passe des services, initialisez la base
de données avec le nom d'utilisateur et le mot de passe par défaut. Vous les
obtiendrez en lisant la documentation de l'image fic-admin:
<https://hub.docker.com/r/nemunaire/fic-admin/>

View file

@ -88,3 +88,21 @@ docker network connect NETWORK CONTAINER
Lorsque plusieurs conteneurs ont rejoint un réseau utilisateur, ils peuvent
mutuellement se découvrir grâce à un système de résolution de nom basé sur leur
nom de conteneur.
::::: {.question}
#### Et l'option `--link` ? {-}
Vous trouverez peut-être dans d'anciens tutoriels l'utilisation de l'option
`--link` pour connecter des conteneurs. Cette option est **dépréciée** depuis
Docker 1.13 et ne devrait plus être utilisée.
Les réseaux utilisateurs (`user-defined bridge networks`) sont la méthode
recommandée pour faire communiquer des conteneurs, car ils offrent :
- Une meilleure isolation réseau
- La découverte automatique des noms (DNS intégré)
- La possibilité de connecter/déconnecter dynamiquement des conteneurs
- Plus de flexibilité et de sécurité
:::::

View file

@ -3,4 +3,113 @@
Garder des secrets
==================
TODO
Lorsque nous déployons des applications conteneurisées, celles-ci ont souvent
besoin d'informations sensibles : mots de passe de base de données, clefs API,
certificats, tokens d'authentification, etc. La gestion de ces secrets est
cruciale pour la sécurité de nos applications.
## Ce qu'il ne faut pas faire
Avant de voir les bonnes pratiques, commençons par les erreurs courantes à
éviter absolument :
::::: {.warning}
**Ne jamais inclure de secrets dans une image Docker !**
Lorsque vous construisez une image avec un `Dockerfile`, tout ce qui est copié
dans l'image y reste, même si vous le supprimez dans une couche ultérieure. Les
images peuvent être inspectées, et les secrets peuvent être extraits de
l'historique des couches.
:::::
::::: {.warning}
**Ne jamais passer de secrets via la ligne de commande !**
Les arguments de la ligne de commande sont visibles via `ps`, `docker inspect`,
et sont souvent enregistrés dans l'historique du shell.
<div lang="en-US">
```bash
# MAUVAIS EXEMPLE - À NE PAS FAIRE
docker container run -e DB_PASSWORD=monmotdepasse myapp
```
</div>
:::::
## Variables d'environnement avec fichiers
Pour éviter que les secrets n'apparaissent dans l'historique de commandes ou
dans les processus, Docker permet de charger les variables d'environnement
depuis un fichier :
<div lang="en-US">
```bash
docker container run --env-file secrets.env myapp
```
</div>
Le fichier `secrets.env` contiendrait :
<div lang="en-US">
```
DB_PASSWORD=monmotdepasse
API_KEY=ma_clef_secrete
```
</div>
::::: {.warning}
Pensez à ajouter ce fichier dans votre `.gitignore` ou `.dockerignore` pour éviter qu'il se retrouve accidentellement dans la nature !
:::::
## Montage de fichiers secrets
Une approche plus sécurisée consiste à monter les secrets comme fichiers en
lecture seule dans le conteneur :
<div lang="en-US">
```bash
docker container run \
--mount type=bind,source=$HOME/.secrets/db_password,target=/run/secrets/db_password,readonly \
myapp
```
</div>
L'application peut ensuite lire le secret depuis `/run/secrets/db_password`.
Cette approche présente plusieurs avantages :
- Les secrets ne sont pas visibles via `docker inspect`
- Les secrets peuvent avoir des permissions restrictives sur l'hôte
- Les applications peuvent être conçues pour lire les secrets depuis des
fichiers, ce qui est compatible avec les systèmes d'orchestration
## Docker Secrets (Docker Swarm)
Docker propose un système de gestion de secrets natif, mais celui-ci n'est
disponible qu'en mode Swarm.
<div lang="en-US">
```bash
# Création d'un secret (nécessite Swarm mode)
echo "monmotdepasse" | docker secret create db_password -
# Utilisation dans un service
docker service create --secret db_password myapp
```
</div>
Dans ce mode, les secrets sont :
- Chiffrés pendant le transit et au repos
- Montés dans `/run/secrets/` sous forme de fichiers temporaires en RAM
- Distribués uniquement aux conteneurs qui en ont besoin
- Gérés de manière centralisée

View file

@ -3,12 +3,12 @@
Stockage de données applicatives
================================
Il est généralement toujours possible d'écrire dans le système de fichier de
Il est généralement toujours possible d'écrire dans le système de fichiers de
notre conteneur. (Cela n'affecte pas l'image, chaque conteneur est démarré à
partir de l'image originale.) Cependant, il n'est pas recommandé de chercher à
stocker des données ainsi. En effet, il n'est pas aisé de récupérer ces données
une fois l'exécution du conteneur terminée; les données peuvent même être
détruite si on a lancé le conteneur avec l'option `--rm`.
détruites si on a lancé le conteneur avec l'option `--rm`.
Docker met donc à notre disposition plusieurs mécanismes pour que les données
de nos applications persistent et soient prêtes à être migrées d'un conteneur à
@ -41,6 +41,31 @@ exemple: <http://10.42.12.23/dQw4w9WgXcQ.mp4>
:::::
::::: {.question}
Pour activer le listing de répertoires avec nginx, il faudrait créer un fichier
de configuration personnalisé avec la directive `autoindex on;` et le monter
dans le conteneur via un volume supplémentaire.
:::::
::::: {.question}
#### Syntaxes `-v` et `--mount` {-}
Vous remarquerez dans cette partie l'utilisation de deux syntaxes différentes
pour monter des volumes :
- **`-v` ou `--volume`** : syntaxe courte et concise
(ex: `-v ~/Downloads:/usr/share/nginx/html:ro`)
- **`--mount`** : syntaxe explicite avec des paires clé-valeur
(ex: `--mount type=bind,source=...,target=...,readonly`)
La syntaxe `--mount` est recommandée pour sa clarté, mais `-v` reste très
utilisée car plus courte. Les deux sont équivalentes en fonctionnalité.
:::::
## Les volumes
Les volumes sont des espaces créés via Docker (il s'agit d'objets Docker). Ils

View file

@ -3,14 +3,14 @@ Composition de Docker
Docker est une suite d'outils de haut niveau, permettant d'utiliser des
*conteneurs*. Le projet en lui-même utilise de nombreuses dépendances,
originellement développées par l'entreprise Docker Inc., puis laissé dans le
originellement développées par l'entreprise Docker Inc., puis laissées dans le
domaine public lors des efforts de standardisation en 2015.
Commençons par planter le décor, en détaillant les principes de base de Docker.
### Séparation des compétences
Le projet s'article autour d'un daemon lancé au démarrage de la machine, avec
Le projet s'articule autour d'un daemon lancé au démarrage de la machine, avec
lequel on interagit via un client (le programme `docker`). La communication
entre le daemon et le client s'effectuant sur une API REST généralement au
travers d'une socket.
@ -67,13 +67,13 @@ défaut.
### Les plugins Docker
L'architecture de Docker est devenue très modulable. Le projet est parti dans
de nombreuses directions, chacun voulant tirer la couverture vers soit, et
de nombreuses directions, chacun voulant tirer la couverture vers soi, et
l'équipe maintenant le projet a parfois eu du mal à arbitrer les bonnes choses
à ajouter ou non au projet.
Afin de palier aux besoins complémentaires, parfois accessoires, parfois
Afin de pallier les besoins complémentaires, parfois accessoires, parfois
salvateurs, un système de plugins a été intégré. Il permet d'appeler d'autres
programmes comme s'il s'agissait de composant de Docker.
programmes comme s'il s'agissait de composants de Docker.
Certains plugins ajoutent des options à la ligne de commande (`docker-compose`,
`docker-scan`, `docker-buildx` ...). D'autres ajoutent des typologies de
@ -85,7 +85,7 @@ utilisateurs de notre machine ou dans `$HOME/.docker/` si l'on veut l'installer
seulement pour nous.
Par exemple, les plugins ajoutant des commandes iront dans
`$HOME/.docker/cli-plugins`. Par exemple, si l'on souhaite pouvoir disposer de
`$HOME/.docker/cli-plugins`. Si l'on souhaite pouvoir disposer de
la commande `docker compose`, on téléchargera le plugin vers l'emplacement:
`$HOME/.docker/cli-plugins/docker-compose`.

View file

@ -2,7 +2,7 @@
Un outil complet pour indexer et chercher des vulnérabilités est
[`Clair`](https://github.com/coreos/clair/), du projet CoreOS. À partir des
informations mises à disposition par les équipes de sécurités des principales
informations mises à disposition par les équipes de sécurité des principales
distributions, cela alimente en continu une base de données qui sera accédé au
moment de l'analyse.
@ -57,7 +57,7 @@ export POSTGRES_PASSWORD=$(openssl rand -base64 16)
Parmi les volumes partagés avec `clair`, il y a un dossier
`./clair_config`. Notez le `./` au début, qui indique que le dossier sera
recherché relativement par rapport à l'emplacement du `docker-compsose.yml`.
recherché relativement par rapport à l'emplacement du `docker-compose.yml`.
Dans ce dossier, vous devez placer un exemplaire du fichier de configuration
dont un [exemple se trouve dans le dépôt du

View file

@ -4,7 +4,7 @@ Open Container Initiative
=========================
Formée en juin 2015, l'Open Container Initiative (OCI) a pour but d'établir le
standard commun aux programmes de contenerisation, afin d'éviter une
standard commun aux programmes de conteneurisation, afin d'éviter une
fragmentation de l'écosystème.
## Spécifications
@ -101,7 +101,7 @@ est utilisé pour sélectionner le bon manifest correspondant au système.
Le format des [couches de système de
fichiers](https://github.com/opencontainers/image-spec/blob/master/layer.md)
sont spécifiées : il est nécessaire de passer par des formats standards (comme
est spécifié : il est nécessaire de passer par des formats standards (comme
les tarballs), contenant éventuellement des fichiers et dossiers spéciaux
représentant les modifications ou les suppressions éventuelles de la couche.
@ -125,5 +125,5 @@ Cela permet de récupérer des images, mais aussi d'en envoyer, en gérant
\
Nous allons voir plus en détails, dans les chapitres suivantes, ce que l'on
Nous allons voir plus en détails, dans les chapitres suivants, ce que l'on
peut tirer de ces spécifications, en décortiquant des usages précis.

View file

@ -10,7 +10,7 @@ de nouveaux services.
Dans les sections à venir, nous allons essayer de récupérer la configuration et
les couches de quelques images courantes (Debian, Ubuntu, `hello-world`, ...) :
dans un premier temps en nous préoccupant simplement de la couche la plus basse
(le système de baase). Puis nous verrons dans le chapitre suivant comment gérer
(le système de base). Puis nous verrons dans le chapitre suivant comment gérer
les autres couches.

View file

@ -135,9 +135,12 @@ et le dossier courant par défaut (`cwd`). Pensez également à faire un volume
entre un dossier de votre home (ou temporaire, peu importe), afin de pouvoir
stocker les photos (dossier `/srv/images`)[^chmod].
[^chmod]: faites attention aux droits du dossier que vous partagez. Le plus
[^chmod]: **Attention aux droits du dossier que vous partagez.** Le plus
simple pour l'instant serait d'attribuer les permissions `0777` à la
source, temporairement.
source, temporairement. **AVERTISSEMENT :** les permissions `0777` donnent
un accès complet (lecture, écriture, exécution) à tous les utilisateurs du
système. **N'utilisez JAMAIS ces permissions en production** et pensez à les
révoquer après l'exercice (`chmod 755` ou `chmod 700` selon vos besoins).
Pour cette étape, considérez que vous avez réussi si vous voyez s'afficher :

View file

@ -3,10 +3,41 @@
Mise en place
=============
* `docker-compose`
* `venv` (Python3)
* `jq`
* `runc`
* `containerd`
* `ctr`
* `linuxkit`
Les exercices suivants nécessiteront l'installation de plusieurs outils.
- `docker-compose`
- `venv`
- `jq` : un outil en ligne de commande pour manipuler du JSON.
- `runc` : *runtime* de bas niveau pour les conteneurs OCI.
- `containerd` : daemon de gestion de conteneurs.
- `ctr` : client en ligne de commande pour `containerd` (généralement installé avec `containerd`).
## LinuxKit {-}
Un outil pour construire des systèmes Linux minimaux et sécurisés.
**Installation :**
<div lang="en-US">
```bash
wget https://github.com/linuxkit/linuxkit/releases/latest/download/linuxkit-linux-amd64
sudo install -m 755 linuxkit-linux-amd64 /usr/local/bin/linuxkit
```
</div>
### QEMU {-}
Nécessaire pour tester les images LinuxKit.
**Debian/Ubuntu :**
<div lang="en-US">
```bash
sudo apt-get install qemu-system-x86
```
</div>
**Arch Linux :**
<div lang="en-US">
```bash
sudo pacman -S qemu-full
```
</div>

View file

@ -14,7 +14,7 @@ exécution. Puis, lorsqu'il aura terminé, celui-ci sera passé dans un statut
Parmi ses attributions, `init`, le PID 1 de notre système, est le processus qui
récupère les processus orphelins du système. Lorsque le parent direct d'un
processus meurt, ses fils sont reparenté sous le processus `init` et ils
processus meurt, ses fils sont reparentés sous le processus `init` et ils
obtiennent alors comme `ppid` 1. Ils ne conservent pas le PID de leur défunt
parent.
@ -29,7 +29,7 @@ l'argument `PR_SET_CHILD_SUBREAPER`.
:::::
Docker procure une isolation, notamment au travers du *namespace* PID : les
processus faisant parti du même *namespace* ne voient seulement qu'une partie
processus faisant partie du même *namespace* ne voient seulement qu'une partie
de l'arbre de processus de l'hôte, et notamment, un PID 1 est recréé, il s'agit
du premier processus à s'exécuter dans le *namespace*.
@ -107,7 +107,7 @@ shell.
Voici donc une raison supplémentaire de préférer `tini` à `bash` (ou à rien du
tout). D'autant plus qu'à moins d'avoir préparé la fin d'exécution, `bash` ne
retournera pas le code d'erreur de la commande que l'on a lancé, mais plutôt 0.
retournera pas le code d'erreur de la commande que l'on a lancée, mais plutôt 0.
## Intégration dans les `Dockerfile`
@ -121,7 +121,7 @@ semble particulièrement indiqué.
L'utilisation par le paramètre `--init` du `run` n'est pas recommandée et
devrait se limiter aux cas où l'image a été construite par quelqu'un qui
n'avait pas en tête ces contraintes. Lorsque l'on sait que des zombies ne vont
pas être géré par leurs parents, le mainteneur se doit d'ajouter `tini` dans
pas être gérés par leurs parents, le mainteneur se doit d'ajouter `tini` dans
son `Dockerfile`. La méthode recommandée est de l'installer par les paquets de
la distribution (`apt-get install tini`, `apk add tini`, ...). Néanmoins, dans
le cas d'une distribution qui ne possèderait pas le paquet, il convient

View file

@ -152,7 +152,7 @@ the selected base image
</div>
Ce dernier exemple est sans appel : `mysql` est une image officielle, et sa
dernière version à l'écriture de ses lignes contient pas moins de 24
dernière version à l'écriture de ces lignes contient pas moins de 24
vulnérabilités dont 9 *high* (pourtant corrigées dans des versions suivantes).
@ -216,7 +216,7 @@ Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
```
</div>
Nous pouvons remarque que Trivy, en plus de faire l'analyse statique des
Nous pouvons remarquer que Trivy, en plus de faire l'analyse statique des
vulnérabilités de l'image, a aussi fait une analyse des dépendances du binaire
`/srv/youp0m`.

View file

@ -11,7 +11,7 @@ pour un projet.
Ainsi, il est possible de provisionner et gérer des machines hôtes sur les
plates-formes de cloud habituelles. C'est également ce projet qui est à la base
de *Docker Dektop*, en permettant de lancer via, respectivement, VirtualBox ou
de *Docker Desktop*, en permettant de lancer via, respectivement, VirtualBox ou
Hyper-V, un environnement Linux prêt à être utilisé avec Docker.
### Par la distribution binaire
@ -33,7 +33,7 @@ chmod +x /usr/bin/docker-machine
### Support de KVM
Le programme support de base de nombreux environnement, dont VirtualBox et
Le programme supporte de base de nombreux environnements, dont VirtualBox et
Hyper-V. Bien d'autres environnements peuvent être supportés, au moyen de
plug-ins.

View file

@ -54,7 +54,7 @@ iyue3rgd0ohs myWebS replicated 1/1 nginx:latest
</div>
Vous pouvez constater que sur l'un des nœuds, sur lequel votre serveur aura été
déployé, le tâche apparaît dans la liste des conteneurs !
déployé, la tâche apparaît dans la liste des conteneurs !
Rien de très excitant pour le moment, car nous ne pouvons pas vraiment accéder
@ -86,8 +86,8 @@ particulièrement.
Essayons de nous connecter aux ports 80 des deux IP correspondant à nos deux
nœuds. Vous devriez voir la même page.
Lorsque plusieurs tâches s'exécutent pour ce service, le nœud d'entrée choisi
selon un round-robin à quelle tâche il va diriger la requête. C'est grâce à ce
Lorsque plusieurs tâches s'exécutent pour ce service, le nœud d'entrée choisit
selon un round-robin vers quelle tâche il va diriger la requête. C'est grâce à ce
mécanisme qu'il est possible de faire de la répartition de charge très
simplement.
@ -103,7 +103,7 @@ consulter la documentation à ce sujet:\
On parle depuis tout à l'heure de lancer plusieurs tâches pour le même
service. La mise à l'échelle, c'est ça : exécuter plusieurs conteneurs pour la
même tâche afin de mieux répartir la charge, idéalement sur des machines
même service afin de mieux répartir la charge, idéalement sur des machines
physiques différentes.
Ce qui se fait souvent avec beaucoup de douleur hors de Docker, se résume ici
@ -156,22 +156,21 @@ version: '3'
services:
redis:
image: redis:alpine
deploy:
replicas: 6
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
placement:
constraints:
- node.role == manager
resources:
memory: 50M
deploy:
replicas: 6
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
placement:
constraints:
- node.role == manager
resources:
memory: 50M
```
</div>
Certaines informations comme les ressources, permettent à l'orchestrateur de
mieux choisir le *workers* de destination, en fonction de certaines de ses
mieux choisir le *worker* de destination, en fonction de certaines de ses
caractéristiques.

View file

@ -7,7 +7,7 @@ Regroupés au sein d'un cluster (on parle de *swarm* pour Docker), chaque
*Docker engine* représente un nœud (*node*) dans lequel on va déployer des
*services*.
Certain nœuds ont un rôle de *manager*, parmi ceux-ci, un seul est élu leader et
Certains nœuds ont un rôle de *manager*, parmi ceux-ci, un seul est élu leader et
prendra les décisions d'orchestration. Les autres sont là pour prendre le
relais en cas de dysfonctionnement sur le manager élu.

View file

@ -21,11 +21,22 @@ peut-être déjà le plugin installé. Si vous n'avez pas d'erreur en exécutant
vous pouvez l'installer comme ceci :
<div lang="en-US">
```bash
# Pour Ubuntu/Debian
sudo apt-get install docker-buildx-plugin
# Pour Fedora/RHEL
sudo dnf install docker-buildx-plugin
```
V="v0.9.1"
Alternativement, vous pouvez l'installer manuellement depuis GitHub :
```bash
# Récupérer la dernière version depuis GitHub
BUILDX_VERSION=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
mkdir -p ~/.docker/cli-plugins
curl -L -s -S -o ~/.docker/cli-plugins/docker-buildx \
https://github.com/docker/buildx/releases/download/$V/buildx-$V.linux-amd64
https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-amd64
chmod +x ~/.docker/cli-plugins/docker-buildx
```
</div>

View file

@ -15,7 +15,7 @@ des instructions similaires à cela :
<div lang="en-US">
```dockerfile
FROM golang:1.18
FROM golang:1
COPY . /go/src/git.nemunai.re/youp0m
WORKDIR /go/src/git.nemunai.re/youp0m
RUN go build -tags dev -v

View file

@ -201,7 +201,7 @@ COPY docker-entrypoint.d /docker-entrypoint.d
```
</div>
Le dossier sera créé s'il n'existe pas, et le contenu du dossier source ser
Le dossier sera créé s'il n'existe pas, et le contenu du dossier source sera
recopié.
:::::
@ -292,7 +292,7 @@ Par exemple, prenons le `Dockerfile` suivait :
<div lang="en-US">
```Dockerfile
FROM python:3.10
FROM python:3
COPY build /usr/lib/python/grapher
EXPOSE 8080
RUN pip install pillow pygal

View file

@ -26,7 +26,7 @@ Les `type`s que vous pouvez découvrir sont ceux que l'on a vu à la
section précédente : `node`, `pod`, ...
La commande `describe` permet d'afficher l'état tel qu'il est attendu
et tel qu'il est actuellement (cela permet de se rendre lorsque les
et tel qu'il est actuellement (cela permet de se rendre compte lorsque les
deux divergent).
@ -110,7 +110,7 @@ kindnet-2gwlp 1/1 Running 0 13m
```
Eh oui ! De nombreux services de base de Kubernetes tournent dans des
conteneurs... qu'il gèren lui-même ! Notamment :
conteneurs... qu'il gère lui-même ! Notamment :
- `etcd` : notre base de données clef/valeur,
- `kube-apiserver` : l'API REST avec qui communique `kubectl`,
@ -166,7 +166,7 @@ kubectl logs -f pingpong
Notez ici l'option -f qui permet de suivre les logs en direct.
Notre premier test ayant réussi, nous pouvons arrêter de DDos Cloudflare :
Notre premier test ayant réussi, nous pouvons arrêter de DDoS Cloudflare :
```bash
kubectl delete pods pingpong
@ -211,19 +211,19 @@ haut niveau et sont là pour s'assurer que les migrations se font en douceur :
elles vont permettre de basculer progressivement les *pod*s d'une version X à une
version Y (par exemple si l'on change notre ping d'alpine 3.16 vers alpine
edge), mais éventuellement de revenir sur la version X si besoin, en cours de
migration. Elles délèguent ensuite aux *replicatsets* la gestion des *pod*s.
migration. Elles délèguent ensuite aux *replicasets* la gestion des *pod*s.
Le *replicatset* est là pour indiquer le nombre de *pod*s que l'on désire et
Le *replicaset* est là pour indiquer le nombre de *pod*s que l'on désire et
s'assurer que le nombre de *pod*s actuellement lancé est bien en adéquation avec
le nombre de *pod*s attendu.
\
Pour résumer : `kubectl` a créé une tâche de déploiement
`deploy/pingpong`. Cette tâche de déploiement a créé elle-même un *replicatset*
`rs/pingpong-xxxx`. Ce *replicatset* a créé un *pod* `po/pingpong-yyyy`.
`deploy/pingpong`. Cette tâche de déploiement a créé elle-même un *replicaset*
`rs/pingpong-xxxx`. Ce *replicaset* a créé un *pod* `po/pingpong-yyyy`.
#### Pasage à l'échelle : facile ?
#### Passage à l'échelle : facile ?
Pour lancer 3 `ping`s en parallèle, modifions la tâche de déploiement comme suit :
@ -232,8 +232,8 @@ kubectl scale deploy/pingpong --replicas 3
```
À ce stade, comme nous ne modifions que le nombre de replicats, Kubernetes va
tout simplement propager ce nombre au *replicatset* existant. Puis, le
*replicatset* voyant un décalage entre le nombre de *pod*s attendus et le nombre
tout simplement propager ce nombre au *replicaset* existant. Puis, le
*replicaset* voyant un décalage entre le nombre de *pod*s attendus et le nombre
de *pod*s en cours d'exécution, il va en lancer de nouveaux, afin de répondre à
la demande.
\
@ -245,11 +245,11 @@ kubectl delete pod pingpong-yyyy-zzz
```
Cela supprime bien un *pod*, mais un autre est relancé instantanément car le
*replicatset* constate une différence dans le nombre attendu.
*replicaset* constate une différence dans le nombre attendu.
Si nous voulons arrêter de DDoS Google/Cloudflare, il ne s'agit pas de tuer
chacun des *pod*s un par un, car de nouveaux seraient créés par le
*replicatset*. Si l'on supprime le *replicatset*, la tâche de déploiement en
*replicaset*. Si l'on supprime le *replicaset*, la tâche de déploiement en
recréera un similaire (avec de nouveaux *pod*s).
Pour arrêter nos conteneurs, il convient donc de supprimer la tâche de

View file

@ -4,7 +4,7 @@ Vue d'ensemble de Kubernetes
*Kubernetes* (prononcé Ku-ber-né-tice[^prononciation-k8s] en grec) est un
système *open source* d'orchestration et de gestion de conteneurs. C'est-à-dire
qu'il se charge de faire coller constamment la liste des conteneurs qu'il voit
vivant aux spécifications qu'on lui aura demandées.
vivants aux spécifications qu'on lui aura demandées.
[^prononciation-k8s]: <https://github.com/kubernetes/kubernetes/issues/44308>
@ -37,7 +37,7 @@ dune série de nœuds *workers*.
Le(s) *control-plane(s)* sont en charge de prendre des décisions globales sur
le déroulement des opérations du cluster : cela va de la détection d'anomalies
sur le cluster ou encore le traiter d'événements et l'organisation de leur
sur le cluster ou encore le traitement d'événements et l'organisation de leur
réponse, ...
On retrouve sur ces nœuds centraux les composants suivants :
@ -52,7 +52,7 @@ L'ordonnanceur
disponibles.
Les contrôleurs
: Ils vont contrôler l'état des différents composants déployées au sein du
: Ils vont contrôler l'état des différents composants déployés au sein du
cluster, pour s'assurer d'être dans l'état désiré. Il y a en fait plusieurs
contrôleurs ayant chacun la responsabilité de veiller sur une partie des
objets, ainsi que le `cloud-controller-manager` lorsque le cluster se trouve
@ -65,11 +65,11 @@ Les contrôleurs
\
Chaque nœud (généralement, le nœud *master* est également *worker*) est utilisé
Chaque nœud (généralement, le nœud *control-plane* est également *worker*) est utilisé
via deux composants :
`kubelet`
: C'est l'agent qui va se charger de créer les conteneurs et les manager, afin
: C'est l'agent qui va se charger de créer les conteneurs et les gérer, afin
de répondre aux spécifications demandées par les *control-planes*.
`kube-proxy`
@ -109,14 +109,14 @@ supprimer ou de vérifier qu'une interface va bien.
Ainsi, à la création d'un conteneur, Kubernetes va laisser aux plugins CNI le
loisir d'ajouter les interfaces réseaux adéquates, d'allouer l'adresse IP, de
configurer les routes, les règles de pare-feu, ... quelque soit
configurer les routes, les règles de pare-feu, ... quelle que soit
l'infrastructure et la complexité du réseau utilisé derrière.
\
Terminons en ajoutant qu'un serveur DNS faisant autorité est nécessaire pour
que, de la même manière que Docker, il soit possible d'accéder aux autres
conteneurs via leur nom (sans qu'il ne soit nécessaire de le déclarer sur un
serveur de noms public). Il n'y a pas de projet de porté par Kubernetes pour
serveur de noms public). Il n'y a pas de projet porté par Kubernetes pour
cela, mais cette tâche est généralement assurée par
[CoreDNS](https://coredns.io/).

View file

@ -39,41 +39,41 @@ metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
```
@ -127,7 +127,7 @@ Après avoir appliqué la nouvelle spec, on constate qu'il y a beaucoup de *pod*
toujours là.
##### Botleneck résolu ? {-}
##### Bottleneck résolu ? {-}
Admirez maintenant dans Chronograf si vous avez réussi à augmenter votre nombre
de pépites !

View file

@ -17,7 +17,7 @@ leurs infrastructures, choisissent de passer par un prestataire. L'entreprise
délègue donc la gestion de son/ses cluster(s) à une autre entreprise, dont
c'est le cœur de métier. La plupart du temps, il va s'agir d'Amazon (via
[Elastic Kubernetes Service](https://aws.amazon.com/fr/eks/)), d'Azure
[Kubernetes
([Kubernetes
Service](https://azure.microsoft.com/fr-fr/services/kubernetes-service/)) ou
Google ([Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)), mais
d'autres acteurs plus petits existent aussi
@ -38,7 +38,7 @@ fonctionne.\
Chaque distribution de *Kubernetes* aura donc pris soin de mettre à disposition
des composants pour un usage plus ou moins spécifique. En dehors des composants
strictement nécessaires qui ne changent pas, on verra d'une distribution à
l'autre des choix quant au moteur d'exécution de conteneurs (tantot CRI-O,
l'autre des choix quant au moteur d'exécution de conteneurs (tantôt CRI-O,
Containerd, KataContainers, ...), le réseau sera géré par une brique spécifique
(Flannel, Calico, Canal, Wave, ...), le stockage peut également faire l'objet
de choix.\
@ -147,8 +147,9 @@ Server Version: Version.Info{Major:"1", Minor:"21", GitVersion:"v1.25.3", [...]
```
</div>
Par défaut, `kubectl` va tenter de contacter le port local 2375, `kind` aura
pris soin de l'exposer pour vous au moment de la création du cluster.
Par défaut, `kubectl` va utiliser le fichier `~/.kube/config` pour contacter
l'API du cluster, `kind` aura pris soin de le configurer pour vous au moment
de la création du cluster.
::::: {.warning}

View file

@ -13,8 +13,8 @@ Avec le paquet `lxc` que nous avons précédemment installé, nous avons égalem
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.
système de base fonctionnel, en suivant les étapes d'installation habituelles
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
@ -45,11 +45,11 @@ 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`.
Généralement on lance `lxc-start` avec l'option `--daemon`, car sans cette
option le conteneur monopolise le terminal. En mode daemon, on utilise la
commande `lxc-console` pour s'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, lançons quelques commandes puis éteignons la machine en
lançant la commande `poweroff` dans le conteneur. Il est également possible de
@ -122,8 +122,9 @@ veth, avec un côté placé dans la machine hôte et l'autre côté placé dans
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 :
Commençons par attribuer une IP à cette nouvelle interface. L'identifiant de
l'interface veth créée côté hôte peut être trouvé avec la commande `ip link`.
Adaptez l'exemple suivant avec votre identifiant d'interface :
<div lang="en-US">
```bash
@ -136,10 +137,10 @@ 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
réseau à l'autre : en l'occurrence, du réseau 172.23.42.0/24 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 :
Pour que notre machine hôte route les paquets, exécutez la commande :
<div lang="en-US">
```bash
@ -151,9 +152,9 @@ 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 :
est une adresse privée, non routable sur Internet. Il faut donc ajouter une
couche de NAT/PAT pour réécrire les adresses sources avant d'envoyer les
paquets sur Internet (en adaptant le nom de l'interface veth) :
<div lang="en-US">
```bash
@ -162,7 +163,7 @@ iptables -t nat -A POSTROUTING ! -o vethYJWD6R -s 172.23.42.0/24 -j MASQUERADE
</div>
Dernière étape, dans notre conteneur, nous devons indiquer la route à utiliser
pour accéder à internet :
pour accéder à Internet :
<div lang="en-US">
```bash
@ -170,7 +171,7 @@ ip route add default via 172.23.42.1
```
</div>
Nous avons maintenant internet dans notre conteneur !
Nous avons maintenant Internet dans notre conteneur !
## Utilisation du conteneur