Wenn ein client-connect-Skript mit einem Wert ungleich null endet, weist OpenVPN den Client ab, und Betreiber gehen meist davon aus, dass das passende client-disconnect-Skript aufräumt. Oft läuft es nie, weil die disconnect-Verarbeitung davon abhängt, was die connect-Phase abschließen konnte. Ablehnung und Rollback als einen Mechanismus zu behandeln, verwandelt eine fehlgeschlagene Anmeldung in eine verwaiste Firewall-Regel oder einen verwaisten Datensatz. Es sind zwei getrennte Pflichten, und nur eine davon ist automatisch. Lesen Sie den eigenen Exit-Pfad des Hooks, bevor Sie dem Server vorwerfen, Zustand zu hinterlassen.

Ein Exit ungleich null lehnt den Client ab; er macht Ihre Arbeit nicht rückgängig

Der Exit-Status von client-connect ist eine Zulassungsentscheidung. Null bedeutet zulassen, alles andere bedeutet, die Instanz abzubauen, bevor sie vollständig aufgebaut ist. Nichts in diesem Vertrag prüft, was das Skript bereits in der Außenwelt getan hat. Hat das Skript eine iptables-Regel hinzugefügt, eine Zeile geschrieben oder einen Zähler erhöht, überleben diese Änderungen die Ablehnung.

Dieselbe Asymmetrie gilt für Plugins, bei denen mehrere connect-Funktionen für eine Instanz laufen können. Ein späterer Fehler hebt einen früheren Erfolg nicht rückwirkend auf, und die frühere Funktion kann nicht erfahren, dass eine Schwesterfunktion gescheitert ist.

Ablehnung ist automatisch, Aufräumen nicht

Das OpenVPN-2.6-Handbuch bindet die client-disconnect-Verarbeitung an die zuvor erfolgreiche connect-Verarbeitung. Eine dokumentierte Ausnahme deckt kaskadierte Ketten ab: Wenn ein connect-Schritt bereits Erfolg zurückgegeben hat, laufen die passenden disconnect-Schritte beim Abbau der Instanz trotzdem, auch wenn ein späterer connect-Schritt fehlschlug. Diese Ausnahme ist eng und hängt davon ab, wie viele Callbacks konfiguriert sind; deshalb sagt ein Test mit einem Shell-Skript nichts über eine Kette aus.

Kartieren Sie die tatsächliche Konfiguration, bevor Sie darum herum entwerfen: welches Skript, welches Plugin und in welcher Reihenfolge sie laufen. Die folgende Tabelle listet auf, was eine halbfertige Einrichtung hinterlässt.

What the hook did before failingWhat the client seesWhat stays behind
Created a firewall entryA rejection, with no tunnelA rule matching an address nobody owns
Wrote a state rowA rejection, with no tunnelA record of a session that never started
Allocated a tunnel addressA rejection, with no tunnelAn address marked busy until something releases it

Jede Zeile endet für den Client gleich und für den Host anders. Der Client kann nicht erkennen, welcher Fall eingetreten ist, also muss der Hook zurückrollen, was er getan hat, bevor er den Fehlerstatus zurückgibt.

Kompensieren Sie in dem Skript, das fehlgeschlagen ist

Prüfen Sie alles, was möglich ist, bevor Sie gemeinsamen Zustand anfassen: Zertifikatsfelder, Adressverfügbarkeit, die Gruppe, zu der der Client gehört. Die billigste Ablehnung ist die, die vor jeder Zuteilung geschieht. Sobald die Einrichtung mehrere externe Operationen umfasst, führen Sie Buch darüber, welche abgeschlossen wurden, und rollen Sie sie in umgekehrter Reihenfolge zurück, bevor Sie den Fehlerstatus zurückgeben.

Ein konkreter Fall: Das Skript legt einen Firewall-Eintrag pro Client an und scheitert dann beim Schreiben der Zustandszeile, die ihn beschreibt. Der fehlschlagende Zweig sollte den gerade erzeugten Eintrag entfernen und dann mit einem Wert ungleich null beenden. Auf ein disconnect-Ereignis zu warten, das vielleicht nie eingeplant wird, ist der Weg, auf dem eine Regel die Sitzung überlebt, für die sie gedacht war.

Machen Sie die Kompensation idempotent und verantwortet

Aufräumen muss zweimal sicher laufen und genau wissen, was es löschen darf. Ein spätes Ereignis aus einem früheren Versuch darf nicht die Zuteilung einer neueren Sitzung entfernen, nur weil beide einen gemeinsamen Namen oder eine wiederverwendete Tunneladresse teilen. Halten Sie den konkreten Handle oder die Operations-ID zusammen mit der Ressource fest und löschen Sie über diesen Schlüssel statt über den Namen. Datenbanktransaktionen helfen nur bei den eigenen Änderungen der Datenbank; eine Firewall-Regel außerhalb dieser Grenze braucht einen ausdrücklichen Kompensationsschritt.

Gehen Sie davon aus, dass der Prozess zwischen Änderung und Aufräumen sterben kann, und ergänzen Sie einen Abgleicher, der den externen Zustand nach Zeitplan mit der Liste der zugelassenen Clients vergleicht.

Testen Sie die drei Pfade, die sich tatsächlich unterscheiden

Üben Sie drei Fälle: eine Ablehnung, bevor etwas zugeteilt ist, eine nach einer teilweisen Zuteilung und eine normale zugelassene Sitzung, die später endet. Diese drei erzeugen unterschiedliche Logzeilen und unterschiedliche Rückstände. Prüfen Sie den externen Zustand nach jedem davon, nicht nur die Fehlermeldung des Clients, denn der fehlschlagende Client ist in zwei der drei Fälle das erwartete Ergebnis.

Protokollieren Sie die Hook-Phase, den Exit-Status und was zugeteilt oder freigegeben wurde, ohne Zugangsdaten. Ein Hook-Log, das die erzeugte Ressource benennt, macht eine verwaiste Regel sechs Monate später auffindbar.

Ähnliche Artikel