Verbindungs-Hooks auf einem VPN-Daemon laufen als der Daemon, und das bedeutet meistens, dass sie als root laufen. In dem Moment, in dem einer dieser Hooks in das Home-Verzeichnis von jemandem schreibt oder einen Desktop-Helfer neu startet, haben Sie einem Netzwerkereignis die Fähigkeit gegeben, als beliebiger lokaler Benutzer zu handeln. Die übliche Abkürzung, einen Benutzernamen aus einer Umgebungsvariablen zu lesen und weiterzugeben, behebt das nicht. Sie benennt die Aktion um, ohne zu ändern, welches Konto sie ausführt. Was funktioniert, ist eine Übergabe in drei Teilen, bei der der privilegierte Schritt sagt, was passiert ist, und eine separate Unit die Handlung unter der richtigen Identität ausführt.

Ein Benutzername in der Umgebung ist kein Identitätsschalter

Unix entscheidet anhand der effektiven Benutzer-ID, worauf ein Prozess zugreifen darf, nicht anhand der Zeichenketten in seiner Umgebung. USER=alice zu setzen oder LOGNAME zu exportieren ändert, was nachlässige Software in ihrer Ausgabe druckt; an Dateibesitzprüfungen ändert es nichts. Ein als root laufender Hook mit gesetzter Variable öffnet Alices Dateien weiterhin als root.

Die Namen sind auch nicht derselbe Name. Der Fernzugriffs-Login, das lokale Unix-Konto und der Common Name des Zertifikats sind drei Bezeichner, die oft übereinstimmen und gelegentlich nicht. Ein Benutzer, der sich als a.smith authentifiziert, hat möglicherweise gar kein lokales Konto oder wird über einen Verzeichnisdienst auf asmith abgebildet. Jedes Design, das annimmt, eine Zeichenkette diene allen drei Rollen, bricht beim ersten Auftragnehmer.

Die erste Frage ist also nicht „welcher Benutzer ist verbunden“, sondern „welches lokale Konto darf diese Aktion ausführen“. Beantworten Sie das aus Ihrer eigenen Kontodatenbank, nicht aus dem, was der Client gesendet hat:

id -u alice
getent passwd alice | cut -d: -f1,3,6
stat -c '%U:%G %a %n' /home/alice/.local/state/vpn-status.json

Der dritte Befehl ist der, den Leute überspringen. Wenn die Zieldatei root gehört, weil der vorherige Hook sie erstellt hat, scheitert eine korrekt konfigurierte Benutzeraktion mit einem Berechtigungsfehler, der wie ein systemd-Problem aussieht und keines ist.

Der Daemon bleibt, wo er ist

Das eigene User= des VPN-Dienstes auf Alice zu ändern ist der falsche Hebel. Es verändert, wie der Daemon Schnittstellen bindet, Schlüssel liest, seine Statusdatei schreibt und Routen verwaltet, und es verschiebt all dieses Risiko in eine Unit, die funktionierte. Der Daemon braucht seine Privilegien; die Folgeaktion nicht.

Sehen Sie nach, was die Unit tatsächlich tut, bevor Sie sie anfassen. Zwei Befehle nennen Ihnen die Ausführungsidentität und die tatsächlich geltenden Kommandozeilen, einschließlich allem, was ein Override hinzugefügt hat:

systemctl show -p User -p Group -p ExecStart openvpn-server@server
systemctl cat openvpn-server@server

Bei einer Standard-Paketinstallation ist der Daemon root, und das --up-Skript erbt das. Bestätigen Sie es, statt es anzunehmen, denn eine gehärtete Distribution könnte es auf ein dediziertes Konto verschoben haben, und ein handgeschriebenes Drop-in könnte den Hook über einen Wrapper mit anderen Regeln starten.

Drei benannte Teile, ein enger Auslöser

Die Anordnung, die Wartung übersteht, hat ein Ereignis, einen Auslöser und eine Aktion. Der Daemon meldet, dass eine benannte Sitzung hochgekommen ist. Ein Auslöser nimmt diese kleine Tatsache an und verlangt eine feste Aktion. Die Aktion läuft unter dem lokalen Konto, dem die Dateien gehören, die sie berührt. Nichts in der Kette akzeptiert einen beliebigen Befehlspfad.

Die Aktion selbst ist eine gewöhnliche Service-Unit, und die Identität ist eine einzige Direktive darin:

[Service]
Type=oneshot
User=alice
Group=alice
ExecStart=/usr/local/bin/refresh-vpn-status --slot %i

Beachten Sie, dass User=alice für die gesamte Unit gilt, und systemd dokumentiert eine Ausnahme, die man kennen sollte: ein Befehl mit dem Präfix + läuft unabhängig davon mit vollen Privilegien. Dieses Präfix ist ein legitimes Werkzeug für einen einzelnen privilegierten Schritt und ein bequemer Weg, die gerade gebaute Trennung rückgängig zu machen, also behandeln Sie jedes + in dieser Unit als etwas, das im Review begründet werden muss.

Halten Sie die Eingabe des Auslösers minimal und nicht geheim. Die Sitzungskennung, das abgebildete Konto und ein Zeitstempel sind normalerweise alles, was nötig ist. Geben Sie niemals ein Shell-Fragment, einen ausführbaren Pfad oder einen vom Client gelieferten Umgebungsblock über die Grenze, denn die empfangende Seite kann einen legitimen Wert nicht von einem eingeschleusten unterscheiden.

Prüfen Sie die Identität, die tatsächlich lief

Konfigurationsprüfung ist kein Beweis. Bestätigen Sie nach einer genehmigten Testverbindung die UID, die die Datei berührt hat, und die UID, die der Daemon noch verwendet:

stat -c '%U %G %y %n' /home/alice/.local/state/vpn-status.json
ps -o user=,pid=,cmd= -p "$(pgrep -o -f 'openvpn --daemon')"
systemd-run --uid=alice --pipe /usr/local/bin/refresh-vpn-status --dry-run

Die erste Zeile sollte Alice und einen Zeitstempel innerhalb Ihres Testfensters zeigen. Die zweite sollte weiterhin root oder das Paketdienstkonto zeigen, was beweist, dass der Daemon sich nicht bewegt hat. Die dritte ist der billigste Weg, die Berechtigungen der Aktion zu testen, ohne auf eine echte Wiederverbindung zu warten.

Wenn der Eigentümer der Datei Alice ist, aber ihre Gruppe oder ihr Modus sich zwischen Läufen geändert hat, suchen Sie nach einer UMask= oder einem Unterschied bei den Verzeichnisrechten statt nach einem Logikfehler. Wenn der Zeitstempel sich überhaupt nicht bewegt hat, wurde der Auslöser nie ausgelöst, und der nützliche nächste Schritt ist journalctl -u für die Aktions-Unit statt einer weiteren Client-Wiederverbindung.

Was ein Verbindungsereignis weiterhin nicht beweist

Ein aufgebauter Tunnel ist eine Tatsache. Er bedeutet nicht, dass die Desktop-Sitzung entsperrt ist, der Sitzungsbus erreichbar ist oder die private Ressource, die die Aktion will, schon verfügbar ist. Eine Oneshot-Unit unter Alices UID hat ihre Identität und nichts von ihrer Sitzung, also muss alles, was einen grafischen Bus braucht, über den unterstützten Benutzersitzungsmechanismus laufen und definieren, was passiert, wenn sie abgemeldet ist.

Wiederverbindungen wiederholen sich auch. Ein Laptop, der im Zug schläft, kann ein Dutzend Verbindungsereignisse in einer Stunde erzeugen, und ein Hook, der jedes Mal eine Zeile anhängt, macht aus einer Statusdatei ein Log, das niemand lesen kann. Machen Sie die Aktion idempotent oder lassen Sie sie einen einzelnen, nach Sitzung geschlüsselten Datensatz ersetzen.

Begrenzen Sie schließlich den Schadensradius in der Unit selbst: eine schreibgeschützte Dateisystemansicht außerhalb des Statusverzeichnisses, keine neuen Privilegien und ein ausdrückliches Timeout. Die Trennung zwischen Daemon und Benutzeraktion ist für sich genommen wertvoll, aber sie ist viel mehr wert, wenn die Aktion nichts anderes tun kann als das eine, was Sie genehmigt haben.

Ähnliche Artikel