Systemd: Service-Start von Netzwerk-Interface abhängig machen

In meinem letzten Beitrag habe ich mein PeerVPN-Setup zur Realisierung meines Wartungsnetzes vorgestellt. Wenige Stunden später musste ich feststellen, dass ich bei meinem Setup etwas wichtiges nicht bedacht hatte: Bei einem Server-Neustart versuchen die Dienste, die nur über das Wartungsnetze 10.8.1.0/24 erreichbar sein sollen, sich an das srvnet0-Interface zu binden. Das schlägt allerdings fehl, denn der Docker-Container mit dem PeerVPN-Node startet erst wesentlich später, und die srvnet0-Schnittstelle existiert bis zu diesem Zeitpunkt noch nicht. Das wird mit Fehlermeldungen und nicht verfügbaren Diensten bestraft. Die Lösung ist, diese Dienste warten zu lassen, bis srvnet0 verfügbar ist.

Ein neues Systemd-Target einführen

Da der Zeitpunkt der srvnet0-Verfügbarkeit ein wichtiger Anker für weitere Abhängigkeiten ist, habe ich mich entschieden, auf meinen Systemen ein neues Systemd-Target /etc/systemd/system/srvnet0.target einzuführen:

[Unit]
Description=target is active if srvnet0 interface is available
Requires=sys-subsystem-net-devices-srvnet0.device
After=sys-subsystem-net-devices-srvnet0.device

Sobald die srvnet0-Schnittstelle verfügbar ist, wechselt der Status des Targets auf “active”, und alle vom Target abhängigen Dienste werden nun ebenfalls gestartet. Der Docker-Container mit PeerVPN wird bereits durch den automatisch startenden Docker-Daemon gestartet, denn als “restart”-Policy wurde für diesen Container “always” angegeben. Darum müssen wir uns also nicht kümmern.

Weitere Dienste vom Target abhängig machen

Nun, da das neue Target definiert ist, und den Zeitpunkt markiert, an dem die VPN-Schnittstelle verfügbar ist, muss dieses nur noch in die Service-Definitionen als Requirement aufgenommen werden:

After=srvnet0.target
Requires=srvnet0.target

Es ist allerdings ratsam, diese Definition nicht in bereits bestehenden Unit-Files zu ergänzen, sondern stattdessen eine weitere Konfigurationsdatei zu erzeugen, welche ein bestehendes Unit-File automatisch ergänzt. Soll beispielsweise der Unbound-Resolver (Service: unbound.service) auf die srvnet0-Schnittstelle warten, so wird ein Verzeichnis /etc/systemd/system/unbound.service.d/ angelegt, und darin eine Datei unbound.conf. Diese enthält folgenden Inhalt:

[Unit]
After=srvnet0.target
Requires=srvnet0.target

Systemd führt alle Unit-Files inkl. Ergänzungsdateien dann automatisch zusammen. Konflikte bei Paket-Upgrades durch manuell veränderte Unit-Files werden somit verhindert und Anpassungen bleiben von den Standardkonfigurationen sauber getrennt.

Die neue Systemd-Konfiguration wird nun neu geladen:

systemctl daemon-reload

Nach einem Reboot kann überprüft werden, ob Target und abhängiger Service erfolgreich gestartet wurden:

systemctl status srvnet0.target
systemctl status unbound.service

Beide Kommandos sollten in grüner Farbe “active” zeigen.

Die srvnet0-Schnittstelle ist übrigens schon verfügbar, bevor Traffic durch das VPN geschickt werden kann. Bis die Kommunikation zwischen den Nodes funktioniert, können noch ein paar Sekunden vergehen. Anwendungen können jedoch schon vorher Ports auf dieser Schnittstelle belegen.

Weitere Services können vom Target abhängig gemacht werden, indem für sie ebenfalls ein neues, passend benanntes Verzeichnis mit der oben stehenden Konfiguration angelegt wird. (Neuladen der Systemd-Konfiguration nicht vergessen!)