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

3.3 KiB

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

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é