Hero Image

News

Das Problem

Auf einem Debian-Server (jessie, systemd 215) läuft eine Web-Applikation, die einen Samba-Share (cifs) via openvpn (IPv6) benötigt. Mit der naiven Konfiguration funktioniert manuelles Starten und Stoppen problemlos, beim Reboot geht's aber nicht richtig. Naive Konfiguration:

  • Debian Packages: apache2 cifs-utils openvpn
  • cifs mount in /etc/fstab konfiguriert
  • openvpn Konfiguration in /etc/openvpn/client.conf

Wenn man alles einzeln startet, funktioniert es reibungslos: - systemctl start [email protected]

  • mount /var/www/data
  • systemctl start apache2.service

Auch das Stoppen geht manuell problemlos: - systemctl stop apache2.service

Aber bei einem reboot klappt weder Hochfahren... - Beim Start wird der mount versucht, bevor das VPN verfügbar ist und schlägt natürlich fehl.

  • Apache läuft zwar, aber die Web-Applikation hat ein Problem, sobald sie auf Files im cifs mount zugreifen will.

... noch Runterfahren: - Das VPN ist unterbrochen, bevor der umount abgeschlossen ist.

  • Das System hängt 2 Minuten bis der Timeout für den umount abgelaufen ist. Sehr lästig.

Ansatz #1

Die Lösung sollte mit systemd units nicht allzu schwierig sein. Da sich in /etc/fstab keine Abhängigkeiten explizit formulieren lassen, wird für den mount eine eigene Unit-Datei erstellt. Wir starten mit der automatisch erzeugten Unit-Datei:

systemctl cat var-www-data.mount > /etc/systemd/system/var-www-data.mount

Und löschen die Einträge SourcePath und Documentation, so dass nur noch das Minimum da steht:

#/etc/systemd/system/var-www-data.mount
[Unit]
Before=remote-fs.target


[Mount]
What=//v6.smb.example.com/WwwDataShare
Where=/var/www/data
Type=cifs
Options=ro,guest,iocharset=utf8

Dann wird der Eintrag aus /etc/fstab gelöscht und die Unit aktiviert mit: systemctl enable var-www-data.mount; systemctl daemon-reload

/var/www/data müsste jetzt sauber ein- und wieder ausgehängt werden können.

Jetzt können wir die Abhängigkeit zu openvpn formulieren:

#/etc/systemd/system/var-www-data.mount
[Unit]
Before=remote-fs.target
[email protected]
[email protected]

systemctl daemon-reloadFunktioniert leider immer noch nicht. Nach diversen Analysen mit journalctl -f war klar, dass

  • die openvpn Unit beim Start behauptet, sie sei "fertig", obwohl die IPv6 Routen noch nicht korrekt gesetzt sind.
  • der umount von /var/www/data beim Stoppen behauptet, er sei fertig, obwohl es noch ausstehende Netzwerk-Kommunikation hat.

Lösung

Zuerst braucht es ein Script, welches die Vorbedingungen beim Starten (ping) und Stoppen (umount) prüft: /etc/openvpn/checks_updown

#!/bin/bash
LOGGER_RED="systemd-cat -t $0 -p err"
LOGGER_BOLD="systemd-cat -t $0 -p notice"
LOGGER_NORM="systemd-cat -t $0 -p info"


case "$1" in
  ping) 
    while true
    do
      if ping6 -q -c 3 v6.smb.example.com > /dev/null 2>&1 
      then
        echo ping6 ok | $LOGGER_BOLD
        exit 0
      else
        echo "." | $LOGGER_NORM
        sleep 4
      fi
    done
    ;;
  umount) 
    while true
    do
      if mount | grep "//v6.smb.example.com/" >/dev/null 2>&1
      then
        echo ";" | $LOGGER_NORM
        sleep 4
      else
        echo umount ok | $LOGGER_BOLD
        exit 0
      fi
    done
    ;;
  *) 
    echo "invalid call: $1" | tee | $LOGGER_RED
    exit 1
    ;;
esac



Für dieses Script bauen wir eine eigene Unit: ```ini

/etc/systemd/system/vpnbarrier.service

[Unit] Requires=[email protected] After=[email protected]

[Service] Type=oneshot RemainAfterExit=yes ExecStart=/etc/openvpn/checks_updown ping ExecStop=/etc/openvpn/checks_updown umount

[Install] RequiredBy=var-www-data.mount


 Das `RemainAfterExit=yes` bewirkt (zusammen mit `oneshot`), dass `ExecStop` erst beim Stop dieser Unit ausgeführt wird. Ohne diese Einstellung würde `ExecStop` unmittelbar nach `ExecStart`, also noch während des Hochfahrens, ausgeführt. Abhängigkeiten in der `mount`-Unit erweitern:


```ini
# /etc/systemd/system/var-www-data.mount
[Unit]
Before=apache2.service
Requires=vpnbarrier.service
After=vpnbarrier.service


[Mount]
...


[Install]
RequiredBy=apache2.service

systemctl enable vpnbarrier.service; systemctl daemon-reloadErst jetzt funktioniert alles wie es muss:

  • systemctl stop [email protected]: Zuerst wird apache2 gestoppt, dann /var/www/data abgehängt und erst dann das VPN gestoppt.
  • systemctl start apache2.service: Zuerst wird das VPN gestartet, dann der cifs-mount und schliesslich apache2.
  • Ein reboot dauert knapp 20 Sekunden.

Bemerkungen

  • Die Dokumentation von systemd ist recht gut, zum Beispiel bei digitalocean. Der Schwerpunkt ist aber meistens auf dem Hochfahren und man findet kaum etwas zum Runterfahren.
  • Eine etwas andere Diskussion hat gezeigt, dass es auch andere gibt, denen etwas fehlt.
  • Die wichtigste Einsicht: Immer sowohl Requires als auch After benutzen, nur so kriegt man auch das Runterfahren in den Griff.
  • Root Cause: Ist das Problem ein generisches systemd-Problem oder ein Bug im openvpn-Paket? Und beim cifs umount?
  • Könnte man das Problem noch etwas eleganter Lösen, z.B. indem man auf eine separate Unit vpnbarrier.service verzichtet und den "ping"- bzw "umount"-Check durch Einträge in *.conf.d-Verzeichnisse geeignet einbaut?
  • Ab und zu war es nicht ausreichend, systemd daemon-reload zu machen, erst nach einem Reboot hat alles wie erwartet funktioniert.