Un peu de customisation de système avec systemd🔗
Introduction
Lors de mon dernier travail sur Cockpit, je me suis rendu compte que ce dernier reposait beaucoup sur systemd. En effet, en essayant l'interface Web de Cockpit, je me suis rendu compte qu'on pouvait ajouter pas mal de renseignements sur la machine via systemd. Vu la quantité d'informations disponibles, un article s'imposait pour présenter l'ensemble des modifications de mon système.
De même, ayant adopté systemd, il m'a semblé plus intéressant d'essayer de gérer la configuration de mes machines en utilisant les mécanismes systemd qui semblent maintenant assez bien éprouvés.
Bon, je sais que systemd est un sujet encore épineux en 2017 mais, je crois qu'en utilisant ses mécanismes de configuration le plus possible, on arrive à une méthode de configuration un peu plus universelle. En effet, sous réserve que d'autres distributions reposent sur les services systemd présentés dans cet article, un administrateur système qui changerait d'environnement serait moins perdu (il y a de grandes différences entre un système RedHat et un système Debian) et, de fait, serait beaucoup plus efficace.
Mais voyons plutôt ce que nous pouvons faire avec systemd.
L'ensemble de ce qui suit a été réalisé sur plusieurs machines sous Debian Stable (Stretch au moment de la rédaction de cet article).
Informations sur la machine via /etc/machine-info
hostnamectl est une commande systemd qui permet de customiser plusieurs éléments:
- l'emplacement physique de la machine
- l'environnement de la machine (production/dev/test/etc).
- le type de machine (chassis).
- Un nom élégant.
- l'icône de la machine (dont le nom est au format XDG).
Ces informations sont stockées dans le fichier /etc/machine-info
. On peut également les modifier avec hostnamectl.
Gestion du temps via systemd-timesyncd
J'utilisais depuis des temps immémoriaux l'antique ntpdate pour synchroniser l'heure de mon serveur public. Attention, cette option est ultra importante, car mon serveur public n'a pas d'horloge temps réel indépendante. Donc, dès qu'il boote, il lui faut récupérer l'heure via le réseau, histoire d'avoir les bonnes dates dans les logs.
Mais avec systemd, il y a un outil pour faire ça: timedatectl
. Si je peux virer un paquet de mon système, autant le faire non ? Par ailleurs, la documentation de timedatectl indique que ce dernier stocke la date et l'heure sur disque ce qui permet de reprendre à une date moins vieille que epoch lors du reboot de la machine.
La configuration de timedatectl est assez simple, vous devez simplement renseigner le fichier /etc/systemd/timesyncd.conf
avec la bonne liste de serveurs, dans le cadre d'une machine à configuration statique.
# This file is part of systemd. # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # Entries in this file show the compile time defaults. # You can change settings by editing this file. # Defaults can be restored by simply deleting this file. # # See timesyncd.conf(5) for details. [Time] NTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
Dans le cas, d'une machine servie par DHCPv4, je vous conseille de ne pas configurer ce fichier mais d'utiliser la directive UseNTP
dans la configuration du service networkd et de servir la liste des serveurs NTP via votre serveur DHCP: votre configuration sera alors centralisée correctement.
Ensuite, n'oubliez pas de supprimer le paquet ntpdate
, ce qui supprime aussi l'unité systemd associée. Enfin, vous devrez activer le service systemd-timesyncd.service
tout en ayant pris soin de recharger le démon systemd.
Gestion des interfaces réseaux via systemd-networkd
Bon ça fait des années que j'utilise le système ifup de Debian qui marche assez bien finalement. Donc pour moi, NetworkManager est un peu "overkill". Néanmoins, systemd présente un mécanisme intéressant pour la gestion des interfaces réseau: il s'agit de systemd-networkd.
Globalement la logique est un peu la même que celle d'ifup:
- On définit un fichier .network de définition d'un périphériqueréseau.
- On y indique les différentes options disponibles dedans.
- Au démarrage de la bécane, systemd se charge "d'allumer" lesdifférents périphériques réseau.
Voici quelques fichiers que j'utilise au boulot et à la maison, pour vous montrer différentes utilisations possibles et que networkd sait être versatile…
Connexion de l'interface filaire ethernet de base via DHCP
Dans ce qui suit, l'interface réseau Ethernet se nomme eno1
. Pour savoir ce que vous avez, un simple ip link
vous donnera la liste des interfaces repérées par le noyau.
Vous devez créer un fichier se terminant par .network
dans le répertoire /etc/systemd/network/
(ex: /etc/systemd/network/ethernet.network) et contenant les lignes à suivre:
[Match] Name=eno1 [Network] Description="Primary Ethernet card" DHCP=yes
Comme vous le voyez, c'est très simple ! Le principe est d'inscrire une directive de match qui permettra de repérer la carte réseau concernée par le fichier .network
. Puis, on indique qu'on souhaite utiliser le DHCP.
Connexion de l'interface filaire Ethernet de base via DHCP avec IPv6
Dans le cas qui suit, on souhaite récupérer une adresse IPv4 via DHCP mais utiliser les mécanismes IPv6 d'allocation automatique d'adresses.
[Match] Name=eno1 [Network] Description="Primary Ethernet card" DHCP=ipv4 LinkLocalAddressing=ipv6 IPv6AcceptRA=true [DHCP] UseDNS=true UseDomains=true UseNTP=true UseRoutes=true UseTimezone=true ClientIdentifier=mac
Pour la directive DHCP, on souhaite:
- Configurer le DNS via DHCP.
- Configurer le domaine par défaut via DHCP.
- Configurer les serveurs NTP utilisés par DHCP.
- L'authentification DHCP se fera par adresse mac.
Connexion de l'interface filaire Ethernet de base via DHCP filtrant
Au bureau, j'ai un serveur DHCP filtrant sur les adresses MAC et qui s'attend à avoir des machines Windows. Pour cela, on va indiquer qu'on souhaite identifier le client DHCP via l'adresse MAC de la carte réseau et on va ajouter un identifiant de vendeur qui aura pour contenu MSFT 5.0 (un truc qui dit que c'est un MS-Windows):
[Match] Name=eno1 [Network] Description="Primary Ethernet card" DHCP=ipv4 LinkLocalAddressing=no [DHCP] UseDNS=true ClientIdentifier=mac VendorClassIdentifier="MSFT 5.0"
Connexion Wifi avec WPA
C'est la connexion classique sur une borne wifi. Ici, il y a une petite astuce: nous allons utiliser wpa_supplicant (le classique) et créer une "fausse" unité systemd pour indiquer à systemd-networkd qu'il faut utiliser wpa_supplicant.
D'abord, la configuration de la carte réseau Wifi (wlp1s0 chez moi) dans /etc/systemd/network/wlp1s0.network
:
[Match] Name=wlp1s0 [Network] Description="Primary WiFi card" DHCP=ipv4 LinkLocalAddressing=ipv6 IPv6PrivacyExtensions=true IPv6AcceptRA=true [DHCP] UseDNS=true UseNTP=true UseDomains=true ClientIdentifier=mac
Dans cette configuration, on configure IPv4 par DHCP, IPv6 par les mécanismes d'annonce de routeur et, bien sûr, on active les extensions de vie privée sur IPv6. Rien que du classique.
Ensuite, il vous faut un fichier de configuration pour wpa_supplicant, nommé de la bonne manière, c'est-à-dire en tenant compte du nom de l'interface. Dans notre cas, ce sera /etc/wpa_supplicant/wpa_supplicant-wlp1s0.conf:
ctrl_interface=/var/run/wpa_supplicant eapol_version=1 ap_scan=1 fast_reauth=1 network={ ssid="Foobar1" psk="password1" priority=1 } network={ ssid="Foobar2" psk="password2" priority=2 }
Enfin, il reste à créer une "fausse" unité systemd et à l'activer:
# systemctl enable wpa_supplicant@wlp1s0.service # systemctl start wpa_supplicant@wlp1s0.service
Normalement, ça devrait bien fonctionner…
Connexion filaire statique avec une route supplémentaire
Pour la maison, j'ai une bécane qui sert de pont "Wifi" pour le réseau 192.168.1.0. Voici la configuration spécifique de sa carte ethernet:
[Match] Name=eno1 [Network] Description="Primary Ethernet card" DHCP=no LinkLocalAddressing=no IPForward=ipv4 [Address] Address=192.168.1.7/24 Broadcast=192.168.1.255
Bien entendu, vous devez utiliser un mécanisme de forwarding IPv4 via nftables pour que ça fonctionne complètement (mais ça, c'est hors-sujet pour aujourd'hui).
Création d'un pont pour des machines virtuelles sur DHCP filtrant
Au boulot, j'ai besoin d'avoir un pont Ethernet pour mes machines virtuelles sous KVM. Pour faire simple, j'utilise un pont via networkd. Mais il y a un peu de magie dans l'histoire: n'oublions pas que mon serveur DHCP est filtrant sur l'adresse MAC. Du coup, je suis obligé d'affecter l'adresse MAC de la carte réseau Ethernet au pont et de mettre une fausse adresse MAC pour la carte réseau. C'est chiant mais sinon, ça ne fonctionne pas du tout !
Vous devez créer 3 fichiers:
- Un fichier de périphérique réseau virtuel pour le pont (br0.netdev).
- Un fichier de configuration réseau pour le pont (br0.network).
- Un fichier qui va inscrire la carte ethernet physique dans le pont.
Contenu de /etc/systemd/network/br0.netdev
:
[NetDev] Description="Network Bridge for KVM" Name=br0 Kind=bridge
Contenu de /etc/systemd/network/br0.network
:
[Match] Name=br0 [Link] MACAddress=88:51:fc:4e:8a:91 [Network] Description="Network configuration for Bridge" DHCP=ipv4 LinkLocalAddressing=no [DHCP] UseDNS=true UseMTU=true UseDomains=true ClientIdentifier=mac VendorClassIdentifier="MSFT 5.0"
Contenu de /etc/systemd/network/ethernet-slave.network
:
[Match] Name=eno1 [Link] MACAddress=88:51:fc:4e:8a:92 [Network] Description="Primary Ethernet card (bridged)" Bridge=br0
Ensuite, relancez le service:
# systemctl restart systemd-networkd.service
Un peu de ménage
Une fois que votre configuration est effective, vous pouvez supprimer les anciens paquets (oui, je sais, ça fait bizarre de supprimer un paquet aussi essentiel que ifupdown mais, vous n'en avez plus besoin):
# apt purge isc-dhcp-client isc-dhcp-common bridge-utils ifupdown # apt autoremove
Gestion du résolveur DNS via systemd-resolved
Bon, c'est une opération assez simple qui consiste simplement à activer le service systemd-resolved et à remplacer le fichier /etc/resolv.conf
par un lien symbolique vers le fichier maintenu par systemd:
# systemctl enable systemd-resolved.service # systemctl start systemd-resolved.service # ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
Une fois opérationnel, le service se charge de gérer la résolution de noms. Plus besoin de gérer le resolv.conf à la main ou via ifupdown.
Gestion des logs
Après quelques mois de fonctionnement correct, je me suis résolu à passer à journald en totalité. En effet, je trouve que ce dernier, qui est installé si vous utilisez systemd remplit bien son rôle. En maitrisant les commandes de consultation, on arrive finalement à s'en sortir assez facilement.
J'en ai donc profité pour configurer son stockage permanent et virer les résidus de syslog. Voici le contenu de mon fichier /etc/systemd/journald.conf
:
[Journal] Storage=persistent SystemMaxUse=200M MaxFileSec=1month ForwardToSyslog=no
Suivi de systemctl restart systemd-journald
. Pour virer rsyslog:
# apt purge rsyslog
Ça fait toujours un paquet en moins à gérer.
Abandonner le traditionnel cron
Oui, systemd gère un peu l'équivalent de cron via les "timers". C'est une approche qui me séduit depuis le début même si, à l'usage, une simple crontab est plus simple à gérer. Car dans le cas de systemd, vous aurez deux choses à faire:
- créer un service à lancer.
- créer un timer pour le service.
Néanmoins, d'un point de vue de sysadmin, cela fait sens, car de toute manière, le script que vous lancez par le démon cron doit bien se situer quelquepart. Avec systemd, vous avez un peu de travail supplémentaire, mais vous pouvez lire assez facilement l'emplacement du service/script. Ce référencement est quasiment obligatoire (il est indiqué dans la définition du service), c'est donc plus beaucoup plus propre, aucun risque de se demander où est situé le script à lancer.
Néanmoins, se débarrasser de cron n'est pas chose aisée. En effet, certains paquets appellent explicitement cron ou au moins le paquet cron-daemon. Mais, bonne nouvelle, il reste un subterfuge: il existe un "cron like" basé sur systemd: systemd-cron. Ce paquet installe le paquet virtuel cron-daemon.
Donc, un simple:
# apt install --no-install-recommends systemd-cron
fera l'affaire. Bon, vous me direz: quel est l'intérêt de remplacer cron par un autre paquet ? Moi, je vous répondrais que c'est pour la beauté du geste !
Conclusions
En fin de l'année 2017, systemd a bien progressé. Il fait beaucoup de choses de facto. Ce n'est pas parfait, notamment pour cron mais c'est surtout lié à la manière d'empaqueter les logiciels sous Debian.
Dans mon objectif de réduire le nombre de paquets à administrer sur une machine aux capacités limitées, systemd remplit parfaitement son rôle. Je ne peux que vous recommander de l'utiliser. Certes parfois, vous allez rencontrer des bugs car tout n'est pas parfait. Mais je trouve que le concept d'un système de configuration déclaratif est quand même beaucoup plus clair qu'un système mélangeant code Shell et fichier de conf.
Par ailleurs, et pour relancer une énième polémique, je trouve que systemd est finalement assez KISS et correspond bien à la philosophie Unix qui implique d'utiliser des outils dédiés pour chaque action. Car, c'est bien ce qu'on retrouve dans systemd: les informations sur la machine se gèrent avec hostnamectl, le réseau avec networkd, la gestion du temps avec timesynd, la gestion de la résolution DNS avec resolved. Systemd propose donc plein d'outils dédiés pour chaque action d'administration système.
Dans tous les cas, pour moi, il n'y a plus de débats possibles: mon serveur public démarre bien plus vite et sans accroc depuis que j'utilise systemd. Auparavant, j'avais toujours mon service dovecot qui ne fonctionnait pas (simplement parce qu'il s'activait au mauvais moment par rapport à l'état de configuration de la carte réseau). J'ai passé des journées entières pour débugguer cette situation sans jamais y parvenir. La migration systemd a tout simplement réglé mon problème après quelques heures de recherche dans les logs.