Depuis Debian Jessie (8.X), systemd se fait de plus en plus présent dans la distribution, et permet d’effectuer une migration en douceur au fil des versions. Parmi celles à noter, je voudrais vous faire découvrir la partie mise en réseau élémentaire.

L’objectif est de remplacer la gestion classique avec l’ensemble ifupdown et le client DHCP de l’ISC. On pourra même supprimer les paquets à la fin de la migration. Pour corser les choses, je vais appliquer ce cas d’usage à un VPS 2016 chez OVH, dont la gestion manuelle révèle quelques surprises.

Testez vos configurations dans une machine virtuelle ! Ça vous évitera des surprises en production. Et lisez cet article en entier avant de vous lancer : systemd est super, mais aussi super chiant parfois…

Configurer systemd

La configuration des éléments réseau de systemd se fait dans le dossier /etc/systemd/network. Pour ce qui nous concerne, on va créer un fichier avec en préfixe un nombre quelconque, par exemple « 30 » puis un nom représentatif pour vous, je prendrai « wired » pour mes liens filaires, et l’extension « .network ». Dans cet exemple, ca me donne donc « 30-wired.network ».

Pour un réseau ayant une passerelle accessible, comme nos box ou n’importe quel réseau plus conventionnel, le fichier est relativement simple.

[Match]
MACAddress=fa:fb:fc:fd:fe:ff

[Network]
DHCP=no

[Address]
Address=10.1.1.10/24
Gateway=10.1.1.1

Cependant, ça ne marche pas sur un VPS, parce qu’OVH a fait en sorte que les serveurs ne se parlent pas directement entre eux au sein du cluster, du moins pas en configuration standard. C’est donc plus compliqué.

[Match]
MACAddress=fa:fb:fc:fd:fe:ff

[Network]
DHCP=no

[Address]
Address=10.1.1.10/32

[Route]
Destination=10.0.0.1
Scope=link

[Route]
Gateway=10.0.0.1
GatewayOnlink=yes

Dans ce fichier, on trouve d’abord une section « [Match] » chargée d’appliquer des paramètres à une ou plusieurs interfaces. Ici, je vais attacher une seule interface par son adresse MAC (paramètre « MACAddress »).

Ensuite j’ai choisi de faire une section « [Address] ». Ce n’est pas strictement utile car je n’ai qu’une adresse à indiquer (paramètre « Address »), mais on pourrait ajouter des paramètres spécifiques à chacune des adresses. J’illustre simplement le concept.

Enfin de la même manière, les sections « [Route] » permettent d’ajouter des routes, en particulier les routes par défaut. Ici, c’est nécessaire car les « Scope » sont différents : j’ai un « link » en premier, et sans précision un « global » en second.

En lecteur attentif, vous avez pu constater que l’adresse IPv4 est accompagnée d’un masque la rendant unique dans son sous-réseau (soit /32 ou 255.255.255.255). Alors comment gérer le routage et la passerelle par défaut ? L’astuce utilisée consiste à attacher au lien physique (« Scope=link ») une destination, elle-même en /32 (ici « Destination=10.0.0.1 »), et enfin on peut ajouter la route par défaut (« Gateway=10.0.0.1 »). La directive « GatewayOnlink » désactive la vérification de la cohérence de la route par le noyau, sans quoi la route par défaut ne peut pas être créée car incohérente.

Avant que ça se mette à merder sur le VPS

systemd version 232 livré en standard avec Debian Stretch est rempli de petit bogues pénibles dès que l’on sort des sentiers battus. Typiquement, le paramètre « GatewayOnlink » ne fonctionne pas, et j’ai eu d’autres cas pour d’autres projets. Bref ! Une mise à jour s’impose !

Fort heureusement, les gens charmants et efficaces qui s’occupent des backports nous permettent d’avoir une des dernières versions en date. Pour ce faire, on rajoute donc une ligne à notre « /etc/apt/sources.list ».

deb http://debian.mirrors.ovh.net/debian/ stretch-backports main

Ensuite on fait une petite mise à jour de systemd, d’udev et des librairies dépendantes (tous sont globalement liés, je préfère tout grouper) :

# apt-get update
# apt-get -t stretch-backports install systemd systemd-sysv udev

Et enfin on peut redémarrer pour que la nouvelle version de systemd soit bien prise en compte. Actuellement, le backport nous amène à la version 236, qui fonctionne correctement pour ce qui nous intéresse ici.

Basculer le démarrage des services réseau

Il faut désactiver la configuration via les anciens mécanismes de type SysV :

systemctl disable networking

Et activer ceux de systemd :

systemctl enable systemd-networkd

Si on laisse le système redémarrer comme ça, le client DHCP a de fortes chances de se déclencher tout de même. Il vaut mieux commenter les lignes chargées de la configuration de l’interface réseau dans le fichier « /etc/network/interfaces ».

En pratique, il y a une dépendance de services que je n’ai pas encore élucidée, et elle appelle tout de même « if-up », et donc « dhclient ». C’est donc une manière fiable, mais pas forcément la plus propre, qui permet de tester avant de désinstaller définitivement isc-dhcp-client.

Finaliser la configuration

Il est préférable de vérifier également le contenu, statique ici, du fichier « /etc/resolv.conf ».

search mondomaine.example
domain mondomaine.example
nameserver 10.1.0.22

Vérifiez tous vos fichiers pour les erreurs de frappe, en particulier si vous modifiez votre VPS ou un serveur distant, et enfin rebootez pour appliquer la configuration.

Remarque : si vous pouvez accéder à la console physique via un KVM virtuel ou via votre hyperviseur, vous pouvez lancer « systemctl start systemd-networkd » sans avoir à redémarrer et donc perdre l’éventuelle connexion SSH servant aux modifications.

Après redémarrage du serveur, ou au moins du service « systemd-networkd », on vérifie les configurations effectives, donc la présence de l’adresse IP et la passerelle par défaut.

# ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether fa:fb:fc:fd:fe:ff brd ff:ff:ff:ff:ff:ff
    inet 10.1.1.10/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::f8fb:fcff:fefd:feff/64 scope link
       valid_lft forever preferred_lft forever
# ip route
default via 10.0.0.1 dev eth0 proto static onlink
10.0.0.1 dev eth0 proto static scope link

Quand tout fonctionne, un petit coup de ménage.

apt-get purge isc-dhcp-client ifupdown

Encore plus fort : IPv6

Puisque désormais l’IPv4 fonctionne, on peut se lancer dans la configuration de la pile IPv6 avec le même genre de démarche.

Pour un réseau classique, rien de bien sorcier, on reste sur le même principe que pour IPv4. Toujours dans le fichier « 30-wired.network ».

[Match]
MACAddress=fa:fb:fc:fd:fe:ff

[Network]
DHCP=no

[Address]
Address=2001:db8:dead:beef::12/64
Gateway=2001:db8:dead:beef::1

Mais chez OVH, comme pour IPv4, ça devient beaucoup plus galère. D’abord parce que contrairement à IPv4, il n’ont pas mis de DHCPv6 dans leur ferme de serveurs, donc pas la peine de chercher de l’inspiration via une configuration automatique, et ensuite parce que le paramètre « GatewayOnlink » utilisé plus haut ne semble fonctionner qu’en IPv4. Bref ! Même combat mais solution différente, et pour le coup plus compliquée.

D’abord les principes identiques à IPv4, à nouveau dans le fichier « 30-wired.network ».

[Match]
MACAddress=fa:fb:fc:fd:fe:ff

[Network]
DHCP=no

[Address]
Address=2001:db8:dead:beef::12/128

[Route]
Destination=2001:db8:dead:beef::1/128
Scope=link

On installe donc une adresse IPv6 et une route statique vers un hôte unique. Par contre, pas de passerelle par défaut pour le moment car ça ne fonctionne pas ! Essayez et vous obtiendrez ce petit fail :

systemd-networkd[453]: eth0: Could not set route: No route to host

En revanche, si on ajoute manuellement la route après le démarrge, ça fonctionne parfaitement. Il y a donc un délai à obtenir entre le démarrage de l’interface et sa configuration IPv6 et l’ajout de la route par défaut. Voici donc un petit tour de passe-passe qui consiste à ajouter un service maison chargé de monter la route après le démarrage du démon systemd-networkd. Ce service se construit à l’aide d’un simple fichier « /etc/systemd/system/network-ipv6-static-route.service ».

[Unit]
Description=Adds a static IPv6 route after systemd-networkd.service is started
Conflicts=shutdown.target
Requisite=systemd-networkd.service
After=systemd-networkd.service

[Service]
Type=oneshot
ExecStart=/sbin/ip -6 route add default via 2001:db8:dead:beef::1 dev eth0

[Install]
WantedBy=network.target

De manière lisible (j’aime beaucoup cet aspect de systemd), on constate clairement la dépendance à systemd-networkd avec le paramètre « After= » ainsi que la cible de démarrage, ici « network.target ». Ce service sera donc lancé pendant la phase de démarrage du réseau, mais uniquement après le démarrage de systemd-networkd. Et à l’usage, ça suffit amplement ! Attention cependant à bien modifier le nom de l’interface par rapport à votre système.

Il faut tout de même l’enregistrer dans le système, et activer manuellement l’ensemble si le besoin est avéré avant le prochain démarrage.

# systemctl enable network-ipv6-static-route
# systemctl restart systemd-networkd
# systemctl start network-ipv6-static-route

En route pour IPv6 !

Et avec DHCP ?

Très simple. Typiquement, pour une machine quelconque (configuration généralisée tout type d’interfaces filaires, compatible v4/v6), le fichier « 30-wired.network » devrait contenir :

[Match]
Name=eth*
Name=enp*
Name=enx*

[Network]
DHCP=yes

Pour le DNS, on laisse faire systemd-resolved, mais il faut le configurer.

# rm /etc/resolv.conf
# ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
# systemctl enable systemd-resolved
# systemctl start systemd-resolved

Voilà donc comment ne plus utiliser de clients tiers comme isc-dhcp-client pour la mise en réseau de serveurs, y compris en IPv6 ! Et plus de resolvconf non plus par la même occasion…

Références