- 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>
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>
- Crée un network namespace nommé
<name>(rtnetlink::NetworkNamespace::add, équivalent deip netns add- fork +unshare(CLONE_NEWNET)+ bind-mount de/proc/self/ns/netsur/run/netns/<name>). - 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). - Déplace
tv-<name>pdans le namespace créé (setns_by_fd). - Adresse le côté hôte (
10.200.<subnet-id>.1/30) et l'active. - 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 activelo(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-idest 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é