Network Namespace

J’ai découvert les Networks Namespaces au travail: j’avais besoin de tester rapidement des règles de pare-feu. Je voulais donc une simple architecture avec deux réseaux, une machine par réseau et un routeur.

Par réflexe, j’ai pensés à ces solutions :

  • Des machines virtuelles.
  • Des containeurs (Docker, LXC etc).

Puis, mon supérieur m’a fait remarqué qu’il serait plus simple d’utiliser des Networks Namespaces.

Mais qu’est-ce que c’est ?

Pour ceux qui sont familliés avec la notion de namespace vous pouvez avancer plus bas.

Bon, pour ceux qui l’ignorent, les namespaces sont un mécanisme du kernel Linux afin d’aider à l’isolation d’une tâche. Il en existe de plusieurs types : mount namespace, network namespace, IPC1 namespace etc.. C’est sur cela que s’appuie les containeurs afin de fournir une isolation.

Je ne vais pas plus rentrer dans le détails, si ça vous intéresse, je vous conseille man 7 namespace.

On va ici principalement présenter les Network Namespaces. D’après le manuel, cela fournit une isolation pour les ressources suivantes : périphériques réseaux, les piles réseaux (IPv4 et IPv6), les ports, les tables de routages, les règles de pare-feu etc.

Dans notre cas, si on ne veut qu’une isolation réseau, c’est parfait. Pas besoin d’isoler le filesystem pour tester des règles de pare-feu. On peut donc créer des namespaces qui chacun représentera une machine de notre réseau à simuler.

Comment l’utiliser ?

Tout d’abord, on peut utiliser la commande ip qui fournit un support pour les networks namespaces. Elle va nous permettre de créer, supprimer, d’inter-connecter.. En bref de tout faire sur nos networks namespace.

Les créer

Comme dit précédemment, on va créer un espace de nom par machine, pour simuler notre réseau.

$ sudo ip netns add NS_NAME

Cette commande va créer un espace de nom appelé NS_NAME, donc notre “machine”. Je vous propose d’en créer trois : machine, serveur et routeur. On va ici utiliser machine pour communiquer avec le serveur en passant par le routeur

Les connecter

On a certes 3 espaces de noms qui représentent nos machines, mais elles ne sont pas connectées entre elles.

Afin de résoudre ce problème, on va créer des interfaces virtuelles : virtual ethernet. Elles se créent par paire, et peuvent être assimilés à un tube : ce qui arrive d’un côté resort forcément de l’autre. Grâce à cela, on peut simuler un réseau de deux machines avec une paire de virtual ethernet. Le plus intéressant est qu’on peut assigner un network namespace différent à chacune des interfaces de la paire, les permettant ainsi de communiquer entre namespace.

Pour créer une paire d’interface virtual ethernet :

$ sudo ip link add veth0 type veth peer name veth1

Ici on crée une interface veth0, qui sera de paire avec l’interface veth1. Ensuite, il faut assigner les interfaces aux network namespace. On peut assigner veth0 à machine comme ceci :

$ sudo ip link set veth0 netns machine

Si l’on crée ainsi 2 paires d’interfaces, et 3 namespaces. On peut simuler le réseau que l’on souhaite. Je vous propose les noms suivants :

  • v_m2rt : machine interface vers routeur.
  • v_rt2m : routeur interface vers machine.
  • v_rt2srv : routeur interface vers serveur.
  • v_srv2rt : serveur interface vers routeur.
# Créer les network namespace
$ sudo ip netns add ns_machine
$ sudo ip netns add ns_server
$ sudo ip netns add ns_router

# Créer les interfaces veth
$ sudo ip link add v_m2rt type veth peer name v_rt2m
$ sudo ip link add v_srv2rt type veth peer name v_rt2srv

# Attribuer les interfaces aux namespaces
$ sudo ip link set v_m2rt netns machine
$ sudo ip link set v_rt2m netns routeur
$ sudo ip link set v_rt2srv netns routeur
$ sudo ip link set v_srv2rt netns serveur

Les configurer

Maintenant que notre réseau est en place, il faut configurer nos interfaces. Et oui, même si tout ça c’est virtuel, faut avoir une ip etc pour communiquer.

Pour lancer une commande dans un network namespace précis il faut :

$ sudo ip netns exec NS_NAME -- CMD

note: Ici -- permet de dire qu’on arrête de parser les arguments. Ainsi si la CMD tient un -a, cela ne sera pas interprété par la commande ip.

Je vous propose donc de prendre comme adresses IPv4 celles ci :

Interface Adresse IPv4
v_m2rt 192.168.1.1/24
v_rt2m 192.168.1.254/24
v_srv2rt 192.168.2.1/24
v_rt2srv 192.168.2.254/24

Maintenant, on va configurer lancer un serveur. Dans un autre terminal, mettez vous en écoute :

$ sudo ip netns exec server -- nc -lvnp 80

Et on va s’y connecter avec machine sur un autre terminal:

$ sudo ip netns exec machine -- nc 192.168.2.1 80

Cette connectivité marche parce qu’on autorise tous les flux au niveau du routeur. Maintenant, si on modifie les règles du pare-feu de notre namespace routeur afin de ne pas transférer les paquets non précisés par une règle.

$ sudo ip netns exec routeur -- iptables -P FORWARD DROP

Si vous retester que la machine puisse atteindre le serveur, ça ne marchera pas :).

Et voilà comment on teste vite des règles de pare-feu, sans utiliser de machines virtuelles, ni toute l’architecture d’un containeur.

Script complet

Voici le petit script:

# Créer les network namespace
$ sudo ip netns add ns_machine
$ sudo ip netns add ns_server
$ sudo ip netns add ns_router

# Créer les interfaces veth
$ sudo ip link add v_m2rt type veth peer name v_rt2m
$ sudo ip link add v_srv2rt type veth peer name v_rt2srv

# Attribuer les interfaces aux namespaces
$ sudo ip link set v_m2rt netns machine
$ sudo ip link set v_rt2m netns routeur
$ sudo ip link set v_rt2srv netns routeur
$ sudo ip link set v_srv2rt netns serveur

# Configurer l'adresse IPv4 et la route pour machine
$ sudo ip netns exec machine -- ip address add 192.168.1.1/24 dev v_m2rt
$ sudo ip netns exec machine -- ip route add default 192.168.1.254 via 192.168.1.1 dev v_m2rt

# Configurer l'adresse IPv4 et la route pour serveur
$ sudo ip netns exec serveur -- ip address add 192.168.2.1/24 dev v_srv2rt
$ sudo ip netns exec serveur -- ip route add default 192.168.2.254 via 192.168.2.1 dev v_srv2rt

# Configurer les addresses IPv4 pour le routeur
$ sudo ip netns exec router -- ip address add 192.168.1.254 dev v_rt2m
$ sudo ip netns exec router -- ip address add 192.168.2.254 dev v_rt2srv

  1. Inter-processus communication ↩︎