tripoli.cloud/tripoli-netctl/README.md
Adam Belkacemi 991f4dd04f Phase 4 : tripoli-netctl, labo network namespaces + veth (rtnetlink)
- CLI (clap) : create/destroy/list. Network namespace créé via
  rtnetlink::NetworkNamespace (fork + unshare(CLONE_NEWNET) + bind-mount,
  équivalent de `ip netns add`), paire veth créée dans la racine, un
  bout déplacé dans le namespace (setns_by_fd) et adressé depuis un
  thread OS dédié qui fait setns() - jamais un thread du pool
  spawn_blocking de tokio, qui serait réutilisé pour d'autres tâches et
  resterait coincé dans le namespace.
- Journal d'audit JSON (stderr + /var/log/tripoli-netctl/audit.log) à
  chaque opération.
- Documenté explicitement comme labo pédagogique un-seul-poste :
  tripoli-edge et tripoli-api restent nativement sur l'hôte, ce projet
  ne pilote aucun trafic de production.
- Testé en conditions réelles sur le serveur : création, adressage des
  deux côtés (dont lo, down par défaut dans un netns neuf), ping
  traversant effectivement la veth, puis destruction propre (veth +
  namespace) vérifiée par absence des interfaces et du namespace.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 22:58:53 +02:00

84 lines
3.3 KiB
Markdown

# tripoli-netctl
Labo pédagogique un-seul-poste sur les network namespaces Linux et la
crate `rtnetlink`. **Ne pilote aucun service de production** :
tripoli-edge et tripoli-api continuent de tourner nativement sur l'hôte,
dans le namespace racine. Ce n'est pas un CNI, pas un orchestrateur -
juste une démonstration, sur une seule machine, de ce qui se passe sous
le capot quand un outil comme Docker ou Kubernetes isole un service en
réseau.
## Ce que fait `create <name>`
1. Crée un network namespace nommé `<name>` (`rtnetlink::NetworkNamespace::add`,
équivalent de `ip netns add` - fork + `unshare(CLONE_NEWNET)` +
bind-mount de `/proc/self/ns/net` sur `/run/netns/<name>`).
2. Crée une paire veth (`tv-<name>h` / `tv-<name>p`, noms tronqués à 6
caractères alphanumériques pour respecter la limite de 15 caractères
des interfaces Linux).
3. Déplace `tv-<name>p` dans le namespace créé (`setns_by_fd`).
4. Adresse le côté hôte (`10.200.<subnet-id>.1/30`) et l'active.
5. Depuis un thread OS dédié qui fait `setns()` vers le namespace cible,
adresse le côté namespace (`10.200.<subnet-id>.2/30`), l'active, et
active `lo` (qui démarre toujours *down* dans un namespace neuf -
détail facile à oublier, source classique de "ça ne marche pas" dans
ce genre de labo).
Chaque étape est journalisée en JSON sur stderr et dans
`/var/log/tripoli-netctl/audit.log`.
## Pourquoi un thread dédié, pas `tokio::task::spawn_blocking`
`setns()` change la namespace réseau du **thread appelant**, pour toute
sa durée de vie. Les threads de `spawn_blocking` sont mis en pool et
réutilisés par tokio pour d'autres tâches bloquantes : y laisser un
thread « coincé » dans un namespace réseau casserait silencieusement
n'importe quelle tâche bloquante ultérieure qui tomberait dessus. Un
`std::thread::spawn` dédié, jamais réutilisé, élimine le risque.
## `destroy <name>`
Supprime le côté hôte de la veth - le noyau supprime automatiquement le
pair, même déplacé dans un autre namespace, dès qu'un des deux bouts
disparaît - puis démonte/supprime le fichier de namespace.
## `list`
Énumère `/run/netns` et vérifie, pour chacun, la présence de la veth
hôte associée.
## Utilisation
```bash
sudo ./tripoli-netctl create demo --subnet-id 5
sudo ./tripoli-netctl list
sudo ./tripoli-netctl destroy demo
```
Root est requis (namespaces + netlink privilégié) - vérifié explicitement
au démarrage plutôt que de laisser échouer la première opération avec un
message obscur.
## Limites assumées (v1)
- Pas d'allocation automatique de sous-réseau : `--subnet-id` est
explicite, une collision entre deux services donnerait une erreur
claire plutôt qu'une allocation silencieuse qui la masquerait.
- IPv4 uniquement.
- Un seul veth par service - pas de topologie plus complexe (bridge,
plusieurs interfaces).
## Vérifié
```
$ sudo tripoli-netctl create demo1 --subnet-id 5
service 'demo1' prêt :
hôte : tv-demo1h 10.200.5.1/30 (namespace racine)
ns : tv-demo1p 10.200.5.2/30 (namespace demo1, /run/netns/demo1)
$ ping -c 2 10.200.5.2 # depuis la racine, vers le namespace
2 packets transmitted, 2 received, 0% packet loss
$ sudo tripoli-netctl destroy demo1
service 'demo1' détruit # veth et namespace bien supprimés, vérifié
```