Ein leeres $_ENV-Array in einem Hook-Skript wirkt wie der Beweis, dass OpenVPN nichts übergeben hat. Meist ist es der Beweis für etwas ganz anderes: PHP war so konfiguriert, dass es diese Superglobale nicht füllt, und die Werte lagen die ganze Zeit in der Prozessumgebung. Die zweite Fehlschlussfolgerung ist subtiler, weil die beiden Adressvariablen in einem Verbindungs-Hook unterschiedliche Dinge bedeuten und austauschbar verwendet werden. Beide Fehler kosten einen Nachmittag, wenn man mit dem falschen beginnt.

Zwei Adressen, zwei verschiedene Aussagen

Der äußere Endpunkt ist das, was der Server beim Verbinden des Clients beobachtet hat, verfügbar als trusted_ip für IPv4 und trusted_ip6 für IPv6. Die virtuelle Adresse ist die, die der Pool für diese Sitzung vergeben hat, verfügbar als ifconfig_pool_remote_ip.

Sie beantworten unterschiedliche Fragen, und ein Datensatz braucht meist eine von beiden statt beider. Die erste ist, woher die Verbindung kam, was eine geteilte NAT-Adresse sein kann, die Hunderte von Menschen nutzen. Die zweite ist, wo diese Sitzung im Tunnel lebt, und sie kann später einer anderen Sitzung zugewiesen werden. Die beiden zu verwechseln erzeugt Datensätze, die plausibel aussehen und falsch sind.

Eine minimale Auslesung sieht so aus.

$outer4 = getenv('trusted_ip');
$outer6 = getenv('trusted_ip6');
$inner  = getenv('ifconfig_pool_remote_ip');
$outer  = $outer4 !== false ? $outer4 : $outer6;

Keiner der beiden Werte identifiziert eine Person. Der eine ist eine Netzwerkbeobachtung, der andere eine Zuweisung auf Zeit, und ein Datensatz, der einen von beiden als stabile Gerätekennung behandelt, wird falsch sein, sobald ein Client aus einem anderen Netz erneut verbindet oder eine andere Pooladresse erhält. Wählen Sie das Feld nach der Frage, die der Datensatz beantworten muss, und schreiben Sie diese Frage neben die Spalte.

Prüfen Sie, wie PHP gestartet wurde, nicht Ihre Shell

Der Hook läuft als Kind des VPN-Daemons mit einer Umgebung, die der Daemon aufbaut. Ihre interaktive Shell hat eine andere, und das Skript von Hand auszuführen testet die Umgebung der Shell statt die des Hooks.

Bestätigen Sie zuerst, dass der Hook wirklich in der erwarteten Phase ausgelöst wurde. Ein Skript, das an der falschen Phase hängt, oder eines, das nie lief, weil die Direktive auf einen anderen Pfad zeigt, erzeugt genau dasselbe Symptom wie eine fehlende Variable. Setzen Sie eine bewusste Markierungszeile an den Anfang des Skripts und bestätigen Sie, dass sie erscheint, bevor Sie irgendetwas anderes prüfen. Eine einzige Zeile mit Zeitstempel im Protokoll trennt nie gelaufen von gelaufen ohne den Wert.

Bestätigen Sie dann, welcher Interpreter und welche Konfigurationsdatei dieser Aufruf verwendet. Die PHP-Konfiguration für einen Webserver zu ändern, ändert nichts an einem CLI-Prozess, den OpenVPN startet, und beide können völlig unterschiedliche php.ini-Dateien laden.

Warum ein leeres $_ENV nichts beweist

PHPs getenv-Referenz dokumentiert ein direktes Lesen der Prozessumgebung, das false zurückgibt, wenn der Name fehlt. Die Superglobale $_ENV wird getrennt gefüllt, und die Direktive variables_order entscheidet, welche Superglobalen gefüllt werden.

Lässt diese Direktive E weg, bleibt $_ENV leer, egal was die Umgebung enthält. Code, der darauf zugreift, meldet dann eine fehlende Variable, die tatsächlich vorhanden ist, und die Untersuchung geht in Richtung OpenVPN, während die Antwort in php.ini liegt. Prüfen Sie die Direktive speziell für die CLI-Konfiguration, nicht für den Webserver.

Das Lesen mit getenv vermeidet diese ganze Klasse von Verwechslungen, und ein Wrapper-Skript, das die Umgebung vor dem Aufruf des Interpreters leert, führt sie in anderer Form wieder ein.

Speichern Sie eine Zuordnung, kein Zeilen-Update

Entscheiden Sie vor dem Schreiben, welchen Wert der Datensatz braucht. Wenn das Ziel ist, festzuhalten, woher eine Sitzung kam, speichern Sie den äußeren Endpunkt und kennzeichnen Sie ihn als Beobachtung. Wenn das Ziel ist, eine Sitzung für die Dauer einer Verbindung auf eine Tunneladresse abzubilden, speichern Sie die Pooladresse und binden Sie sie an die Sitzung.

Aktualisieren Sie nach der authentifizierten Identität und der aktuellen Sitzung statt nach jeder Zeile, die sich eine Tunneladresse teilt, weil Pooladressen wiederverwendet werden und ein spätes Trennungsereignis sonst die Daten einer neueren Verbindung überschreiben kann. Behandeln Sie IPv6 ausdrücklich, statt still nur den IPv4-Wert zu schreiben. Eine Zeile mit NULL als äußerer Adresse ist besser als eine mit der falschen, denn die erste ist sichtbar unvollständig.

Binden Sie das Schreiben an die Sitzungskennung, die OpenVPN liefert, damit ein erneutes Verbinden einen neuen Datensatz anlegt statt den alten zu bearbeiten. Verwenden Sie parametrisierte Anweisungen und ein Konto mit nur den Rechten, die der Vorgang braucht. Ein Hook läuft mit der Vollmacht des Daemons, also ist eine sorglose Abfrage ein größeres Problem als eine falsche Protokollzeile.

Diagnostizieren, ohne die Umgebung auszugeben

Protokollieren Sie, welche Namen vorhanden waren, welche Phase lief und welcher Interpreter verwendet wurde. Geben Sie nicht die gesamte Umgebung aus, denn ein Hook-Prozess kann fremde Werte erben, die nicht in einer Protokolldatei landen sollten.

Prüfen Sie eine Verbindung von Anfang bis Ende: Lesen Sie den äußeren Endpunkt, lesen Sie die zugewiesene Adresse und bestätigen Sie beide gegen die Sicht des Servers auf diese Sitzung. Ist das Paar für einen Client bestätigt, lässt sich der Zuordnung auch anderswo trauen.

Ähnliche Artikel