Der Hook läuft, wenn Sie ihn von Hand als root ausführen, und liefert Operation not permitted in dem Moment, in dem der Dienst ihn startet. Die Datei hat sich zwischen diesen Läufen nicht geändert; die Umgebung hat es. systemd gibt dem Prozess eine kleinere Welt: weniger Capabilities, ein anderes /dev, schreibgeschützte Dateisystempfade und eine Obergrenze für Tasks. Lesen Sie den Fehler als eine Grenze, die eine bestimmte Operation abgelehnt hat, nicht als ein Skript, das kaputtgegangen ist.

Beginnen Sie bei der Operation, die abgelehnt wurde

Eine Logzeile, die sagt, die Unit sei fehlgeschlagen, verrät Ihnen fast nichts. Die nützliche Zeile benennt den Aufruf, der abgelehnt wurde: einen Geräteknoten, der nicht geöffnet werden konnte, ein Verzeichnis, in das nicht geschrieben werden konnte, einen Namespace, der nicht erzeugt werden konnte. Finden Sie diesen Aufruf, bevor Sie eine einzige Direktive anfassen, denn jede entspricht einer anderen Einschränkung. Lesen Sie dann die Unit, die tatsächlich lief, samt Drop-ins, statt die Datei, die das Paket ausgeliefert hat.

Die Abfrage unten gibt den Fragmentpfad, die Drop-in-Verzeichnisse und die Eigenschaften aus, die bei einer Geräte- oder Dateisystem-Ablehnung zählen. Lesen Sie alle, bevor Sie entscheiden, welche Grenze relevant ist:

systemctl show "[email protected]" -p FragmentPath -p DropInPaths -p PrivateDevices -p DevicePolicy -p DeviceAllow -p ProtectHome -p ProtectSystem -p TasksMax
journalctl -u "[email protected]" -b --no-pager | grep -iE 'operation not permitted|permission denied|cannot open|failed'

Nichts in diesem Block schreibt. Fehlt der abgelehnte Aufruf im Journal, erhöhen Sie für einen Lauf die Ausführlichkeit des Clients, statt Direktiven zu raten. Ein laufender Prozess beweist, dass die Unit gestartet ist, nicht dass der Hook mit den nötigen Rechten lief.

Ein Namespace braucht Mount-Rechte, die ein TUN-Gerät nie verwendet

Zwei Operationen, die innerhalb eines Hooks gleich aussehen, haben unterschiedliche Berechtigungsprofile. Das Öffnen von /dev/net/tun und das Anlegen des Interfaces braucht Netzwerkverwaltung plus Zugriff auf diesen Knoten. Einen benannten Netzwerk-Namespace anzulegen braucht ebenfalls Mount-Arbeit, weil das Namespace-Werkzeug eine frische /sys Ansicht einhängt und ein Statusverzeichnis an seinen Platz bindet. Client-Unit aus dem Upstream lässt die Mount-Verwaltung bewusst außerhalb ihres Bounding Sets, deshalb wird der Namespace-Schritt abgelehnt, während der Tunnel selbst funktioniert hätte.

Deshalb rettet das Gewähren des Geräteknotens das Skript nicht. Eine Geräteerlaubnis beantwortet eine Frage, ob dieser Prozess diesen Knoten öffnen darf, und die Namespace-Frage ist eine andere. Ordnen Sie den fehlschlagenden Aufruf seiner Capability zu, bevor Sie irgendetwas erweitern.

Ein Test in Ihrer Shell erbt jede Capability, die ein unbeschränkter root hat, und die Unit erbt, was ihr Bounding Set behalten hat. Die beiden Läufe sind verschiedene Experimente , selbst wenn die Binärdatei identisch ist.

Geräte, geschützte Pfade und Task-Obergrenzen

Ist die abgelehnte Operation bekannt, erklären drei Gruppen von Direktiven die meisten Fehler. Gerätezugriff regelt PrivateDevices, was /dev durch ein minimales Set an Pseudo-Geräten ersetzt, und DevicePolicy mit DeviceAllow, die entscheiden, was die cgroup zulässt. /dev/net/tun ist nicht Teil dieses minimalen Sets, also kann eine Unit mit PrivateDevices=yes ihn nicht öffnen, was die Geräterichtlinie auch sagt.

Dateisystemschutz beantwortet eine zweite Frage. ProtectHome=yes macht /home, /root und /run/user unzugänglich, und ProtectSystem=full macht /usr, /boot und /etc schreibgeschützt. Ein Hook, der neben seiner eigenen Konfigurationsdatei schreibt, funktioniert deshalb in Ihrer Shell und scheitert unter der Unit, mit derselben Berechtigungsform wie eine fehlende Capability. Verschieben Sie die schreibbare Eingabe auf einen Pfad, den die Richtlinie erlaubt, oder fügen Sie einen schmalen ReadWritePaths= Eintrag hinzu.

Ressourcengrenzen sind die dritte Gruppe und die am wenigsten offensichtliche. TasksMax= begrenzt, wie viele Tasks die Unit anlegen darf, und eine Obergrenze für Dateideskriptoren verhält sich für Sockets und Handles gleich. Ein Deployment, das über die Grenze hinausgewachsen ist, scheitert mit einem Ressourcenfehler statt einem Berechtigungsfehler, und deshalb zählt die genaue Meldung weiterhin.

Ziehen Sie den Namespace-Schritt aus dem Hook heraus

Vorbereitung, die ein Privileg braucht, das der Client nicht halten darf, gehört in eine eigene Unit. Ein Oneshot, der vor dem Client läuft, sein Ergebnis behält und vor der Verbindung eingeordnet ist, gibt dem Namespace-Schritt, was er braucht, ohne diese Rechte an den Tunnelprozess zu übergeben. Der Client verweist dann auf diese Unit, statt das Skript aus seinem Up-Hook aufzurufen.

Die Vorlage unten ist eine Ausgangsform, nichts, was man ungelesen anwendet. Sie hält die Vorbereitung dort, wo das Privileg lebt, und lässt die Tunnel-Unit unberührt:

# /etc/systemd/system/[email protected]
[Unit]
Description=Namespace preparation for %i
Before=openvpn-client@%i.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/netns-up.sh %i
CapabilityBoundingSet=CAP_NET_ADMIN CAP_SYS_ADMIN
[Install]
WantedBy=multi-user.target

Fügen Sie die Reihenfolge mit einem Drop-in hinzu, statt die paketierte Unit zu bearbeiten, damit ein Upgrade Ihre Änderung nicht stillschweigend fallenlässt. Das Verzeichnis ist am Instanznamen festgemacht, also braucht ein zweites Profil seine eigene Datei:

sudo mkdir -p /etc/systemd/system/[email protected]
sudo tee /etc/systemd/system/[email protected]/10-namespace.conf >/dev/null <<'CONF'
[Unit]
[email protected]
[email protected]
CONF
sudo systemctl daemon-reload

Wiederholen Sie denselben Aufruf nach der Änderung

Eine Reparatur ist erst bestätigt, wenn derselbe Aufruf ein anderes Ergebnis liefert. Starten Sie beide Units neu, führen Sie die fehlgeschlagene Operation aus und vergleichen Sie ihr Ergebnis mit der Logzeile, die Sie vor der Änderung gesichert haben. Eine Unit, die startet, während der Hook nie wieder läuft, ist keine Reparatur.

Die Sequenz unten hält den Vergleich ehrlich, weil sie den abgelehnten Aufruf prüft und nicht die Farbe der Unit. Speichern Sie ihre Ausgabe neben der Logzeile, die Sie vor der Änderung festgehalten haben:

sudo systemctl restart "[email protected]" "[email protected]"
systemctl status "[email protected]" --no-pager
journalctl -u "[email protected]" -b --no-pager | grep -c 'Operation not permitted'
ip -brief addr show tun0

Zählen Sie die Ablehnungen, statt die letzte Zeile zu lesen, denn ein Hook kann scheitern, es erneut versuchen und Erfolg haben, und nur die Zahl zeigt, ob sich die Grenze bewegt hat. Hinterlassen Sie im Drop-in einen Kommentar, der die gelockerte Direktive und den Grund nennt, da das nächste Upgrade Ihre Konfiguration mit der paketierten Voreinstellung vergleicht. Eine Unit, die erst startet, wenn jeder Schutz abgeschaltet ist, ist nicht repariert.

Ähnliche Artikel