VPN-Dienst scheitert bei EXEC: Unit, Binärdatei und sudoers vergleichen
Ein Fehler bei Schritt EXEC bedeutet, dass systemd die Binärdatei nicht starten konnte. Vergleichen Sie den Unit-Pfad, den installierten Pfad und die sudoers-Freigabe, bevor Sie etwas ändern.
Gestern lief der Tunnel, und heute Morgen ist die Unit tot, mit Failed at step EXEC spawning /usr/local/bin/vpnclient im Journal. Zugangsdaten, Routen und das Konto kommen in diesem Satz nie vor. systemd hat ExecStart zu einem Pfad aufgelöst, den Kernel gebeten, ihn auszuführen, und der Kernel hat es abgelehnt, meistens weil an diesem Pfad nichts existiert oder die Datei nicht ausführbar ist. Behandeln Sie den Fehler als Datei-und-argv-Problem, bis der geparste Befehl das Gegenteil beweist.
Warum EXEC scheitert, bevor die Authentifizierung beginnt
systemd führt ExecStart nicht über eine Shell aus. Es forkt, baut aus der Zeile einen Argumentvektor und ruft execve auf das erste Element auf. Scheitert dieser Aufruf, erreicht der Dienst nie den eigenen Code des VPN-Clients, sodass weder Profilbearbeitung noch Prüfung der Zugangsdaten hilft.
Der Statuscode trennt diese Familie von gewöhnlichen Abstürzen. Eine Unit, die mit status=203/EXEC endet, hat nie einen Prozess gestartet; eine Unit, die startet und dann mit status=1 endet, ist möglicherweise an Authentifizierung, DNS oder Routing gescheitert. Lesen Sie den Code vor dem Client-Log, denn im ersten Fall bleibt das Client-Log leer.
Der Pfad im Journal ist der, den systemd verwendet hat, nicht unbedingt der, den Sie bearbeitet haben. Drop-ins und Generatorausgabe können die Datei des Herstellers überschreiben, und ein veralteter daemon-reload lässt die alte Zeile im Speicher. Deshalb besteht die erste Diagnose darin, systemd zu fragen, was es für den Befehl hält, statt die Datei zu öffnen, die Sie geändert zu haben glauben.
Drei Stellen, an denen der Pfad der Binärdatei steht
Ein VPN-Client, der aus einem Distributionspaket, einem Herstellerskript und einem Release-Tarball installiert wurde, kann in drei verschiedenen Präfixen landen. Unit, Privilegienregel und Installer haben jeweils eines dieser Präfixe festgehalten, und der Fehler tritt auf, wenn zwei der drei nicht übereinstimmen.
| Record | Common value | How to read it |
|---|---|---|
| Unit or drop-in | /usr/local/bin/vpnclient | systemctl cat vpnclient.service |
| Installed binary | /usr/bin/vpnclient | command -v vpnclient, then ls -l the result |
| Sudoers grant | /opt/vpnclient/bin/vpnclient | sudo grep -r vpnclient /etc/sudoers.d/ |
Eine konkrete Abweichung sieht so aus: Das Herstellerskript installiert nach /usr/local/bin, der Tarball wurde unter /opt entpackt, und die Unit nennt weiterhin den alten Pfad /usr/bin aus einem früheren Paket. Nur eine dieser Dateien existiert, und es ist selten die in ExecStart.
Entscheiden Sie, welcher Pfad kanonisch ist, bevor Sie eine der Dateien ändern. Gehört die Binärdatei dem Distributionspaket, ist der Paketpfad kanonisch, und die Unit sollte ihm folgen. Gehört sie dem Herstellerskript, gilt dieselbe Regel für das Herstellerpräfix. Eine Binärdatei zwischen Präfixen zu kopieren, erzeugt zwei Kopien, die beim nächsten Upgrade auseinanderlaufen.
Wenn ein Befehl in Anführungszeichen zu einem Dateinamen wird
Ein zweiter Fehlermodus versteckt sich hinter derselben Journalzeile. Jemand schreibt dies in die Unit und erwartet, dass der Client beim Booten verbindet:
ExecStart=/usr/bin/"vpnclient connect office"systemd zerlegt den Wert in Wörter, sodass das erste Wort /usr/bin/vpnclient connect office wird, wobei die Anführungszeichen als Gruppierungssyntax entfernt werden. Dann sucht der Kernel eine Datei, deren Name Leerzeichen enthält, und die gibt es nicht. Der Fehler ist identisch mit einer fehlenden Binärdatei, obwohl die Binärdatei installiert ist.
Die unterstützte Form ist eine echte ausführbare Datei, gefolgt von getrennten Argumenten, zum Beispiel ExecStart=/usr/bin/vpnclient connect office. Braucht der Arbeitsablauf wirklich eine Pipeline oder eine Kette, legen Sie sie in ein Skript und rufen das Skript auf, denn ExecStart hat kein &&, keine Pipes und keine Glob-Erweiterung.
Reparieren Sie die Unit, nicht das Dateisystem
Die verlockende Reparatur ist ein symbolischer Link vom erwarteten Pfad auf die echte Binärdatei. Er funktioniert, bis das Paket aktualisiert wird und der Link auf eine entfernte Datei zeigt, oder bis ein SELinux-Label nicht mehr zum Ziel passt. Bearbeiten Sie die Unit lieber über ein Drop-in, damit die Datei des Herstellers unangetastet bleibt und Upgrades vorhersehbar bleiben.
Die Privilegienregel muss zu demselben Pfad und denselben Unterbefehlen passen. Ein sudoers-Eintrag für /usr/local/bin/vpnclient autorisiert nicht /usr/bin/vpnclient, und ein Eintrag, der jedes Argument erlaubt, lässt jeden lokalen Prozess den Tunnel umkonfigurieren oder stoppen. Gewähren Sie die beiden Verben, die die Unit braucht, und halten Sie den Abbau draußen, wenn das Stoppen des Dienstes die Schnittstelle ohnehin schließt.
Hier zahlt sich der Vergleich aus: Unit, Paketdatenbank und sudoers sollten alle einen Pfad nennen. Tun sie das, verschwindet der EXEC-Fehler ohne Neustart und ohne eine Kopie der Binärdatei an dritter Stelle.
Beweisen Sie den geparsten Befehl vor dem Neustart
sudo systemctl daemon-reload
sudo systemctl cat vpnclient.service
systemctl show -p ExecStart vpnclient.service
systemd-analyze verify /etc/systemd/system/vpnclient.service
sudo systemctl restart vpnclient.service
systemctl status vpnclient.service --no-pagersystemctl show ist die entscheidende Ausgabe, weil sie die argv ausgibt, die systemd tatsächlich geparst hat, einschließlich jedes Drop-ins. systemd-analyze verify fängt einen fehlenden oder nicht ausführbaren Pfad ab, bevor Sie neu starten, und der Statusbefehl nach dem Neustart sagt Ihnen, ob der Prozess nun existiert.
Startet die Unit und scheitert danach, haben Sie die EXEC-Familie verlassen und die eigene Fehlerbehandlung des Clients betreten. Suchen Sie im Journal die Meldung des Clients, nicht einen weiteren execve-Fehler. Bewahren Sie den Unit-Diff und den sudoers-Diff gemeinsam im Änderungsdatensatz auf, damit das nächste Upgrade den geteilten Pfad nicht stillschweigend wieder einführt.