mountctl: Eine Alternative zur fstab, wenn mount-Versuche ins Netzwerk aus den unterschiedlichsten Gründen fehlschlagen oder zu nervenden Störungen führen
Inhaltsübersicht über die Kapitel dieses Artikels:
|
...*hmmm*.... fast eine gute Frage. Auf die ich allerdings keine bessere Antwort habe als "Weil es funktioniert!". Dabei ist mir natürlich bewusst, dass nicht alles was funktioniert auch wirklich sinnvoll und gut ist. Die bessere Frage wäre vielleicht "Muss eine Lösung wirklich so kompliziert sein?" Aber auch auf die Frage habe ich keine gescheite Antwort, außer vielleicht weil es zum jetzigen Zeitpunkt im aktuellen Debian 9 für eine tatsächlich komplizierte Anforderung noch keine einfache und wirklich funktionierende Lösung gibt. Fakt ist, das Mounten von Netzwerkplatten auf einem (mobilen?) Enduser-Desktop-PC oder mit wechselnden Anwendern ist meiner Meinung nach unter Linux bei solcherart flexiblen Anforderungen absolut unbefriedigend gelöst und von haufenweise unschönen oder sogar problematischen Nebeneffekten begleitet. Fakt ist, die fstab entstammt einer Zeit, zu der vermutlich noch niemand das willkürliche Ein- und Ausschalten eines (mobilen?) Desktop-PCs und das dynamische Mounten und Umounten von Netzwerkplatten an wechselnden Standorten im Sinne hatte, wozu auch noch die User selber berechtigt sein sollen. Die damalige Zielsetzung war vermutlich viel mehr auf klassische Server in Rechenzentren gerichtet. Die Stärken der fstab liegen auch heute noch ganz sicher im statischen System, einschalten, mounten und dauerhaft laufen lassen. Dieser grundsätzliche Gedanke ist aber völlig inkompatibel zu einem Laptop, der sich mal hier, mal dort mit irgendwelchen Netzen verbindet, und mal die Netzwerkplatten mountet und beim nächsten Einschalten eben nicht. Also scheint die Sachlage doch etwas komplizierter zu sein, als anfangs angenommen.
Und weil das meiner Meinung nach so eine komplizierte Angelegenheit ist, welche die fstab tatsächlich nicht akzeptabel handhaben kann, ist meine Lösung auch dementsprechend (vermeintlich) kompliziert ausgefallen. Ich habe zuvor vieles getestet, um vielleicht mit dem einen oder anderen Verfahren eine vernünftige Lösung einzustellen. Ich habe versuchsweise mit PAM-Mounts experimentiert, mit automounts, traditionell mit der fstab sowieso, mit systemd - irgendwie war nichts dabei, was meinen Anspruch an 'reproduzierbar und stabil' auch wirklich zufriedenstellend erfüllt hat. Irgendein ungelöstes Problem bestand immer. Um die Zusammenhänge und Probleme besser zu verstehen, sind jetzt jedoch ein paar weitergehende Erläuterungen und Betrachtungen notwendig.
Dabei hilft ein kurzer Blick auf einen Musterausschnitt einer meiner frühen fstab's, der neben weiteren Einträgen prinzipiell genau so und identisch auf all unseren Systemen eingerichtet werden sollte. Das ist übrigens eine unverzichtbare und alternativlose Prämisse: Ich will keinesfalls geräte-individuelle Einstellungen einrichten müssen... der Aufwand wäre mir viel zu hoch. Eine Lösung muss für alle Systeme anwendbar sein, idealerweise durch einfaches Kopieren von einigen wenigen immer gleichen Dateien an immer die gleichen Zielorte. So sahen also früher beispielhaft unsere Remote-Mounts aus, die unabhängig von der Anmeldung eines Users bei jedem Rechnerstart gemountet wurden:
# Admin-Shares //172.1.1.2/HD1 /media/HD1 cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=thomas,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/HD2 /media/HD2 cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=thomas,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/SSD /media/SSD cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=thomas,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/Install /media/Install cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=thomas,rw,auto,nosuid,nodev,noexec,nouser,async
# Public Shares //172.1.1.2/Film /media/Film cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=sambauser,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/Musik /media/Musik cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=sambauser,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/Buch /media/Buch cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=sambauser,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/Foto /media/Foto cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=sambauser,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/Downloads /media/Downloads cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=sambauser,rw,auto,nosuid,nodev,noexec,nouser,async //172.1.1.2/DatenAlle /media/DatenAlle cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=sambauser,rw,auto,nosuid,nodev,noexec,nouser,async
# Private Shares //172.1.1.2/homes/SHome /home/thomas/SHome cifs credentials=/home/thomas/.smbcred,uid=thomas,gid=thomas,rw,auto,nosuid,nodev,nouser,async //172.1.1.2/homes/SHome /home/silvia/SHome cifs credentials=/home/thomas/.smbcred,uid=silvia,gid=silvia,rw,auto,nosuid,nodev,nouser,async //172.1.1.2/homes/SHome /home/manuel/SHome cifs credentials=/home/thomas/.smbcred,uid=manuel,gid=manuel,rw,auto,nosuid,nodev,nouser,async //172.1.1.2/homes/SHome /home/steffi/SHome cifs credentials=/home/thomas/.smbcred,uid=steffi,gid=steffi,rw,auto,nosuid,nodev,nouser,async //172.1.1.2/homes/SHome /home/bibi/SHome cifs credentials=/home/thomas/.smbcred,uid=bibi,gid=bibi,rw,auto,nosuid,nodev,nouser,async //172.1.1.2/homes/SHome /home/marco/SHome cifs credentials=/home/thomas/.smbcred,uid=marco,gid=marco,rw,auto,nosuid,nodev,nouser,async |
Der obenstehende fstab-Beispielabschnitt sieht eigentlich völlig normal und unspektakulär aus - Admin-Shares, auf die nur ich zugreifen darf, Public-Shares, die von allen verwendet werden und persönliche Verzeichnisse, auf die nur der User selber Zugriff hat. Aber alle Mounts wurden mit meinen Samba-Rechten eingerichtet. Für den Samba-Server gab es also nur einen einzigen Anwender, und zwar immer nur mich. Es blieb dabei völlig unberücksichtigt, wer sich tatsächlich am PC angemeldet hat, was zu Folge hatte, dass individuelle Samba-Berechtigungen damit quasi komplett ausgehebelt sind. Die Besonderheit an dieser fstab ist also der Umstand, dass sie einfach nicht zufriedenstellend funktioniert. Aber diese Besonderheit ist gleichzeitig nach meinen Erfahrungen auch Linux-Normalität. Wenn man eine Zeitlang regelmäßig diverse Linux-Foren besucht, erkennt man, dass das ein oft wiederkehrendes Problem ist, an dem unzählige private Heimnetz-Admins scheitern. Und welche Probleme genau gibt es damit...?... einige! Um einmal die bei uns vorhandenen Probleme zu verstehen, muss man allerdings vorher noch einige Rahmenbedingungen unseres Netzwerkes und der Art und Weise unserer Hardware-Nutzung kennen:
Alle unsere Rechner sind so eingestellt, dass typische Anwender-Daten zentral auf dem Server gespeichert werden. Dateien wie Office-Dokumente, Mails, Kontakte und Termindaten, etc. landen alle auf dem Server. Alle PCs haben aber trotzdem noch für jeden User ein lokales Homedir auf dem jeweiligen PC eingerichtet. | |
Es gibt bei uns Multiuser-PC/Laptops, an denen sich je nach Bedarf verschiedene Personen anmelden, die natürlich an jedem PC jeweils ihre eigenen Daten sehen wollen. Wer sich wann und wo anmeldet, ist willkürlich. | |
Es gibt Laptops, die sich via WLAN anmelden und die von Fall zu Fall ebenfalls von verschiedenen Personen jeweils mit ihrem eigenen Account und ihren eigenen Daten genutzt werden wollen. | |
Unsere Laptops melden sich zum Teil auch an wechselnden WLAN-Netzen an. Zum Beispiel meldet sich der Laptop meines Sohns Zuhause an, in der Uni und bei der Freundin. Ich wechsel den WLAN-Anbieter mit meinem Laptop auf Reise täglich. Das heißt, die fstab soll die Mounts durchführen, wenn wir uns zuhause verbinden, sie darf aber nicht blockieren, wenn wir uns in fremden Netzen anmelden. |
Aus diesem Umfeld und diesen speziellen Anforderungen resultieren in der Folge einige Probleme, für die eine Lösung notwendig ist.
Die fstab wird beim Systemstart abgearbeitet wird, also ein Zeitpunkt, an dem nicht bekannt ist, wer sich auf einem Multiuser-PC irgendwann anmelden wird und ob sich vielleicht sogar nacheinander verschiedene User anmelden. Also müssen immer alle Mounts auf allen Clients quasi vorauseilend mit einer umfassenden Berechtigung hergestellt werden, eine Berechtigung, die tatsächlich alle Fälle umfasst. Die Folge ist, auf dem Server ist nicht transparent ersichtlich, welcher User letztendlich die Mounts verwendet hat. User- oder Gruppenbezogene Samba-Berechtigungen werden damit quasi außer Kraft gesetzt. | |
Weil beim Systemstart unbekannt ist, ob und wer sich anmeldet, müssen also immer (quasi vorauseilend) alle Mounts hergestellt werden, auch die, die für diese Anmeldung gar nicht gebraucht werden oder wenn spezielle Ressourcen an 'andere‘ User gebunden sind. | |
Wenn die Laptops sich via WLAN verbinden, schlägt der Mount beim Systemstart immer fehl, weil die fstab vom System abgearbeitet wird, bevor das Netzwerk gestartet und verbunden ist und die gewünschte Ressource noch nicht erreichbar ist. | |
Bei einem an verschiedenen Standorten eingesetzten mobilen Laptop sollen die Freigaben des Heimservers nur verbunden werden, wenn der Heimserver entweder im eigenen Netz oder unterwegs via OpenVPN überhaupt erreichbar ist. | |
Bei Einsatz eines Network-Managers (NWM) und einer WLAN-Verbindung passiert es oft oder regelmäßig, dass der NMW beim Shutdown des Rechners die WLAN-Netzwerkverbindung trennt, bevor die Mounts getrennt sind. Daraus resultieren dann beim Runterfahren des Rechners diese 90-Sekunden-Stopjobs beim Versuch des Umounts, was ja jetzt gar nicht mehr möglich ist und in Folge dessen wird der Shutdown-Prozess blockiert. |
Kurz zusammengefasst bestehen also diese Probleme:
Die fstab unterscheidet nicht nach User und wartet nicht aufs Netzwerk. Die Credentials zum Mount und somit auch die benutzerbezogenen Samba-Berechtigungen sind nicht an den User gebunden, der letztlich die Netzwerk-Ressourcen nutzt. | |
PAM-Mount unterscheidet zwar nach User, wartet aber auch nicht aufs Netzwerk oder prüft die Verfügbarkeit des Netzes. | |
Systemd-Mounts können zwar auf eine Netzwerkverbindung warten, aber nicht erkennen, ob es das richtige Netzwerk mit Samba-Server ist, und sie unterscheiden ebenfalls keine User. Die Mounts erfolgen im Boot-Prozess, wo wiederum nicht bekannt ist, welcher User sich später überhaupt anmelden wird. | |
In der Bootphase verbundene Mounts sind aktiv, obwohl sich noch gar kein User angemeldet hat und sie bleiben weiter geöffnet, nachdem sich der User abgemeldet hat | |
Warten bis das Netzwerk bereit ist, bedeutet nicht auf irgendein Netz zu warten, sondern auch festzustellen, ob der Samba-Server in diesem Netz überhaupt erreichbar ist. | |
Automounts warten weder aufs Netzwerk, noch unterscheiden sie nach User… der User bekommt, was er öffnet (sofern er berechtigt ist, das Netzwerk verbunden ist und die Ressource verfügbar ist). Wie bei der fstab sind User-Bezogene Samba-Berechtigungen nicht verfügbar. | |
Es gibt so gut wie keine Unterstützung bzw. Abstimmung zwischen Mount und Netzwerkverbindung für On-the-fly-Mounts via OpenVPN, wenn unterwegs fremde Netze via WLAN verbunden sind. |
In den folgenden Kapiteln versuche ich jetzt nach und nach die hier zuvor beschriebenen Probleme zu lösen. Aber die hier gefundenen Problemlösungen sind viel mehr als nur starr fokussierte Lösungen, es sind teilweise auch 'integrierte' Problemlösungen. Das bedeutet, sie funktionieren zwar an sich und natürlich auch ohne sich um anderes zu kümmern, aber zusätzlich eben auch im Zusammenspiel mit anderen Komponenten auf höherer und integrativer Ebene. Ein hochgradig relevanter Aspekt ist hier z.B. meine Backup-Strategie, die ich mit dem Programm xtbak realisiert habe. Das bezieht sich auf die Organisation der Speicherorte im Netzwerk Und ich hatte hier bereits mehrfach die auf Reise oder unterwegs bestehenden Probleme erwähnt, wenn der eigene Samba-Server auch unterwegs via OpenVPN erreicht werden soll. Genau vor diesem Hintergrund war ein abgestimmtes Zusammenwirken zwischen den Komponenten (selnic, meiner Firewall und der OpenVPN-Installation) das höhere Ziel.
|
Problem 1: Ist unser Server über das verbundene Netzwerk überhaupt erreichbar, um erfolgreich mounten zu können?
Die Frage mag auf den ersten Blick merkwürdig klingen, wenn wir doch eigentlich erwarten, dass er mit dem Einschalten selbstverständlich sofort erreichbar ist... das ist sie aber nicht. Wir müssen nur einmal daran denken, dass wir mit unserem Laptop auch mal nicht zu Hause am Schreibtisch oder auf der Couch sitzen, sondern mit einem fremden Netzwerk verbunden sind, z.B. bei Mc Cafe oder irgendwo auf Reise über einen offenen Hotspot in der City. Eine Netzwerkverbindung ist erfolgreich hergestellt, wir können im Internet surfen, aber von dort sind unsere heimischen Netzwerkfreigaben trotzdem ganz bestimmt nicht erreichbar. Der Mount-Versuch durch die fstab-Bearbeitung wird in jedem Fall fehlschlagen.
Darüber hinaus sollten wir uns bewusst machen, dass auch zuhause eine erfolgreich hergestellte Netzwerkverbindung nicht sofort mit dem Betätigen des Einschaltknopfes verfügbar ist. Wenn das Betriebssystem gestartet ist, muss vom Kernel erstmal das Netzwerkinterface (die Hardware) identifiziert und initialisiert werden, die passenden Kernel-Module zur Hardware-Unterstützung müssen geladen werden, die Netzwerksoftware muss geladen und gestartet werden, der Network-Stack versucht dann eine Verbindung zu einem Netzwerk herzustellen und muss bei Erfolg darauf warten eine IP-Adresse vom DHCP-Server zu bekommen. Und irgendwann mit Zuweisen der erhaltenen IP-Adresse an das eigene Netzwerkinterface steht der Kommunikation mit anderen Geräten im Netzwerk oder dem Zugang zum Internet schließlich nichts mehr im Wege. Problematisch wird es nur, wenn die fstab-Bearbeitung schon vorher versucht hat, die Netzwerkfreigaben zu verbinden, was ja in dem Fall, wenn das Netzwerk selber noch gar nicht vollständig verbunden ist, vorhersagbar gar nicht gelingen kann
Also ist das hier bei allen in dieser Dokumentation noch folgenden Lösungsschritten sogar die zentrale Frage. Oder anders gesagt, für die im Netzwerk liegenden Speicher die wichtigste Frage überhaupt, ganz besonders dann, wenn es sich um mobile Geräte mit wechselnden Einsatzorten handelt. Denn wenn der Mountversuch stattfinden würde, bevor z.B. das Netzwerk im Betriebssystem gestartet und auch vollständig verbunden ist oder bevor eine Verbindung zu einem bestimmten entfernten NAS oder zum Samba-Server hergestellt wurde, werden die Mounts immer fehlschlagen - was bei alleiniger Verwendung der fstab fast nichts ungewöhnliches ist... aber gerade deswegen ist es auch einigermaßen unerfreulich. Allerdings darf man jetzt hierbei nicht den Fehler begehen und dem Betriebssystem einen Fehler oder Versagen unterstellen.... nein, beides trifft nicht zu. Dieses Problem ist schlicht und einfach ein Effekt von parallel durchgeführten Starts und Services, was prinzipiell sogar eine wünschenswerte Eigenschaft eines Init-Systems ist.
Unter systemd, dem aktuellen und modernen Init-System der meisten Linux-Distributionen gehören solche Fehlversuche meiner Meinung nach fast sogar zur Normalität, dann besonders forciert auftretend, wenn ein Verbindungsaufbau durch mangelhafte Verbindungsqualität schleppend oder verzögert abläuft oder gar vollständig fehlschlägt. Dabei ist das hier tatsächlich nur ein zeitliches Abstimmungsproblem der Prozesse. Sehr oft wird dann von hilfsbereiten und erfahreneren Anwendern zu Autofs oder entsprechenden Automount-Service-Units geraten, die das Problem über einen Workaround quasi umschiffen, so das der Zeitpunkt des Mounts von der Bootphase losgelöst wird und erst zu einem Zeitpunkt stattfindet, wenn sich der User angemeldet hat. Im Regelfall kann man dann davon ausgehen, dass das Netzwerk erfolgreich verbunden ist. Allerdings bedeutet das noch lange nicht, dass der entsprechende Server auch wirklich erreichbar ist. Denn, wenn ich mit meinem Laptop irgendwo auf der Welt unterwegs bin, ist von meinem NAS zuhause weit und breit nichts zu sehen, und genau das wird natürlich wieder nicht geprüft. Ein solche spezielle Situation ist auch mit systemd-automounts kaum zu lösen. Diese Umstände oder auch Mankos kann man allerdings nicht systemd anlasten, es ist einfach ein Effekt von parallel erfolgten Starts von Diensten und Services und der Unkenntnis von systemd, welche Absichten ich im Moment mit meinem Laptop gerade habe. Eins ist allerdings gewiss ... mein Wissen darum, was ich ganz bestimmt nicht will: und zwar das eine mangelhafte Abstimmung der Komponenten untereinander auf meinem System zu nervenden Wartezeiten oder Störungen führt.
Darüber hinaus ist festzustellen, dass netzwerkabhängige Fremdressourcen wie ein NAS oder der Samba-Server immer nachträglich vom User oder Admin des Rechners einem installierten Betriebssystem manuell hinzugefügt wurden. Also hat man als Admin und verantwortlicher Customizer des Systems auch die Aufgabe dafür zu sorgen, dass die für die eigenen Änderungen erforderlichen Abhängigkeiten tatsächlich erfüllt sind. Oder man plant seine Änderungen so in den Systemstart ein (die zuvor erwähnten Automounts), dass das System die Abhängigkeiten erfüllt hat. Allerdings ist das bei einer entfernten Ressource leider nicht ganz so einfach, weil eben der aktuelle Zustand dieser entfernten Ressource (das NAS einer anderen Hardware) eben nicht selbstverständlich im startenden eigenen Rechner resp. Betriebssystem bekannt ist.
Für die Prüfung, ob mein Samba-Server überhaupt verfügbar ist, verwende ich das folgende Script, welches über eine Service-Unit bei Systemstart gestartet wird. Zur Anpassung auf die eigenen Gegebenheiten ist nur die in der Service-Unit rot markierte IP-Adresse auf die Adresse des eigenen Servers oder NAS zu ändern. Das Script überpüft mit Pings etwa anderthalb Minuten lang, ob der Server antwortet. Antwortet er, können die Mounts durchgeführt werden. Antwortet er nicht, wird der Versuch abgebrochen und die Service-Unit endet mit einem failed-state. Alle Mount-Service-Units sind vom Ergebnis dieses Scripts abhängig und mounten 'ihre' Freigabe nur, wenn der Server erreichbar ist. |
/usr/local/bin/serverctl | Rechte-Einstellung: root:root 755 |
#!/bin/bash #============================================================================================= # Description : Check if given server is reachable # # Script-Name : serverctl # Version : 2.0 # Date : 02.12.2018 # Written by : TomL*thlu.de # Licence : GNU GPL3 #=============================================================================================
[ -z "$1" ] && Server="8.8.8.8" || Server=$1 echo "active/running Server=$Server" | systemd-cat -t "thlu:$(basename $0)" -p "info"
timeout=85 Diff=0 HomeNetIsConnect=-1
Start=$(date +%s);
while [ true ]; do /bin/ping -c1 -W1 -q $Server &>/dev/null HomeNetIsConnect=$?
[ $HomeNetIsConnect -eq 0 ] && break /bin/sleep 0.5
End=$(date +%s); Diff=$((End-Start)) [[ Diff -gt timeout ]] && break done
rc=0 if [[ $HomeNetIsConnect -eq 0 ]]; then echo "Host $Server is reachable! (RC:$HomeNetIsConnect, after $Diff Seconds wait)" | systemd-cat -t "thlu:$(basename $0)" -p "info" else echo "Host $Server is not reachable! (RC:$HomeNetIsConnect, after $Diff Seconds wait)" | systemd-cat -t "thlu:$(basename $0)" -p "err" rc=1 fi
echo "Successful terminated with exitcode=$rc" | systemd-cat -t "thlu:$(basename $0)" -p "info" exit $rc #============================================================================================= #EOF | |
/etc/systemd/system/serverctl.service | Rechte-Einstellung: root:root 644 |
[Unit] Description=thlu:serverctl.service: Waiting for Network or Server to be up After=network.target
[Service] Type=oneshot RemainAfterExit=yes TimeoutStartSec=95 ExecStartPre=/usr/local/bin/serverctl 172.1.1.2 ExecStart=/bin/systemctl start network-is-connect.service ExecStop=/bin/systemctl stop network-is-connect.service
[Install] WantedBy=multi-user.target | |
/etc/systemd/system/network-is-connect.service | Rechte-Einstellung: root:root 644 |
[Unit] Description=thlu:network-is-connect.service: Starts after Network is connected or before disconnected
[Service] Type=oneshot RemainAfterExit=yes
ExecStart=/bin/true ExecStop=/bin/true |
Für den erwünschten automatischen Start des Scripts beim Hochfahren des Rechners ist die Service-Unit einmalig zu aktivieren. Wie in meinen anderen Artikeln ebenfalls üblich, wenn es sich um echte administrative Arbeiten am System handelt, sind diese Arbeiten nicht mit root-Rechten, sondern durch root selber durchzuführen. Und wie üblich gehe ich deshalb nicht auf 'sudo' ein oder beziehe das in mein Vorgehensweise ein.... 'sudo' auf einem Enduser-Desktop-PC ist für mich zweifelsfrei ein die System-Sicherheit gefährdendes NoGo.
$ su -
# cd /etc/systemd/system
# systemctl start serverctl.service
Wir warten ein paar Sekunden und kontrollieren sofort, ob das Script erfolgreich seinen Job verrichtet hat:
# journalctl -b | grep serverctl
Nov 28 15:08:48 thomaspc systemd[1]: Starting thlu:serverctl.service: Waiting for Network or Server to be up...
Nov 28 15:08:48 thomaspc thlu:serverctl[2116]: active/running Server=172.1.1.2
Nov 28 15:08:53 thomaspc thlu:serverctl[2447]: Host 172.1.1.2 is reachable! (RC:0, after 5 Seconds wait)
Nov 28 15:08:53 thomaspc thlu:serverctl[2450]: Successful terminated with exitcode=0
Nov 28 15:08:53 thomaspc systemd[1]: Started thlu:serverctl.service: Waiting for Network or Server to be up.
# systemctl status serverctl.service
● serverctl.service - thlu:serverctl.service: Waiting for Network or Server to be up
Loaded: loaded (/etc/systemd/system/serverctl.service; enabled; vendor preset: enabled)
Active: active (exited) since Wed 2018-11-28 15:08:53 CET; 1h 24min ago
::::
# systemctl status network-is-connect.service
● network-is-connect.service - thlu:network-is-connect.service: Starts after Network is connected or before disconnected
Loaded: loaded (/etc/systemd/system/network-is-connect.service; static; vendor preset: enabled)
Active: active (exited) since Wed 2018-11-28 15:08:53 CET; 1h 24min ago
::::
Wofür ist die network-is-connect.service? Am besten ist diese Serve-Unit im übertragenen Sinne mit Signal-Fahne beschrieben, also so etwas wie eine Flagge ... die allerdings ansonsten keine besondere Aufgabe hat. Ist die Flagge gehisst (Unit=active), ist das vorgegebene Netzwerk verbunden, sonst nicht. Man kann diese Unit auch nicht als Dependency-Unit verwenden, weil sie systemisch eigentlich nicht bekannt ist. Ich verwende diese Unit an besonderen Stellen mit dem Befehl systemctl is-active network-is-connect.service zur Status-Abfrage für meine Netzwerk-Verbindung. Na ja, ist eine einfache Spielerei, nichts wirklich wichtiges.
Als letztes und nur dann, wenn beim Testen keine Fehler aufgetreten sind, ist die Service-Unit jetzt noch einmalig zu aktivieren:
# systemctl enable serverctl.service
Alle hier in dieser Beschreibung angelegten bzw. anzulegenden Dateien können mit dem dieser Beschreibung entsprechenden Inhalt unter der folgenden Adresse runtergeladen werden. Wenn die im Tar-File enthaltenen Dateien in die späteren Original-Verzeichnisse kopiert werden, um sie dort manuell anzupassen, müssen unbedingt auch die Rechte-Einstellungen gemäß dieser Anleitung kontrolliert und ggf. nachbearbeitet werden! Im Übrigen ist es nicht unmöglich, dass die Beispiele hier in der Anleitung weniger aktuell sind, als die im Tar-File enthaltenen Dateien. Deswegen empfehle ich unbedingt, nur die Beispieldateien aus dem Tar-File zu verwenden. |
|
Problem 2: Mount-Units mit Abhängigkeit zur Server-Verfügbarkeit als Ersatz für unpräzise fstab-Einstellungen
Mit der Verfügbarkeits-Prüfung des Remote-Servers durch das zuvor beschriebene serverctl ist eine entscheidende Grundlage zur Lösung des Problems erstellt, dass Mounts in der Bootphase ewig (bis zum systemd-Timeout) hängen bleiben und schlimmstenfalls sogar den Start von Diensten und Services in der Bootphase blockieren, weil der Server nicht verfügbar ist. Hier schließt sich nun der nächste Schritt mit der Entscheidung an, für die Remote-Mounts gar nicht mehr die fstab zu nutzen, sondern direkt mount-Units einzurichten. Der Hintergrund dafür, warum das sinnvoll ist, ist einfach erklärt: systemd erzeugt aus den fstab-Einträgen sowieso virtuelle Mount-Units. Was spricht also dagegen, gleich eigene Mount-Units zu erstellen, die zudem auch noch optimiert eigene Anforderungen erfüllen, anstatt das Geschäft einem Unit-Generator zu überlassen, der das nach Schema-F tut? Gar nichts spricht dagegen, das ist sogar auf jeden Fall die bessere Alternative.
Hier folgend ist eine Beispiel-Mount-Unit beschrieben, die in Abhängigkeit von der Verfügbarkeit des Servers die Netzwerk-Ressource //172.1.1.2/SSD in das lokale Verzeichnis /media/SSD mountet:
/etc/systemd/system/media-SSD.mount | Rechte-Einstellung: root:root 644 |
[Unit]
Description=thlu:Mount Network-Drives
ConditionPathExists=/media/SSD
Options=username=thomas,password=meinpassword,rw,nosuid,nodev,noexec,async
Welche Besonderheiten hat diese Unit?
Requires=serverctl.service Conflicts=shutdown.target | 1. Verlangt, dass serverctl.service ohne Fehler aktiviert wurde (Server ist also verfügbar) 2. Wird in der Bootphase NACH serverctl.service aktiviert und dadurch im Poweroff VOR serverctl.service 3. Sobald shutdown.target angezogen wird, wird diese mount.unit beendet |
Mountpoint muss vorhanden sein, fehlender MP führt zum Abbruch | |
media-SSD.mount <> Where=/media/SSD | Achtung: Durch systemd vorgegebene Namenskonvention! Der Name dieser Mount-Unit steht in unmittelbarer Verbindung zum Mountpoint. Das heisst, aus dem Namen einer Mount-Unit kann immer der dazugehörige Moint-Point abgeleitet werden. Nach dem gleichem Prinzip dieser Namenskonvention werden auch die Namen für die autogenerated Units aus den fstab-Einträgen maschinell generiert. Damit ist es bei Wartungsarbeiten deutlich einfacher, einem Moint-Point einer Mount-Unit zuzuordnen. Ansonsten sieht man vielleicht den Mount-Point und muss sich dann für den Stop-Befehl der Unit fragen "Wie heisst denn nun das 'Dingen', was diesen Mount durchgeführt hat?" Wegen dieser Regel besteht nun die elegante Möglichkeit einfach die Mounts über /proc/mounts auswerten und darüber automatisch immer die jeweils passende namensgleiche Mount-Unit zu finden, ohne vorher wissen zu müssen, welche Units es überhaupt gibt, ob die aktiv sind oder nicht, und wie die Unit für einen bestimmten Mount-Point heisst. Gäbe es da nicht diese Restriktion, wüsstest man nicht, mit welcher Unit dieser Mount-Point erstellt wurde und müsste für jeden Mount-Point manuell suchen. Aber gerade wegen dieser Regel kann man alles auch generisch abhandeln, ohne sich eigene Tabellen zum Nachlesen anlegen zu müssen und ohne sich zu merken, welche Mounts habe ich bei systemstart überhaupt geöffnet. Das ist beispielsweise dann wichtig, wenn man userbezogene Mounts hat, die je nach Anmeldung wechseln. |
nosuid,nodev,noexec | Aus Sicherheitsgründen sind diese Verbote natürlich immer gesetzt. Ausführbare Programme befinden sich nur auf dem lokalen Rechner, aber nicht auf irgendwelchen Netzwerksressourcen. |
username=thomas,password=meinpassword | Kann natürlich auch in der üblichen Credentials-Datei hinterlegt werden. |
Nach gleichem Schema dieser Beispiel-Mount-Unit kann man nun für seine anderen Netzwerk-Freigaben eigene passende Mount-Units erstellen. Mit solchen Service-Units für die eigenen Remote-Mounts ist das große Problem beseitigt, dass auf einem mobilen Rechner, der sich mal in Reichweite des eigenen Servers befindet und mal nicht, der Start durch unmögliche Mount-Versuche blockiert wird, weil das Netzwerk gar nicht aktiviert ist oder wenn man in einem fremden Netz verbunden ist, in dem der Server zuhause nicht erreichbar ist. Die Kombination aus serverctl.service und Mount-Units sorgt meiner Meinung nach in den meisten Fällen für einen fehlerfreien Ablauf bei Boot und Poweroff des Rechners im Zusammenhang mit Mounts.
Natürlich sind eigene Mount-Units nicht nur auf mobilen Rechnern sinnvoll, man kann sie genauso gut auch auf den stationären Desktop-PCs zuhause einrichten. Selbst wenn es sich auf den Rechnern weitestgehend um statisch eingerichtete Mounts handelt, die immer gleichbleibend bei jedem Systemstart durchgeführt werden sollen, halte ich das dennoch für eine ausgesprochen gute Alternative zur traditionellen (um nicht zu sagen "alten") fstab. Timing-Konflikte zwischen fstab-Verarbeitung und Netzverfügbarkeit sind damit auf jeden Fall Vergangenheit.
Wenn schließlich für jede weitere Samba-Freigabe eine eigene Mount-Unit erstellt ist, wird ganz am Ende jede neue Unit einmalig direkt im Terminal sichtbar im Vordergrund gestartet, um sie auf Fehler zu prüfen. Erfolgt keine weitere Ausgabe, ist das ein gutes Zeichen. Und wenn der Blick auf das Ergebnis der Status-Abfrage auch noch zeigt, dass die Unit erfolgreich gestartet ist, kann sie anschließend für alle weiteren künftigen Systemstarts enable'd werden, um bei jedem Einschalten des Rechners die Freigaben automatisch zu verbinden. Aber Achtung, enable'd wird die Unit nur dann, wenn die Statusabfrage keine Fehler angezeigt hat und die Freigabe tatsächlich fehlerlos auf den Mount-Point gemountet wurde. Es ist nicht möglich einen Fehler zu beheben, in dem die Unit einfach aktiviert wird... vor der Aktivierung müssen vorhandene Fehler immer beseitigt werden. Das gilt nicht nur für diese Mount-Unit, sondern für jede Art von systemd-Service-Units.
$ su -
# cd /etc/systemd/system
# systemctl start media-SSD.mount
# systemctl status media-SSD.mount
● media-SSD.mount - /media/SSD
Loaded: loaded (/proc/self/mountinfo)
Active: active (mounted) since Sat 2018-12-08 19:48:38 CET; 2min 50s ago
Where: /media/SSD
What: //172.1.1.2/SSD
# ls /media/SSD
"enable" nur dann, wenn bei den vorherigen Aktionen keine Fehler aufgetreten sind:
# systemctl enable media-SSD.mount
| Zusammenfassung und Zwischenstand: So, an diesem Punkt angekommen gehe ich davon aus, dass mit der Kombination aus Serverctl und Mount-Units vermutlich (ich behaupte das einfach mal) 99 von 100 privaten Samba-, NFS- oder NAS-Anwendern ihre durch mangelnde Abstimmung bei der fstab-Bearbeitung in der Boot-Phase des Rechners wiederholt verursachten Schwierigkeiten lösen konnten. Probleme, weil z.B. auf Reise unser Heimnetzwerk gar nicht erreichbar ist. Weil ein Rechner (vielleicht ein Raspberry PI) etwas länger für die LAN-Verbindung braucht. Oder weil das WLAN durch Betonwände hakelt. Also einfach nur deshalb, weil der Mount-Versuch durch die fstab-Bearbeitung stattfindet, obwohl das Netzwerk noch gar nicht bereit war, was dann zwangsläufig fehlgeschlagene Mount-Versuche zur Folge hatte. Man liest das immer wieder in den Foren, wenn Leute ihre Probleme schildern "...manuell im Terminal klappt es perfekt, automatisch beim Start dann nicht.... wo ist der Fehler?". Hier haben wir nun eine Lösung. Was auch immer der Grund für Mount-Probleme durch die Reihenfolge bei der Serialisierung von Diensten oder Programmen eines regulären Starts unter eigentlich immer den selben Bedingungen war... das sollte eigentlich hiermit gelöst sein. | |
| Hier ist jetzt mit einem kleinen Script (serverctl) und Mount-Units für die Remote-Mounts eine funktionierende Lösung erstellt, die zusammen die mangelhafte zeitliche Abstimung bei der fstab-Bearbeitung für statische und quasi-persistente Netzwerk-Mount beseitigen - eine passable und ausreichende Lösung für die meisten Linux-Heimnetzwerk-Anwender. |
Aber es gibt noch weitere ungelöste Probleme, die uns durchaus auch mal stören oder nerven könnten, und zwar nicht beim Start, sondern beim Runterfahren des Rechners. Wenn z.B. der Shutdown unseres Rechners wiederholt in 90-Sec-Stop-Jobs hängt und der Shutdown einfach nicht durchgeführt wird oder scheinbar ewig lange benötigt. Das ist sogar ein recht einfach reproduzierbares und ziemlich oft auftretendes Problem, welches aber dennoch nicht alle Debian-Anwender betrifft. Hierbei geht es um die Kombination von Remote-Mounts über reine WLAN-Verbindungen. Wer über eine kabelgebundene Verbindung am Netzwerk angeschlossen ist, wird dieses Problem möglicherweise nicht kennen, WLAN-Benutzer aber leider gar nicht mal so selten. Mit diesem Problem befasst sich das nächste Kapitel.
|
Problem 3: Vermeiden von 90-Sec-Stop-Jobs beim Shutdown des Computers
Das ist ein mir seit Jahren bekanntes ständiges Problem im Zusammenhang mit dem Network-Manager (NWM) und WLAN-Verbindungen. Aber was passiert da überhaupt...?...tja, das ist einfach erklärt: Der problemrelevante Zusammenhang entsteht bei über eine WLAN-Verbindung hergestellte Netzwerk-Mounts. Sobald der Anwender nun den Shutdown (bei weiterhin offenen Mounts) seines Systems veranlasst, wird von systemd das shutdown.target angezogen. Dieses Target veranlasst alle weiteren notwendigen Arbeiten, um das System ordnungsgemäß und möglichst ohne Datenverlust herunterzufahren. Unter anderem werden dabei alle laufenden Dienste aufgefordert, sich ordentlich zu beenden - so auch der NWM. Mit anderem Worten, das komplette System sollte eigentlich auf geordnete Art und Weise terminiert, beendet, abgebrochen werden. Sogar eigenen von mir selbst erstellten Service-Units wird die Möglichkeit eingeräumt, unmittelbar auf das gestartete shutdown.target zu reagieren. Man sieht das in der Mount-Unit aus dem vorherigen Kapitel, welche das Statement Conflicts=shutdown.target enthält. Dieses Statement führt dazu, dass sich eine laufende Service-Unit, sofern sie dieses Statement enthält, als Konfliktreaktion auf den beginnenden Shutdown-Prozess sofort selber ordentlich beendet. Das bedeutet, ein vergessener umount, der dadurch den shutdown möglicherweise bis zum Timeout (90 Sekunden) blockiert, könnte damit eigentlich gar nicht mehr passieren.
Soweit sind das eigentlich die Spielregeln und soweit sollten diese auch allen bekannt sein... Spielregeln, an die sich auch eigentlich alle Komponenten halten müssten... 'eigentlich'.... denn leider werden diese Spielregeln im Zusammenwirken des Networkmanagers (NWM) mit dem für WLAN-Verbindungen verantwortlichen Tool wpasupplicant leider überhaupt nicht zuverlässig beachtet. Bisher konnte ich das Problem fast beliebig auf meinem Laptop mit offenen WLAN-Mounts reproduzieren. Sobald der Shutdown-Prozess über das Start-Menü des Desktop-Environments initiiert wurde, beendet der NWM eine laufende WLAN-Verbindung, in dem einfach das WLAN-Interface resp. wpasupplicant geschlossen wird. Dabei wird nicht beachtet, ob über dieses WLAN-NIC vielleicht noch laufende Prozesse aktiv sind, wie zum Beispiel ein via WLAN verbundenes NAS mit offenen Mounts. Die Verbindung wird einfach gekappt, ohne den betroffenen Prozessen die Möglichkeit zu geben, sich ordnungsgemäß zu beenden und ohne darauf zu warten, dass diese Prozesse im Shutdown-Verlauf sowieso beendet werden. Aber genau dieses harte Kappen der Verbindung führt folgerichtig dann dazu, dass der Linuxinterne mount-helper bis zum Erreichen des Timeouts hartnäckig versucht, seine offenen Mounts ordentlich zu schließen - denn auch dieser wird ja von systemd aufgefordert, sich und seine Ressourcen zu beenden. Irgendwann ist systemd es dann leid und terminiert nach diesen Stop-Jobs mit einem harten Kill, um den Shutdown nach nervenden 90 Sekunden dann noch abschließen und den Rechner ausschalten zu können. Dieses Problem kann also theoretisch auf jedem Laptop auftreten, der via WLAN mit dem Netzwerk verbunden ist und über diese Verbindung durch mounts auf Netzwerkressourcen zugreift.
Solange es sich also um einen regulär durchzuführenden Shutdown handelt, kann man diesen unschönen Effekt mit dem folgenden Script vollständig eliminieren. Das Script umountet einfach alle gefundenen Netzwerk-Ressourcen und initiiert erst dann den shutdown, der danach konfliktfrei durchlaufen kann und sehr schnell abgeschlossen ist. Zum Starten des Scripts habe ich mir einen Desktop-Starter mit dem hier nebenstehenden Icon angelegt. |
/usr/local/bin/mountctl | Rechte-Einstellung: root:root 755 |
#!/bin/bash
while read line
do
/bin/umount $line -f 2>&1
done < <(/bin/cat /proc/mounts | grep // | awk -F ' ' '{ print $1 }')
/bin/systemctl poweroff -i >/dev/null 2>&1
exit 0
~/Schreibtisch/mountctl_poweroff_helper.desktop | Rechte-Einstellung: $user:$user 644 |
[Desktop Entry]
Type=Application
Exec=/usr/bin/pkexec /usr/local/bin/mountctl poweroff
Icon=/usr/local/share/Icons/poweroff.png
Terminal=true
Categories=Utility;
Name=Mountctl-Helper
Comment=Helper for Umount remote-shares before lan is disconnect
Name[de_DE]=Poweroff
Und weil dieser Job natürlich root-Rechte erfordert, ist dafür auch die entsprechende Berechtigung über eine Policy-Regel zu erstellen. Damit sind alle User berechtigt, vor dem Shutdown einen umount für verbundene Netzwerk-Laufwerke ohne weitere Password-Abfrage durchzuführen:
/usr/share/polkit-1/actions/LocalExtPermissions.policy | Rechte-Einstellung: root:root 644 |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<action id="LocalExtPerms.mountctl">
<defaults>
<allow_any>yes</allow_any>
<allow_inactive>yes</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/mountctl</annotate>
</action>
</policyconfig>
Man muss hier zweifelsfrei zugeben, dass das nicht mehr als nur ein Workaround ist, nicht um ein bestehendes Problem zu lösen, sondern um es einfach zu umgehen. Etwas boshaft ausgedrückt kann man es fast auch als Dirty Hack bezeichnen, den man nicht brauchen würde, wenn die Interprozess-Kommunikation resp. die Abstimmung untereinander passen würde. Und mit diesem Script bzw. mit dieser Vorgehensweise ist auch nicht das Problem gelöst, dass der User selber immer noch über die grafischen Menü-Dialoge des NWM die WLAN-Verbindung trotz offener Netzwerk-Mounts ganz einfach manuell trennen kann. In dem Fall entsteht eine vorhersehbare Baustelle, denn der mount-helper ist jetzt nicht mehr imstande, die offenen Mounts zu schließen, zumal er gar keine Kenntnis davon hat, dass er sie überhaupt schließen soll. Ihm sind im wahrsten Sinne des Wortes mitten im Sprint die Beine weggezogen worden. Also, im Gegensatz zum ordentlichen Beenden wird er sogar ständig weiter versuchen, die Mounts wieder zurück ins Leben zu bringen. Das kann bei Aufruf eines Dateimanagers oder des Shutdowns sofort zu einem vorübergehenden Stillstand des Systems führen. Tja, an der Stelle muss man also resümieren: Selbstverursachte Probleme.... und die bestehenden Werkzeuge können das meiner Meinung nach derzeit nicht zufriedenstellend handhaben. Wenn man selber Hand anlegen will und bewusst manuell die WLAN-Verbindung trennen möchte, so müssen auch vorher bewusst manuell die Remote-Mounts getrennt werden, um Probleme zu vermeiden.
Es ist, wie es ist... aber wenn man die Probleme einmal verstanden hat, kann man auch damit umgehen.
|
Problem 4: Einrichten von benutzer-bezogenen Mounts, damit user-individuelle Samba-Berechtigungen auch wirksam sind
In diesem Teil meiner Beschreibung erwartet uns die anspruchsvollste Problemstellung. Aber am Ende erwartet uns auch eine wirklich anspruchsvolle Lösung, die uns mehr Schutz, mehr Sicherheit, mehr Transparenz, mehr Stabilität in unser Netzwerk bringt. Es ist eine Lösung, die ich für meinen Samba-Server konzipiert habe. Bis auf den Teil der [homes]-Sektion (s.u.) wird das auch auf NFS-Servern oder anderen NAS-Geräten funktionieren.
Für die Lösung sind die folgenden Dateien notwendig, von denen die meisten nur kopiert oder repliziert werden müssen. Anzupassen sind lediglich spezielle Mount-Parameter in den Units sowie an 2 Stellen die Server-IP-Adresse
/usr/local/bin/serverctl | Programm zur Prüfung, ob der Server verfügbar ist. Durch Service-Unit gestartet. | - |
/usr/local/bin/mountctl | Programm zur Prozess-Steuerung bei user- individuellen Mount und Umount. Durch Service-Unit gestartet. | - |
/usr/local/bin/sessionctl | Programm zum User-Login-Logout-Handling. Durch PAM-Services gestartet | - |
/etc/systemd/system/serverctl.service | Service-Unit zum Start des Programms serverctl | Enabled = ja |
/etc/systemd/system/suspend-resume.service | Wiederherstellung der Mounts nach Suspend | Enabled = ja |
/etc/systemd/system/mountctl@.service | Service-Unit zum Start des Programms mountctl | Enabled = nein |
/etc/systemd/system/network-is-connect.service | View-Flag, Unit is-active = Server ist verfügbar | Enabled = nein |
/etc/systemd/system/media-SSD@.service /etc/systemd/system/media-???@.service | n instantiierte Service-Units zum Mounten der Freigaben. Je 1 Unit für 1 Mountpoint. | Enabled = nein |
/etc/systemd/system/SHome@.service | 1 instantiierte Service-Unit zum individuellen Mounten des persönlichen servergespeicherten Daten-Verzeichnisses eines Anwenders in sein lokales Client-PC-Homedir | Enabled = nein |
/etc/pam.d/common-session | Startet das Programm sessionctl nach erfolgter Anmeldung eines Users | - |
/usr/share/polkit-1/actions/LocalExtPermissions.policy | Berechtigung für User, das Programm mountctl mit erweiterten Rechten auszuführen | - |
/home/?/.bash_aliases | Alias-Befehle für CLI.... sofern einzelne User das überhaupt benötigen (*) | - |
/home/?/Schreibtisch/mountctl_poweroff_helper.desktop /home/?/Schreibtisch/mountctl_reboot_helper.desktop /home/?/Schreibtisch/mountctl_suspend_helper.desktop | Desktop-Starter für Poweroff, Suspend und Reboot (**) | - |
Anmerkungen:
*) Normale User benötigen das normalerweise nicht
**) Bei kabelverbundenen PCs sollten für Poweroff und Reboot auch die normalen Menü-Aktionen des Desktop-Environments problemlos funktionieren. Bei Suspend wird gleichzeitig jedoch auch das Netzwerk getrennt, vermutlich wird dafür aber vorher kein geregelter umount durchgeführt, insofern ist das Verhalten nach dem Restart nicht genau vorhersagbar. Es handelt sich hier durch die Einbindung von mountctl also um eine vorbeugend konfliktvermeidende Aktion. Das gilt gleichermaßen für alle 3 mountctl-helper.
Auf gehts.....
In diesem Kapitel soll das Problem gelöst werden, dass nicht umfassend und vorauseilend beim Systemstart unter Verwendung einer einzigen Admin-Berechtigung alle möglichen Mounts durchgeführt werden müssen, ohne zu wissen, ob sich die für einen Mountpoint bestimmten oder berechtigten Benutzer an diesem System in dieser Rechner-Laufzeit überhaupt anmelden. Üblicherweise und allgemeine Praxis ist, dass alle Mounts bei Systemstart über die Admin-Credentials des Systemadministrators erfolgen, auf unseren Rechnern wären das meine Credentials. Im Gegensatz zu individuellen User-Rechten würde der Samba-Server dabei niemals den tatsächlich am Client-PC angemeldeten User sehen, sondern immer nur mich. Denn bei Verwendung meiner Credentials wird Samba immer glauben, ich wäre auch der aktuelle Benutzer, ohne für den tatsächlichen arbeitenden User individuelle Berechtigungen anwenden zu können. Der negative Begleiteffekt ist, es werden auf dem Server Samba-Seitig zwangsläufig immer meine höheren Admin-Rechte beim Speichern/Ändern/Löschen von Dateien angewendet.
Mit anderen Worten, das Ziel dieses Kapitels ist, dass nach der Anmeldung eines Users nur die Mounts geöffnet werden, für die der Benutzer entsprechende Rechte hat. Und das infolgedessen die Verwendung dieses Mountpoints auch wirklich unter den Beschränkungen seiner persönlichen Samba-Berechtigungen erfolgt.
Aus dieser Vorgabe resultiert also eine ganz einfache Forderung: Der Mount von möglichen Netzwerks-Ressourcen z.B. eines Samba-Servers darf nicht automatisch beim Hochfahren des Rechners erfolgen, sondern erst dann, wenn sich ein User namentlich und mit Password im Desktop-Environment angemeldet hat. Und natürlich nur dann, wenn der Server zur Verfügung steht. Außerdem sollen auch nur die Ressourcen gemountet werden, die der User auch wirklich verwenden darf und soll. Letztlich ist das auch ein Sicherheitsfeature... oder ein Aspekt, der zumindest einen positiven Einfluss auf die Daten-Sicherheit hat, denn in Zeiten von Ransomware sollte es wirklich nicht sein, dass dem User immer alles von allen/allem zur Verfügung steht. Das wäre im Fall der Fälle die Garantie für den größtmöglichen Schaden bezogen auf Datenverlust. Speziell für die Anforderung "nur was der User darf und können soll" ist nun eine besondere Vorgehensweise notwendig. Mein eigentlicher Lösungsansatz basiert hierbei auf instantiierte Service-Units. Das bedeutet, für einen bestimmten User werden speziell für seine UID eigene Mount-Instanzen erzeugt, bei denen dann die tatsächlichen Rechte dieses Users zugrunde gelegt werden.
Warum sind es hier Service-Units und weiter oben sind es Mount-Units? Tja, hier besteht aufgrund anderer Anforderungen ein gravierender Unterschied zwischen diesen zwei Problemlösungen. Die Mount-Units oben sind quasi statisch und explizit gültig, die Service-Units hingegen sind variabel einsetzbare Templates, die tatsächlich eine personifizierte Instanz für die Mountpoints darstellen. Bedauerlicherweise sind in der aktuellen Debian-Stable-Implementierung von systemd keine instantiierten Mount-Units möglich, deswegen verwende ich hier normale Service-Units. Ich halte das aber nicht für ein Problem oder für nachteilig, im Gegenteil... es ist ein vollständig systemd-Konformer Lösungsansatz.
Eine Besonderheit in den folgenden Mount-Service-Units ist der in Zeile 1 stehende Kommentar: #mountctlgroup=thomas. Dieser Eintrag hat eine dreifache Bedeutung:
1. Für den systemd-Controller ist es wegen dem Zeichen "#" eine Kommentarzeile, die nicht beachtet wird. 2. Für das Programm mountctl ist der Text mountctlgroup ein Identifikations-Kennzeichen, um die betroffenen Service-Units zu identifizieren. Nur Service-Units mit diesem Kennzeichen werden ausgewertet. 3. Im Programm wird der Eintrag hinter mountctlgroup= zur Prüfung verwendet, ob der sich jetzt anmeldende User in einer der hier angegebenen Gruppe befindet. Nur wenn er in der Gruppe enthalten ist, wird diese Freigabe gemountet. Hier in diesem Beispiel muss sich der anmeldende User in der Gruppe "thomas" befinden, damit der Mount durchgeführt wird. Es sind auch solche Einträge möglich, die mehr als eine Gruppe zugrunde zulegen: #mountctlgroup=thomas sambauser mp3user |
Der Eintrag mountctlgroup hat eine andere Zielsetzung und eine andere Auswirkung als beispielsweise die für systemd-Service-Units vorgesehenen Parameter ConditionUser und ConditionGroup. Mit diesen Parametern wird beschränkt, dass die Service-Unit nur unter der UID und GID der hier eingetragenen Parameter aktiviert werden kann. Die hier in diesem Artikel beschriebenen Service-Units werden aber immer durch root und unter der UID 0 gestartet. Hier geht es also nicht darum, unter welcher UID/GID laufen die Prozesse der Service-Units, sondern um die Frage, ob der User berechtigt ist, diesen Mount durchzuführen.
4.1 Service-Units für allgemeine Mounts
Schauen wir nun ein wenig genauer auf die Units und betrachten jeweils die Zielsetzung. Die SSD-Unit und auch die Multimedia-Unit betrachte ich beide als Admin-Shares. Das bedeutet, damit ist ein Zugriff außerhalb der anderen beschränkenden Freigaben direkt auf die SSD oder ein höheres Verzeichnis möglich. Mit solchen administrativen Zugängen haben die normalen Anwender im Regelfall nichts zu tun, also wird der Mount auch nur durchgeführt, wenn der sich anmeldende User in der Linux-Gruppe thomas enthalten ist ... und das kann natürlich immer nur ich selber sein. Die weiteren Units sind allgemeine Freigaben für mehrere Anwender, der Zugriff ist hierbei auf die Mitglieder die Linux-Gruppe sambauser beschränkt. In dieser Gruppe sind die Familienmitglieder enthalten. Ansonsten ist der ablaufende Vorgang identisch. Alle rot markierten Einträge können geändert werden bzw. müssen zur Anpassung an die lokalen Gegebenheit geändert werden.
Hier darf man allerdings auf keinen Fall vergessen, dass für diesen personalisierten Ablauf unbedingt in den Homedirs der Benutzer jeweils das persönliche Credentialsfile angelegt sein muss, denn mit genau diesen Zugangsdaten identifiziert sich ja der Anwender gegenüber Samba.... und nur damit kann Samba anschließend individualisierte Benutzer-Rechte anwenden. | |
Für die folgenden Service-Units hier ein kurzer redaktioneller Hinweis: Aufgrund der Länge des Exec-Start-Statements in den folgenden Service-Units musste ich mich entscheiden, einen möglicherweise falsch zu deutenden Zeilenumbruch hinzunehmen, oder mit einer kleineren Schriftart die Eindeutigkeit einer zusammenhängenden Zeile unzweifelhaft herzustellen. Ich habe mich für die kleinere Schriftart entschieden, die in der endgültigen Unit als Textdatei sowieso egalisiert wird. Die Eindeutigkeit der Code-Zeilen war mir hier wichtiger. |
/etc/systemd/system/media-SSD@.service | Rechte-Einstellung: root:root 644 |
#mountctlgroup=thomas
[Unit] Description=thlu:Mount Network-Drives for specified user: %n Requires=serverctl.service After=serverctl.service Conflicts=shutdown.target ConditionPathExists=/media/SSD
[Service] Type=simple RemainAfterExit=yes ExecStart=/bin/mount //172.1.1.2/SSD /media/SSD -t cifs -o credentials=/home/%I/.smbcredentials,vers=3.0,uid=%I,gid=%I,dir_mode=0770,file_mode=0644,rw,noauto,nosuid,nodev,noexec,nouser,async ExecStop=/bin/umount /media/SSD
[Install] WantedBy=multi-user.target | |
/etc/systemd/system/media-Multimedia@.service | Rechte-Einstellung: root:root 644 |
#mountctlgroup=thomas
[Unit] Description=thlu:Mount Network-Drives for specified user: %n Requires=serverctl.service After=serverctl.service Conflicts=shutdown.target ConditionPathExists=/media/Multimedia
[Service] Type=simple RemainAfterExit=yes ExecStart=/bin/mount //172.1.1.2/Multimedia /media/Multimedia -t cifs -o credentials=/home/%I/.smbcredentials,vers=3.0,uid=%I,gid=%I,dir_mode=0770,file_mode=0644,rw,noauto,nosuid,nodev,noexec,nouser,async ExecStop=/bin/umount /media/Multimedia
[Install] WantedBy=multi-user.target | |
/etc/systemd/system/media-Film@.service | |
#mountctlgroup=sambauser
[Unit] Description=thlu:Mount Network-Drives for specified user: %n Requires=serverctl.service After=serverctl.service Conflicts=shutdown.target ConditionPathExists=/media/Film
[Service] Type=simple RemainAfterExit=yes ExecStart=/bin/mount //172.1.1.2/Film /media/Film -t cifs -o credentials=/home/%I/.smbcredentials,vers=3.0,uid=%I,gid=%I,dir_mode=0770,file_mode=0644,rw,noauto,nosuid,nodev,noexec,nouser,async ExecStop=/bin/umount /media/Film
[Install] WantedBy=multi-user.target | |
/etc/systemd/system/media-Musik@.service | |
#mountctlgroup=sambauser
[Unit] Description=thlu:Mount Network-Drives for specified user: %n Requires=serverctl.service After=serverctl.service Conflicts=shutdown.target ConditionPathExists=/media/Musik
[Service] Type=simple RemainAfterExit=yes ExecStart=/bin/mount //172.1.1.2/Musik /media/Musik -t cifs -o credentials=/home/%I/.smbcredentials,vers=3.0,uid=%I,gid=%I,dir_mode=0770,file_mode=0644,rw,noauto,nosuid,nodev,noexec,nouser,async ExecStop=/bin/umount /media/Musik
[Install] WantedBy=multi-user.target | |
usw. für die anderen Shares. |
4.2 Service-Unit für den Mount eines persönlichen Datenverzeichnisses
Die Verwendung eines Samba-Servers und die Anwender-Individuelle Anmeldung an diesen Server eröffnet uns noch eine weitere tolle Möglichkeit, und zwar die Einrichtung eines persönlichen nur dem Anwender gehörenden Datenverzeichnisses. Ein für die Backup-Strategie signifikanter Vorteil liegt darin, dass diese persönlichen Verzeichnisse aber dennoch für alle Anwender an einem zentralen Ort auf dem Samba-Server liegen. Der sofort erkennbare Vorteil ist offensichtlich, weil von diesem zentralen Ort dann mit geringstmöglichen Aufwand ein Backup aller persönlichen Anwender-Daten erstellt werden kann. Klingt kompliziert, ist es aber nicht. Der Schlüssel für diese Funktionalität ist die anwenderbezogene Anmeldung auf dem Samba-Server, wie sie bisher hier realisiert wurde.
Am einfachsten kann man es sich vorstellen, wenn man auf das nebenstehende Bild schaut. Die gezeigte Festplatte ist entweder die interne Festplatte des Samba-Servers oder eine externe Platte, bei mir ist es die interne SSD des Servers, die nach /media/SSD gemountet ist. Eine starre Festlegung gibt es hier nicht, es ist immer die gleiche Funktionalität möglich. In der fstab des Samba-Servers sind u.a. die folgenden Einträge enthalten: UUID=abcxyz /media/SSD ext4 rw,noexec,auto,nouser,async,noatime 0 0
/media/SSD/Multimedia /media/Multimedia none bind /media/SSD/MultiMedia/Film /media/Film none bind /media/SSD/MultiMedia/Musik /media/Musik none bind /media/SSD/MultiMedia/Foto /media/Foto none bind /media/SSD/MultiMedia/Buch /media/Buch none bind
/media/SSD/SHome/thomas /home/thomas/SHome none bind /media/SSD/SHome/silvia /home/silvia/SHome none bind /media/SSD/SHome/manuel /home/manuel/SHome none bind usw. |
Diese Einträge mounten nicht nur die SSD als ganzes, sondern darüber hinaus auch noch jeweils die Unterverzeichnisse aus /Multimedia und auch die persönlichen und anwendereigenen Verzeichnisse in /SHome getrennt via "bind" in weitere separate Unterverzeichnisse. Dadurch kann dann jedes Unterverzeichnis als Samba-Freigabe einzeln und von allen anderen unberührt über Samba mit eigenen Zugriffsrechten versehen werden, um dann durch den Client-PCs gemountet zu werden. Natürlich muss dazu vorher auf dem Server in /media und in jedem Homedir der passende Mountpoint angelegt werden, zum Beispiel:
mkdir /media/Film
mkdir /home/thomas/SHome
Die Bezeichnung SHome ist willkürlich von mir vorgegeben. Ich habe sie gewählt, um sofort zu erkennen, dass es sich hier um ein gemountetes Server-Verzeichnis handelt, was schließlich das lokale Homedir auf dem Client-PC funktional ergänzt .... ergänzt, nicht ersetzt! Entsprechend der Einträge in der fstab enthält natürlich auch die Samba-Konfiguration des Servers die damit im Zusammenhang stehenden Einträge zur Erstellung der Freigaben. Für die normalen Freigaben exitiert jeweils eine eigene Sektion, hier als Beispiel für die Freigabe "Film":
[Film]
path=/media/Film
writeable = yes
browseable = no
guest ok = no
write list = thomas
valid users = @sambauser
force user = thomas
force group = sambauser
create mask = 0660
directory mask = 0770
force create mode = 0660
force directory mode = 0770
Die Sektion [homes] gilt durch die vorhandene spezielle Samba-Funktionalität für alle User:
[homes]
comment = Home Directories
browseable = no
read only = no
valid users = %S
Mit diesen Einstellungen ist nun möglich, nachdem ich mich auf meinem Client-PC angemeldet habe, dass mein persönliches Datenverzeichnis /home/thomas/SHome des Samba-Servers konfliktfrei (bezogen auf die anderen User-Verzeichnisse) ganz einfach in mein lokales Datenverzeichnis /home/thomas/SHome meines Client-PC gemountet werden kann. Genau diese Aufgabe übernimmt auf meinem Client-PC die folgende spezielle Service-Unit, die sich (im Unterschied zu den vorherigen Units) eben diese besondere Samba-Eigenschaft zunutze macht.
Das servergespeicherte Verzeichnis ist die Samba-Freigabe, das gleichnamige PC-gespeicherte Verzeichnis ist der Mountpoint für diese Freigabe, gemountet wird die Freigabe durch die folgende Service-Unit mit dem immer gleichen Freigabename //$(IP)/homes/SHome nach Anmeldung des Users im Linux. Und das ist die Besonderheit, obwohl immer der gleiche Freigabename verwendet wird, erfolgt dennoch ein anwenderbezogener Mount dieser Freigabe. Das bedeutet, es wird immer die persönliche Homedir-Freigabe des Users zugewiesen, der sich auch tatsächlich mit seinen Credentials angemeldet hat.
An dieser Stelle ist allenfalls noch erwähnenswert, warum ich nicht konsequent server-gespeicherte Homedirs verwende. Ich habe mich damals aus Performance-Gründen dagegen und für die Beibehaltung lokaler Homedirs entschieden. Aufgrund von wechselnder Hardware durch die User würde das andere vermutlich über kurz oder lang zu Problemen führen. Die Lösung war eben, nur die persönlichen Dateien des Anwenders auf dem Server zu speichern. Jedes unserer lokalen Homedirs enthält also seit jeher schon ein Datenverzeichnis mit dem Namen ~/SHome , was faktisch aber nur ein Mountpoint für das gleichnamige Server-Directory ist, indem schließlich die persönlichen, privaten und schützenswerten Dateien eines jeden Anwenders gespeichert sind. Weil sich dieses Vorgehen in der Vergangenheit als vorteilhaft erwiesen hat, habe ich es bis heute beibehalten.
Die unleugbaren Vorteile dieser Vorgehensweise sind schnell aufgezählt: jeder angemeldete User sieht nur seine persönlichen Daten, die eigenen Dateien sind vor dem Zugriff durch andere Anwender geschützt, dementsprechend sind die Dateien der anderen User vor ihm geschützt. "Fremde" Dateien sind auch dann geschützt, wenn ein User es wirklich geschafft hat, eine Ransomware auf die Daten des Servers loszulassen. In diesem Fall sind nur die jeweils eigenen Dateien betroffen. Ein weiterer Vorteil ist, alle Anwender speichern aus eigenem Interesse ganz zuverlässig alle ihre persönlichen Daten/Dokumente/Dateien in ihre eigene und vor Fremdzugriff geschützte Verzeichnisstruktur nach ~/SHome. Warum tun sie das? Tja ... entweder sind sie zu faul, sich selber um regelmäßige Backups ihrer Daten zu kümmern, oder sie können es nicht zuverlässig. Und durch den Speicherort ~/SHome können sie dieses Problem schlichtweg vergessen, die Sicherung übernimmt zuverlässig und maschinell gesteuert der Server. Ein dritter wirklich signifikanter Vorteil ist, es gibt keine Fragmentierung von Daten, die mal hier, mal dort, mal oben, mal unten, mal links, mal rechts. mal sonst-wo liegen. Die Dateien aller Anwender liegen zentral an einer Stelle, von der sie ohne große Aufwände zuverlässig gesichert werden können, und zwar zentral auf einer Festplatte des Servers in dem auf dieser Platte eingerichteten Verzeichnis /SHome... siehe Grafik etwas weiter oberhalb.
/etc/systemd/system/SHome@.service | Rechte-Einstellung: root:root 644 |
#mountctlgroup=sambauser
[Unit] Description=thlu:Mount Network-Drives for specified user: %n Requires=serverctl.service After=serverctl.service Conflicts=shutdown.target ConditionPathExists=/home/%I/SHome
[Service] Type=simple RemainAfterExit=yes ExecStart=/bin/mount //172.1.1.2/homes/SHome /home/%I/SHome -t cifs -o credentials=/home/%I/.smbcredentials,vers=3.0,uid=%I,gid=%I,dir_mode=0770,file_mode=0644,rw,noauto,nosuid,nodev,noexec,nouser,async ExecStop=/bin/umount /home/%I/SHome
[Install] WantedBy=multi-user.target |
Etwas weiter oberhalb in der kleinen Tabelle am Anfang dieses Kapitels war es schon erkennbar... einige der hier erstellten systemd-Service-Units werden für den Systemstart enabled und einige andere nicht. Es werden tatsächlich nur 2 Service-Units enabled, und zwar die Units serverctl.service und suspend-resume.service. Die für die eigentlichen Mounts verantwortlichen Service-Units sind alle disabled... und das bleiben sie auch. Als Endergebnis ist ja das Ziel vorgegeben, dass die Mounts nicht direkt am Start erfolgen, sondern dynamisch passend zur jetzt durchgeführten Useranmeldung. Genau das ist die Aufgabe des folgenden Programms mountctl, und dafür dürfen sie nicht enabled sein.
Die Personifizierung der aktuell durchzuführenden Mounts wird nach dem Login-Event durch pam.d veranlasst, welches nach der Anmeldung des Users das Script sessionctl startet, von dem dann mountctl.@service als explizit userbezogene Instanz gestartet wird. sessionctl wird erneut gestartet, wenn sich der User vom System abmeldet, um die aktiven Mounts wieder zu trennen.
/etc/pam.d/common-session | Rechte-Einstellung: root:root 644 |
# /etc/pam.d/common-session - session-related modules common to all services # # This file is included from other service-specific PAM config files, # and should contain a list of modules that define tasks to be performed # at the start and end of sessions of *any* kind (both interactive and # non-interactive).
session [default=1] pam_permit.so session requisite pam_deny.so session required pam_permit.so session required pam_unix.so session optional pam_systemd.so
session required pam_exec.so seteuid /usr/local/bin/sessionctl |
/etc/systemd/system/mountctl@.service | Rechte-Einstellung: root:root 644 |
[Unit] Description=thlu:mountctl.service: Connect + Disconnect Remote-Mounts: %n After=serverctl.service Requires=serverctl.service Conflicts=shutdown.target ConditionFileNotEmpty=/home/%I/.smbcredentials
[Service] RemainAfterExit=yes Type=idle ExecStart=/usr/local/bin/mountctl start %I ExecStop=/usr/local/bin/mountctl stop
[Install] WantedBy=multi-user.target |
Um den Ablauf ein wenig besser nachvollziehen zu können, werfen wir einen Blick auf die folgende Grafik, die den Systemstart sehr grob skizziert und speziell nur diesen Aspekt der Boot-Phase und des User-Logins beschreibt. In Worten beschrieben passiert folgendes:
1. Nach dem Einschalten des Rechners startet (irgendwann) der systemd-System-und-Service-Manager, der als Init-Instanz für das erfolgreiche Hochfahren des Systems verantwortlich ist und den Start des Rechners kontrolliert und geordnet durchführt.
2. Der System-Manager startet neben allen anderen Services und Diensten u.a. auch die Service-Unit serverctl.service, mit der die Erreichbarkeit unseres Servers festgestellt wird und die als Flag wiederum network-is-connect.service startet, wenn der gesuchte Server verfügbar ist.
3. Der Anmeldebildschirm erscheint auf dem Monitor, der Anwender kann sich am System anmelden
4. Nach der Anmeldung erscheint für den Anwender der "gewohnte" Desktop und der Rechner ist mit Erreichen des systemd-multiuser.targets bereit zur Verwendung
5. Gleichzeitig wird aber auch durch die Anmeldung "getriggert" von der PAM-Infrastruktur das Programm sessionctl gestartet, welches für den angemeldeten User die Service-Unit mountctl@$USER.service startet. Die Service-Unit wiederum startet das Programm mountctl, welches die Mounts für diesen User schließlich durchführt.
6. Meldet sich der User zu einem späteren Zeitpunkt regulär über das Startmenü seiner Desktop-Umgebung ab, wird erneut von der PAM-Infrastruktur das Programm sessionctl gestartet, um nun wieder über mountctl alle für diesen User geöffneten Mounts zu schließen.
7. Trennt der Anwender die aktive Netzwerkverbindung mit selnic oder er beendet (anstatt eines Logouts) über einen der 3 gezeigten Buttons (also über einen Desktopstarter) seine Arbeitssitzung, so werden ordnungsgemäß alle offenen Mounts getrennt. Die dafür notwendige Berechtigung erhält der User über eine Policykit-Regel.
Die Grafik zeigt es eigentlich ziemlich deutlich: Das hier folgende Programm mountctl ist der zentrale Punkt, auf den von verschiedenen Quellen eingewirkt wird. Hier passiert es dadurch, dass andere Prozesse mountctl auffordern, etwas bestimmtes zu tun.... also entweder Mounts durchzuführen, oder geöffnete Mounts zu schließen, oder gar den Rechner vollständig auszuschalten, oder den Rechner auch nur vorübergehend in den Schlaf zu schicken.
/usr/local/bin/mountctl | Rechte-Einstellung: root:root 755 |
#!/bin/bash #=========================================================================================================================================== # Description : Controlled user-binded mounting and unmounting of network drives # # Script-Name : mountctl # Written by : TomL*thlu.de # Version : 2.3 # Date : 27.12.2018 # Lizenz : GNU GPL3 # # Dependencies : dialog, PAM->common-session, polkit->Permission, serverctl, sessionctl #===========================================================================================================================================
Action="" CurrUser="" UsersGroups="" aMountUnits=() aUserGrps=()
[ -n "$1" ] && Action=$1 [ -n "$2" ] && CurrUser=$2 #===========================================================================================================================================
ErrExit() { local tmp=""
[ $1 -eq 1 ] && tmp="Canceled! User=$CurrUser not exist or no user defined or root as user (root is not a samba-user)!" [ $1 -eq 2 ] && tmp="Nothing to do! No mounts defined." [ $1 -eq 3 ] && tmp="Job canceled, wrong or missing parameters. See: journalctl -b | grep mountctl" [ $1 -eq 4 ] && tmp="Job canceled, resume Network failed"
[ $1 -gt 0 ] && /bin/echo "$tmp" | systemd-cat -t "thlu:$(basename $0)" -p "err" /bin/echo -e "$tmp\n\nUsage: $0 {start | stop | poweroff | reboot | suspend} {existing username}"
exit 1 } #===========================================================================================================================================
IsGroupOK() { [ -z "$1" ] && return 0 for i in "${!aUserGrps[@]}"; do [[ "$1" =~ "${aUserGrps[i]}" ]] && return 0 done
return 1 } #=========================================================================================================================================== SetupOrReleaseMounts() { local action="" # 0=mount, 1=umount
[ -n "$1" ] && action=$1 || return 1
for x in ${!aMountUnits[*]}; do tmp=${aMountUnits[$x]}
munit=$(awk -F ':' '{ print $1 }' <<< $tmp) # links von ':' munit=$(basename "$munit") munit="${munit//'@'/@$CurrUser}" # Replace @ durch @Username mgrp=$(awk -F 'mountctlgroup=' '{ print $2 }' <<< $tmp) # rechts von 'mountctlgroup='
IsGroupOK "$mgrp" if [[ $? -eq 0 ]]; then if [[ $action -eq 0 ]]; then if [ ! "$(/bin/systemctl is-active $munit)" == "active" ]; then /bin/echo "start $munit" | systemd-cat -t "thlu:$(basename $0)" -p "info" /bin/systemctl start $munit fi else if [ "$(/bin/systemctl is-active $munit)" == "active" ]; then /bin/echo "stop $munit" | systemd-cat -t "thlu:$(basename $0)" -p "info" /bin/systemctl stop $munit fi fi /bin/sleep 1 else /bin/echo "start $munit failed! group-permissions denied" | systemd-cat -t "thlu:$(basename $0)" -p "info" fi done
return 0 } #===========================================================================================================================================
while read line; do # Relevante Units mit 'mountctlgroup' ermitteln [ -n "$line" ] && aMountUnits+=( "$line" ) done < <(/bin/grep -i "mountctlgroup" /etc/systemd/system/*.service; echo "")
if [ -z "$CurrUser" ]; then # kein user übergeben? if [ -s /var/run/mountctl.user ]; then CurrUser=$(cat /var/run/mountctl.user) # mounts aktiv? ja, gleichen user erneut verwenden
elif [ -s /var/run/sessionctl.user ]; then # oder CurrUser=$(grep -i pam_user /var/run/sessionctl.user | awk -F '=' '{ print $2 }') # neue anmeldung? neuen user übernehmen CurrUser=${CurrUser// /} else CurrUser=$USER fi fi
[ -n "$CurrUser" ] && [ -z "$(cat /etc/passwd | grep ^$CurrUser:)" ] && ErrExit 1 # gültiger user? [ -z "$CurrUser" ] && ErrExit 1 [ "$CurrUser" == "root" ] && ErrExit 1
/bin/echo "active/running Action=$Action CurrUser=$CurrUser" | systemd-cat -t "thlu:$(basename $0)" -p "info" tmp=$(/usr/bin/groups "$CurrUser" 2>/dev/null) tmp=$(awk -F ':' '{ print $2 }' <<< $tmp) aUserGrps=(${tmp// / })
[ ${#aMountUnits[*]} -eq 0 ] && ErrExit 2
#------------------------------------------------------------------------------------------------------------------------------------------- # start = called after login from pam_session-Exec # resume = called after suspend from suspend-resume.service
rc=0 case $Action in start) /bin/echo "Processing mount ($Action)" | systemd-cat -t "thlu:$(basename $0)" -p "info" SetupOrReleaseMounts 0 [ "$Action" == "start" ] && echo "$CurrUser" >/var/run/mountctl.user ;; #------------------------------------------------------------------------------------------------------------------------------------------- stop) sync /bin/echo "Processing umount ($Action)" | systemd-cat -t "thlu:$(basename $0)" -p "info" SetupOrReleaseMounts 1
while read line do /bin/umount $line -f 2>&1 done < <(/bin/cat /proc/mounts | grep // | awk -F ' ' '{ print $1 }')
[ -f /var/run/mountctl.user ] && /bin/rm /var/run/mountctl.user /bin/sleep 1 ;; #------------------------------------------------------------------------------------------------------------------------------------------- poweroff) [ -f /usr/bin/dialog ] && /usr/bin/dialog --infobox "Hinweis:\n\nDer Computer wird in wenigen Sekunden ausgeschaltet." 7 70 /bin/systemctl stop mountctl@$CurrUser /bin/systemctl poweroff -i >/dev/null 2>&1 ;; #------------------------------------------------------------------------------------------------------------------------------------------- reboot) [ -f /usr/bin/dialog ] && /usr/bin/dialog --infobox "Hinweis:\n\nDer Computer wird in wenigen Sekunden neu gestartet." 7 70 /bin/systemctl stop mountctl@$CurrUser /bin/systemctl reboot -i >/dev/null 2>&1 ;; #------------------------------------------------------------------------------------------------------------------------------------------- suspend) [ -f /usr/bin/dialog ] && /usr/bin/dialog --infobox "Hinweis:\n\nDer Computer wird in wenigen Sekunden in den Ruhezustand versetzt." 7 70
nics=($(ip link show | /bin/grep broadcast -i | grep "state UP" | awk -F ': ' '{ print $2 }' | sort -b -g)) for iface in "${nics[@]}"; do if [[ $iface =~ ^e ]]; then if [[ ! "$(readlink /sys/class/net/$iface)" =~ "devices/virtual" ]]; then echo "$iface" >/var/run/mountctl.ifce break fi fi done
/bin/systemctl stop mountctl@$CurrUser /bin/systemctl stop serverctl /bin/systemctl suspend -i >/dev/null 2>&1 ;; #------------------------------------------------------------------------------------------------------------------------------------------- resume) if [ -s /var/run/mountctl.ifce ]; then iface=$(cat /var/run/mountctl.ifce) # get active interface before suspend
ip4="" for ((n=0;n<30;n++)); do # up to 30 seconds wait until the network is resumed ip4=$(ip -4 -o addr show $iface | grep -v "deprecated" | grep "scope global" -m 1 | cut -d\ -f 7 | cut -d/ -f 1) [ -n "$ip4" ] && break || sleep 1 done
if [ -z "ip4" ]; then # network is dead? /bin/echo "Network failed, restarting" | systemd-cat -t "thlu:$(basename $0)" -p "info" /sbin/ip link set dev "$iface" down /bin/sleep 3 /sbin/ip link set dev "$iface" up
/bin/systemctl is-active systemd-networkd && /bin/systemctl restart systemd-networkd /bin/systemctl is-active networking && /bin/systemctl restart networking /bin/systemctl is-active ifup@$iface && /bin/systemctl restart ifup@$iface fi fi
/bin/systemctl start serverctl || ErrExit 4 /bin/systemctl start mountctl@$CurrUser || ErrExit 4 /bin/echo "Network successfully resumed" | systemd-cat -t "thlu:$(basename $0)" -p "info" ;; #------------------------------------------------------------------------------------------------------------------------------------------- *) ErrExit 3 ;; esac
/bin/echo "Successful terminated with exitcode=$rc" | systemd-cat -t "thlu:$(basename $0)" -p "info" exit $rc #=========================================================================================================================================== #EOF |
Dem Anwender vor dem Bildschirm wird bei den Aktionen poweroff, suspend und reboot eine Nachricht auf dem Bildschirm angezeigt, so das er erkennen kann, dass der von ihm angestoßene Prozess auch wirklich angelaufen ist. Das ist sinnvoll, weil ja eben auch das Schließen der Netzwerk-Freigaben einige Momente dauern kann. Eine typische Meldung sieht etwa so wie das nachfolgende Beispiel aus:
Damit die Meldung aber überhaupt angezeigt werden kann, ist das Programm dialog notwendig, welches mit den zwei folgenden Befehlen auf "Bereits vorhanden" oder "noch zu installieren" geprüft werden kann: Prüfen: # dpkg -l dialog dpkg-query: Kein Paket gefunden, das auf dialog passt Wenn das Programm fehlt, ist es zu installieren: # apt install dialog |
Damit das Script mountctl auch manuell durch den Anwender mit den notwendigen erweiterten Rechten gestartet werden darf, ist eine erlaubende Regel für das Policykit erforderlich. Es ist hier die gleiche Datei, wie sie schon oben bei dem kleinen Workaround verwendet wurde.
/usr/share/polkit-1/actions/LocalExtPermissions.policy | Rechte-Einstellung: root:root 644 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig> <action id="LocalExtPerms.mountctl"> <defaults> <allow_any>yes</allow_any> <allow_inactive>yes</allow_inactive> <allow_active>yes</allow_active> </defaults> <annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/mountctl</annotate> </action> </policyconfig> |
4.5 Interaktive Aufrufe von mountctl durch den Anwender
An welchen Stellen verwendet der Anwender denn überhaupt einen manuellen Start des Programms mountctl, dass er dafür eine extra Berechtigung benötigt? Tja, soweit er das über das Menüsystem oder über einen Desktopstarter tut, wird es dem Anwender noch nicht mal bewusst sein, dass er hier einen manuellen Start initiiert, aber es ist dennoch so. In den meisten Fällen wird er ohne lange über Berechtigungen nachzudenken mit einem Mouse-Click aus seinem Desktop-Oberfläche einfach eine dieser besonderen Aktionen starten, die dann aber eigentlich nur mit seinen geringeren Berechtigungen ausgeführt werden würde. Bei den üblichen Mountprozessen oder bei einem Eingriff in das System funktioniert das aber nicht, weil dafür immer root-Rechte notwendig sind. Darüber hinaus gibt es natürlich noch den echten manuellen Start im wahrsten Sinne des Wortes, wenn der Anwender im Terminal einen entsprechenden Befehl absetzt. Ich betrachte hier alle drei Aspekte: Also vom Anwender ausgehende Aktionen z.B. über das Start-Menü oder via Desktop-Starter oder durch Tastatur-Eingabe im Terminal.
Auf unseren Rechnern sind jeweils drei Desktop-Starter für eine komfortable Handhabung durch die User eingerichtet. Ich hatte einen dieser 3 Desktop-Starter schon weiter oben einmal erwähnt. Und hier sind es nun 3 und sie sind erneut ein wichtiges Thema.
/home/thomas/Schreibtisch/mountctl_poweroff_helper.desktop /home/thomas/Schreibtisch/mountctl_reboot_helper.desktop /home/thomas/Schreibtisch/mountctl_suspend_helper.desktop |
An dieser Stelle muss man klar feststellen, dass die Laufzeit-Stabilität des gesamten hier beschriebenen Prozesses vom Start des Rechners bis zum Runterfahren nur gewährleistet werden kann, wenn man sich lückenlos an einige wenige Regeln hält und nicht selber die Stabilität ansägt oder gar durchtrennt. Gerade bei diesen 3 Aktionen ist es also wichtig, mountctl nicht außen vor zu lassen. Dabei macht mountctl wirklich nichts geheimnisvolles, sondern es verwendet sogar ausschließlich die genau dafür gedachten systemd-Aufrufe, wie:
/bin/systemctl poweroff
/bin/systemctl reboot
/bin/systemctl suspend
Die einzige Magie ist, dass 'meine' aktuell geöffneten Remote-Mounts ordentlich beendet und geschlossen werden, bevor einer dieser System-Aufrufe erfolgt. Deshalb ist es auch so wichtig, gerade bei solchen bedeutsamen Aktionen wie Poweroff und Reboot dafür dafür zu sorgen, dass möglicherweise offene Mounts diesen neuen Vorgang nicht empfindlich stören, z.B. durch die schon oben genannten Stop-Jobs. Der Desktopstarter für Poweroff führt als Beispiel genannt also nur den folgenden Befehl aus:
pkexec /usr/local/bin/mountctl poweroff
Ich zeige stellvertretend für die 3 Starter hier nur einen, im Tarfile sind aber alle drei enthalten.
~/Schreibtisch/mountctl_poweroff_helper.desktop | Rechte-Einstellung: $user:$user 644 |
[Desktop Entry]
Type=Application
Exec=/usr/bin/pkexec /usr/local/bin/mountctl poweroff
Icon=/usr/local/share/Icons/poweroff.png
Terminal=true
Categories=Utility;
Name=Mountctl-Helper
Comment=Helper for Umount remote-shares before lan is disconnect
Name[de_DE]=Poweroff
Auf all unseren Systemen wurde einmalig das Start-Menü angepasst, so dass bei Auswahl von Poweroff, Reboot oder Suspend automatisch immer mountctl verwendet wird. Das ist nicht aufwendig, da auf allen Rechner der gleiche Custom-Desktop installiert ist. Allerdings hat die Praxis mittlerweile gezeigt, dass das hier keiner wirklich verwendet. Viel beliebter sind die 3 Icons, deren Icons auf dem Desktop links unten über dem Startmenü platziert sind.
Das Menüfile einer Desktop-Umgebung enthält dazu diese 3 Einträge... und wie man sieht, wiederholen sich die gleichen Befehle immer wieder:
item {
image=/usr/local/share/Icons/reboot.png
action=/usr/bin/lxterminal -e /usr/bin/pkexec /usr/local/bin/mountctl reboot
name=Neu starten
}
item {
image=/usr/local/share/Icons/suspend.png
action=/usr/bin/lxterminal -e /usr/bin/pkexec /usr/local/bin/mountctl suspend
name=Ruhezustand
}
item {
image=/usr/local/share/Icons/poweroff.png
action=/usr/bin/lxterminal -e /usr/bin/pkexec /usr/local/bin/mountctl poweroff
name=Herunterfahren
}
Als letzte und hier beachtete weitere Möglichkeit sind die manuellen Eingaben im Terminal zu nennen. Und auch hier greift wieder das Motto, wenn man sie nicht an die Regeln hält, wird irgend etwas anders ablaufen, als man erwartet. Ich habe dafür in der ~/.bash_aliases der User, die das betrifft, die folgenden Alias' eingetragen:
alias mountctl="/usr/bin/pkexec /usr/local/bin/mountctl"
alias poweroff="/usr/bin/pkexec /usr/local/bin/mountctl poweroff"
alias suspend="/usr/bin/pkexec /usr/local/bin/mountctl suspend"
alias reboot="/usr/bin/pkexec /usr/local/bin/mountctl reboot"
Anstatt der regulären Befehle, gibt der Anwender im Fall der Fälle also einfach nur die Kurzform ein, wie z.B.: poweroff, oder suspend, oder reboot. Mein begleitender Hinweis war "Wenn Du das machen möchtest, sind es die gleichen Befehle, wie sie unter dem Icon auf dem Desktop stehen, nur alles klein geschrieben". Die Alias-Statements sorgen dann dafür, dass anstatt dieses Befehls mountctl aufgerufen wird, was zunächst die Mounts ordentlich trennt und dann den entsprechenden Befehl ausführt.
Ja, ich weiss, man kann sich natürlich darüber hinwegsetzen... das ist natürlich niemals eine 100%-Lösung. Aber die Erfahrungen aus der gelebten täglichen Praxis haben gezeigt, dass es funktioniert. Mehr brauch ich nicht. Und wer sich hier vorsätzlich drüber hinwegsetzt hat schlimmstenfalls "aufgehängte" Mounts oder solche zuvor erwähnten Stop-Jobs. Tja, Pech gehabt.
4.6 Service-Unit zur Wiederherstellung der Mounts bei einem "Resume after Suspend"
Die folgende Service-Unit sorgt dafür, dass nach einem Rechner-Resume, also wenn wir den Rechner vorher mit suspend schlafen gelegt haben, alle vorherigen Mounts störungsfrei wiederhergestellt werden. Warum ist das notwendig? Deshalb, weil beim suspend auch das Netzwerk getrennt wird und aus Sicherheitsgründen deshalb vom Script mountctl vor dem suspend alle Mounts ordentlich getrennt werden. Es ist z.B. durchaus möglich, dass der DSL-Router während der Suspend-Zeit ein Reconnect (z.B. bei einer heute auch noch üblichen Zwangstrennung) durchführt und vom DSL-Provider einen neuen IPv6-Prefix erhält. Ebenso ist es denkbar, dass die bisher verwendete IPv4 während der Suspend-Zeit durch den DHCP-Server einem anderen Client vergeben wurde. Die daraus möglicherweise entstehenden Probleme versucht diese Unit mit dem Start von mountctl zu beheben. Selbst mit offenen und ungespeicherten Libre-Office-Dokumenten konnte ich bisher keinen Fehler feststellen. Wenn der Rechner wieder aufgeweckt wird, wird der kurze Moment abgewartet, bis das Netzwerk wieder verfügbar ist, um dann wieder alle Mounts neu einzurichten.
Allerdings halte ich es sowieso für eine gute Idee, aus Gründen der Datensicherheit vor dem suspend generell alle offenen und im Netzwerk abgelegten Dokumente zu speichern und zu schließen. Damit ist dann wirklich jede Gefahr von Datenverlust gebannt.
/etc/systemd/system/suspend-resume.service | Rechte-Einstellung: root:root 644 |
[Unit] Description=thlu:suspend-resume.service: resume network + mounts after suspend After=suspend.target
[Service] Type=oneshot ExecStart=/usr/local/bin/mountctl resume
[Install] WantedBy=suspend.target |
Die Service-Unit startet also auch nur das Programm mountctl, wo dann geprüft wird, ob die Netzwerkverbindung aktiv ist oder ob sie ggf. einmal neu gestartet werden muss. Mit erfolgreich wiederhergestellter Netzwerk-Verbindung werden dann auch wieder die Mounts hergestellt. Die Unit wird mit dem folgenden Befehl aktiviert:
# systemctl enable suspend-resume.service
Achtung: Bei dieser Service-Unit ist unbedingt daran zu denken, dass mountctl hier primär nur kabelgebundene Interfaces unterstützt. Sofern bei einem Resume die Neu-Verbindung fehlschlägt, versucht mountctl durch einen Restart des Netzwerkes und des Interfaces die Verbindung wiederzubeleben. Erst mit erfolgreich wieder-verbundenen Netzwerk werden auch die Mount -Points erneut verbunden. Allerdings funktioniert das Netzwerk bei WLAN-Verbindungen im Zusammenspiel mit wpa-supplicant, mit wechselnder SSID und jeweils individuellen Zugangs-Parametern völlig anders. Falls der systemische "Resume" die WLAN-Verbindung nicht automatisch erfolgreich herstellen kann, muss das manuell getan werden, z.B. mit selnic oder mit einem der üblichen Network-Manager. Gerade bei WLAN-Verbindungen ist es ja jedes Mal fraglich, ob der zuletzt verwendete AccessPoint nach dem Suspend und vielleicht einem zwischenzeitlichen Ortswechsel überhaupt noch erreichbar ist. | |
Mit anderen Worten: die Service-Unit wird durchaus auch mit einer WLAN-Verbindung funktionieren und nach einem Suspend-Resume die Mount-Points erneut verbinden, wenn wpa-supplicant sich erneut mit der SSID verbinden konnte. Wenn nicht, passiert einfach gar nichts und die Mounts sind nicht verbunden. |
|
Das wars... fertig, geschafft, am Ende angekommen...
All das zusammengenommen scheint auf den ersten oberflächlichen Blick wirklich eine übermäßig komplizierte Handhabung zu sein. Aber genau das Gegenteil ist hier der Fall. Ich betreue mehrere Systeme und eine von mir festgelegte Grundvoraussetzung ist, dass mir das keine bzw. nicht viel Arbeit machen soll. Wenn ich heute ein System neu einrichten muss, so greife ich nach dem Installer einfach auf eine Muster-Verzeichnis-Struktur (/etc und /home) auf dem Server zurück, welches sich mit seinem Aufbau quasi analog an einer Debian-Installation orientiert und kopiere von dort, was ich brauche. Allerdings muss hier das Thema ergänzend noch ein weiterer wichtiger Aspekt in die Betrachtung hinzugenommen werden, und zwar die Backups von PC's und Daten. Eine Grundlage meiner Backup-Strategie ist seit jeher, keinesfalls die Rechner der Anwender selber zu sichern ... das ist mir viel zu viel sinnlose Arbeit ohne Erfolgsgarantie. Deswegen besteht hier auch die Direktive, keine lokalen Daten zu speichern... wer das tut, muss sich selber um seine Datensicherung kümmern. An dem Punkt bin ich kompromisslos. Aber wie ich an anderer Stelle weiter oben schon sagte, dazu sind alle zu faul, alle nutzen lieber aus eigenem Interesse den Server. Daraus ergibt sich aber die Erkenntnis, dass ein Rechner ohne lokale Daten faktisch nur das Resultat eines Installers und von im Laufe der Zeit durchgeführten Uprades ist... also vollständig jederzeit verlustfrei reproduzierbar. Also muss ich auch keine Zeit und Geld und Arbeit in etwas investieren, was eh keinen Sinn ergibt. Aber genau das ist dann auch eine der Rahmenbedingungen, wenn heute wirklich mal eine Neuinstallation notwendig ist. |
Und genau vor dem Hintergrund ist dann die Neuinstallation mit dem Debian-Installer plus der fehlenden Pakete in weniger als 2-3 Stunden passiert. Für die letztliche Personalisierung des neuen Rechners kopiere ich einfach nur aus diesem genannten Muster-Verzeichnis alle benötigten Dateien an ihren Zielort, was selten länger als 5 Minuten benötigt und das war es mit der Einrichtung der Remote-Mounts. Es sind ja immer die gleichen zentralen Ressourcen, auf die von allen Systemen zugegriffen wird, unterschiedlich sind nur die lokalen Berechtigungen, die ich aber jetzt ganz einfach und komfortabel über eine lokale Linux-Gruppe auf dem Client einrichten kann. Das restliche Berechtigungsmanagement übernimmt an zentraler Stelle der Samba-Server, der das für jeden User (resp. Gruppe) somit quasi individuell handhaben kann.
Um nun noch mal auf die Eingangsfrage zurückzukommen "Muss das echt so kompliziert sein, geht das wirklich nicht einfacher?".... tja, keine Ahnung, ich weiß es nicht. Und ich bin dankbar für jede andere Lösung, die einfacher ist und trotzdem die oben beschriebenen Anforderungen erfüllt. Bis zur aktuellen Version 1.0 von mountctl hat es fast 3 Jahre gedauert. Es hat vorher 28 Versionen gegeben, die sich immer weiter entwickelt haben, aber immer irgendwie doch unvollkommen waren, manche wurde sogar ganz verworfen. Nebenbei hab ich haufenweise andere ebenfalls immer erfolglose Versuche gestartet. Die aktuelle Version 2.1 hat quasi 5 Jahren gebraucht und ist schließlich durch die fortwährende Suche nach einer guten und praktikablen Lösung entstanden. Je mehr man sich mit systemd befasst, umso mehr erkennt man die Möglichkeiten. Das aktuelle mountctl ist eine vollständig auf systemd setzende Lösung. Solange also meine aktuelle Lösung hier so erfolgreich funktioniert und solange es keine wirklich gute Alternative gibt, werde ich wohl dabei bleiben.
Haftungsausschluss
Ich erhebe keinen Anspruch darauf, dass die Bash-Script-Programme mountctrl, serverctl und sessionctl vollständig fehlerfrei programmiert sind und ich behaupte das auch nicht. Sowohl durch Programmierfehler in den Programmen ist Datenverlust möglich, wie auch durch den Anwender selber verursacht durch unsachgemäße Implementierung, unsachgemäße Änderungen, fehlerhafte Einstellungen oder falsche Parameterübergaben bei den Programmen. Deshalb schließe ich jede Haftung für Schäden an Software oder Hardware oder Vermögensschäden oder für Datenverlust aus, die durch die Benutzung der Programme mountctrl, serverctl und sessionctl sowie der als Beispiele aufgeführten systemd-Service-Units enstehen. Die Benutzung der Programme erfolgt auf eigenes Risiko.
Datum Änderung
27.06.2019 | 4.1 und 4.2: Mount-Optionen eingefügt: vers=3.0,uid=%I,gid=%I,dir_mode=0770,file_mode=0644 vers=3.0: Erzwingt aus Sicherheitserwägungen das ‚SMBv3.0 Protocol‘ uid=%I,gid=%I: Setzen von lokal sichtbaren Eigentümer-Rechten, die auch lokal angewandt werden. Selbst wenn der Samba-Server weitergehende Rechte vorsieht, gelten lokal diese Rechte -sofern so deklariert- ggf. als Beschränkung. Eine Erweiterung der Rechte über die Rechte des Samba-Servers hinaus ist jedoch nicht möglich. dir_mode=0770,file_mode=0644: wie zuvor, hier jedoch bezogen auf Lesen/Änderungs-Rechte. |
| Mount-Option nobrl entfernt. Mit ‚nobrl‘ wird bei Verwendung mount.cifs nur die Simulation einer Sperrung durchgeführt, was sich bei Journaling-Aktionen (z.B. Datenbank-Backends) destruktiv erweisen kann. Der Parameter kann evtl. trotzdem notwendig sein, wenn z.B. eine sqlite-Datenbank auf einem Samba-Share hinterlegt ist und weil Samba Datensatz-Sperr-Flags vom Client-PC nicht durchreicht. In dem Fall reagiert die sqlite-Engine zum Schutz der Daten mit einem „Database locked“. Um trotzdem mit dieser DB arbeiten zu können, kann in solchen Ausnahmefällen ‚nobrl‘ als Mount-Option verwendet werden. |
| 4.3: Weitere Erläuterungen zu PAM-->common-session und mountctl@.service eingefügt |
|
|