Verstehen und Implementieren von FastCGI-Proxyfunktionen in Nginx

Einführung

Nginx hat sich zu einer der flexibelsten und leistungsfähigsten verfügbaren Webserver-Lösungen entwickelt. In puncto Design handelt es sich jedoch in erster Linie um einen Proxy-Server. Dieser Fokus bedeutet, dass Nginx bei der Bearbeitung von Anfragen mit anderen Servern sehr performant ist.

Nginx kann Proxy-Anforderungen über http, FastCGI, uwsgi, SCGI oder memcached ausführen. In diesem Handbuch wird das FastCGI-Proxying behandelt, eines der am häufigsten verwendeten Proxying-Protokolle.

Warum FastCGI-Proxy verwenden?

FastCGI-Proxys in Nginx werden im Allgemeinen zum Übersetzen von Clientanforderungen für einen Anwendungsserver verwendet, der Clientanforderungen nicht direkt verarbeitet oder verarbeiten sollte. FastCGI ist ein Protokoll, das auf dem früheren CGI-Protokoll (Common Gateway Interface) basiert und die Leistung verbessern soll, indem nicht jede Anforderung als separater Prozess ausgeführt wird. Es wird verwendet, um eine effiziente Schnittstelle mit einem Server herzustellen, der Anforderungen für dynamischen Inhalt verarbeitet.

Einer der Hauptanwendungsfälle des FastCGI-Proxys in Nginx ist die PHP-Verarbeitung. Im Gegensatz zu Apache, das die PHP-Verarbeitung direkt mit dem Modulmod_phpabwickeln kann, muss Nginx für die Verarbeitung von PHP-Anforderungen auf einen separaten PHP-Prozessor angewiesen sein. Am häufigsten wird diese Verarbeitung mitphp-fpm ausgeführt, einem PHP-Prozessor, der ausgiebig für die Arbeit mit Nginx getestet wurde.

Nginx mit FastCGI kann mit Anwendungen verwendet werden, die andere Sprachen verwenden, sofern eine zugängliche Komponente konfiguriert ist, um auf FastCGI-Anforderungen zu antworten.

FastCGI-Proxy-Grundlagen

Im Allgemeinen leitet der Proxy-Server, in diesem Fall Nginx, Anforderungen von Clients an einen Back-End-Server weiter. Die Anweisung, die Nginx verwendet, um den tatsächlichen Server für die Verwendung des FastCGI-Protokolls als Proxy zu definieren, lautetfastcgi_pass.

Um beispielsweise alle übereinstimmenden Anforderungen für PHP an ein Backend weiterzuleiten, das sich mit der Verarbeitung von PHP unter Verwendung des FastCGI-Protokolls befasst, sieht ein grundlegender Standortblock möglicherweise folgendermaßen aus:

# server context

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

. . .

Das obige Snippet funktioniert nicht sofort, da es zu wenig Informationen enthält. Jedes Mal, wenn eine Proxy-Verbindung hergestellt wird, muss die ursprüngliche Anforderung übersetzt werden, um sicherzustellen, dass die Proxy-Anforderung für den Back-End-Server sinnvoll ist. Da wir die Protokolle mit einem FastCGI-Pass ändern, sind einige zusätzliche Arbeiten erforderlich.

Während beim HTTP-zu-HTTP-Proxying hauptsächlich die HTTP-Header erweitert werden, um sicherzustellen, dass das Back-End über die Informationen verfügt, die es benötigt, um im Auftrag des Clients auf den Proxy-Server zu antworten, ist FastCGI ein separates Protokoll, das keine HTTP-Header lesen kann. Aufgrund dieser Überlegung müssen relevante Informationen auf andere Weise an das Backend weitergegeben werden.

Die Hauptmethode zum Übergeben zusätzlicher Informationen bei Verwendung des FastCGI-Protokolls sind Parameter. Der Hintergrundserver sollte so konfiguriert sein, dass er diese liest und verarbeitet und sein Verhalten je nach dem, was er findet, ändert. Nginx kann FastCGI-Parameter mithilfe der Direktivefastcgi_paramfestlegen.

Die absolute Mindestkonfiguration, die in einem FastCGI-Proxy-Szenario für PHP tatsächlich funktioniert, sieht folgendermaßen aus:

# server context

location ~ \.php$ {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

In der obigen Konfiguration haben wir zwei FastCGI-Parameter festgelegt, die alsREQUEST_METHOD undSCRIPT_FILENAME bezeichnet werden. Beides ist erforderlich, damit der Back-End-Server die Art der Anforderung versteht. Ersteres gibt an, welche Art von Operation ausgeführt werden soll, während Letzteres dem Upstream mitteilt, welche Datei ausgeführt werden soll.

In diesem Beispiel haben wir einige Nginx-Variablen verwendet, um die Werte dieser Parameter festzulegen. Die Variable$request_method enthält immer die vom Client angeforderte http-Methode.

Der ParameterSCRIPT_FILENAME wird auf eine Kombination der Variablen$document_root und der Variablen$fastcgi_script_name gesetzt. Die$document_root enthalten den Pfad zum Basisverzeichnis, wie in der Direktiveroot festgelegt. Die Variable$fastcgi_script_name wird auf den Anforderungs-URI gesetzt. Wenn der Anforderungs-URI mit einem Schrägstrich (/) endet, wird der Wert der Direktivefastcgi_indexan das Ende angehängt. Diese Art von selbstreferenziellen Standortdefinitionen ist möglich, da der FastCGI-Prozessor auf demselben Computer wie unsere Nginx-Instanz ausgeführt wird.

Schauen wir uns ein anderes Beispiel an:

# server context
root /var/www/html;

location /scripts {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

. . .

Wenn der obige Speicherort ausgewählt ist, um eine Anforderung für/scripts/test/ zu verarbeiten, ist der Wert vonSCRIPT_FILENAME eine Kombination aus den Werten der Direktiveroot, dem Anforderungs-URI und demfastcgi_index Richtlinie. In diesem Beispiel wird der Parameter auf/var/www/html/scripts/test/index.php gesetzt.

Wir haben eine weitere wichtige Änderung in der obigen Konfiguration vorgenommen, indem wir das FastCGI-Backend mithilfe eines Unix-Sockets anstelle eines Netzwerk-Sockets spezifiziert haben. Nginx kann mit beiden Schnittstellentypen eine Verbindung zum FastCGI-Upstream herstellen. Wenn sich der FastCGI-Prozessor auf demselben Host befindet, wird aus Sicherheitsgründen in der Regel ein Unix-Socket empfohlen.

Ausbrechen der FastCGI-Konfiguration

Eine wichtige Regel für wartbaren Code ist, dass Sie versuchen, dem DRY-Prinzip ("Don't Repeat Yourself") zu folgen. Dies hilft, Fehler zu reduzieren, die Wiederverwendbarkeit zu erhöhen und die Organisation zu verbessern. Angesichts der Tatsache, dass eine der wichtigsten Empfehlungen für die Verwaltung von Nginx darin besteht, Richtlinien immer in ihrem breitesten Anwendungsbereich festzulegen, gelten diese grundlegenden Ziele auch für die Konfiguration von Nginx.

Bei FastCGI-Proxy-Konfigurationen teilen sich die meisten Anwendungsfälle einen großen Teil der Konfiguration. Aus diesem Grund und aufgrund der Funktionsweise des Nginx-Vererbungsmodells ist es fast immer vorteilhaft, Parameter in einem allgemeinen Bereich zu deklarieren.

Festlegen von FastCGI-Konfigurationsdetails in übergeordneten Kontexten

Eine Möglichkeit, die Wiederholung zu reduzieren, besteht darin, die Konfigurationsdetails in einem höheren übergeordneten Kontext zu deklarieren. Alle Parameter außerhalb der tatsächlichenfastcgi_pass können auf höheren Ebenen angegeben werden. Sie kaskadieren nach unten in den Ort, an dem der Pass stattfindet. Dies bedeutet, dass mehrere Standorte dieselbe Konfiguration verwenden können.

Zum Beispiel könnten wir das letzte Konfigurations-Snippet aus dem obigen Abschnitt ändern, um es an mehr als einem Ort nützlich zu machen:

# server context
root /var/www/html;

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;

location /scripts {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

. . .

Im obigen Beispiel sind sowohl die Deklarationen vonfastcgi_paramals auch die Direktive vonfastcgi_indexin beiden nachfolgenden Positionsblöcken verfügbar. Dies ist eine Möglichkeit, sich wiederholende Deklarationen zu entfernen.

Die obige Konfiguration hat jedoch einen schwerwiegenden Nachteil. Wennfastcgi_param im unteren Kontext deklariert ist, werdennone derfastcgi_param-Werte aus dem übergeordneten Kontext geerbt. Sie verwenden entwederonly der geerbten Werte oder Sie verwenden keinen von ihnen.

Die Direktivefastcgi_param ist eine Direktivearrayim Nginx-Sprachgebrauch. Aus Benutzersicht ist eine Array-Direktive im Grunde jede Direktive, die in einem einzelnen Kontext mehrmals verwendet werden kann. Bei jeder nachfolgenden Erklärung werden die neuen Informationen an die Informationen angehängt, die Nginx aus den vorherigen Erklärungen kennt. Diefastcgi_param-Direktive wurde als Array-Direktive konzipiert, damit Benutzer mehrere Parameter festlegen können.

Array-Direktiven erben auf andere Weise an untergeordnete Kontexte als einige andere Direktiven. Die Informationen aus Array-Anweisungen werden an untergeordnete Kontexteonly if they are not present at any place in the child context geerbt. Dies bedeutet, dass, wenn Siefastcgi_param an Ihrem Standort verwenden, die vom übergeordneten Kontext geerbten Werte effektiv vollständig gelöscht werden.

Zum Beispiel könnten wir die obige Konfiguration leicht modifizieren:

# server context
root /var/www/html;

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;

location /scripts {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

Auf den ersten Blick können Sie denken, dass die ParameterREQUEST_METHOD undSCRIPT_FILENAME in den zweiten Standortblock vererbt werden, wobei der ParameterQUERY_STRING für diesen bestimmten Kontext zusätzlich verfügbar ist.

Was tatsächlich passiert, ist, dassall der übergeordnetenfastcgi_param-Werte im zweiten Kontext gelöscht werden und nur derQUERY_STRING-Parameter festgelegt wird. Die ParameterREQUEST_METHOD undSCRIPT_FILENAME bleiben nicht festgelegt.

Ein Hinweis zu mehreren Werten für Parameter im selben Kontext

Eine Sache, die an dieser Stelle definitiv erwähnenswert ist, sind die Auswirkungen des Einstellens mehrerer Werte für dieselben Parameter in einem einzigen Kontext. Nehmen wir das folgende Beispiel als Diskussionspunkt:

# server context

location ~ \.php$ {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $request_uri;

    fastcgi_param DOCUMENT_ROOT initial;
    fastcgi_param DOCUMENT_ROOT override;

    fastcgi_param TEST one;
    fastcgi_param TEST two;
    fastcgi_param TEST three;

    fastcgi_pass 127.0.0.1:9000;
}

. . .

Im obigen Beispiel haben wir die ParameterTEST undDOCUMENT_ROOT mehrmals in einem einzigen Kontext festgelegt. Dafastcgi_param eine Array-Direktive ist, wird jede nachfolgende Deklaration zu den internen Datensätzen von Nginx hinzugefügt. Der ParameterTEST enthält Deklarationen im Array, die ihn aufone,two undthree setzen.

An dieser Stelle ist zu beachten, dass alle diese Daten ohne weitere Verarbeitung von Nginx an das FastCGI-Backend übergeben werden. Dies bedeutet, dass es völlig Sache des gewählten FastCGI-Prozessors ist, zu entscheiden, wie mit diesen Werten umgegangen werden soll. Leider verarbeiten verschiedene FastCGI-Prozessoren die übergebenen Wertecompletely differently.

Wenn beispielsweise die obigen Parameter von PHP-FPM empfangen würden, würde der Wert vonfinalinterpretiert, um einen der vorherigen Werte zu überschreiben. In diesem Fall würde der ParameterTEST aufthree gesetzt. In ähnlicher Weise würde der ParameterDOCUMENT_ROOT aufoverride gesetzt.

Wenn der obige Wert jedoch an so etwas wie FsgiWrap übergeben wird, werden die Werte sehr unterschiedlich interpretiert. Zunächst wird festgelegt, welche Werte zum Ausführen des Skripts verwendet werden sollen. Es wird derDOCUMENT_ROOT-Wert voninitial verwendet, um nach dem Skript zu suchen. Wenn es jedoch die tatsächlichen Parameter an das Skript übergibt, werden die endgültigen Werte übergeben, genau wie bei PHP-FPM.

Diese Inkonsistenz und Unvorhersehbarkeit bedeutet, dass Sie sich nicht auf das Backend verlassen können und sollten, um Ihre Absichten korrekt zu interpretieren, wenn Sie denselben Parameter mehr als einmal einstellen. Die einzig sichere Lösung besteht darin, jeden Parameter nur einmal zu deklarieren. Dies bedeutet auch, dass es keinen sicheren Wert gibt, einen Standardwert mit der Direktivefastcgi_paramzu überschreiben.

Verwenden von Einschließen, um die FastCGI-Konfiguration aus einer separaten Datei zu beziehen

Es gibt eine andere Möglichkeit, die allgemeinen Konfigurationselemente voneinander zu trennen. Wir können die Direktiveinclude verwenden, um den Inhalt einer separaten Datei an den Speicherort der Direktivendeklaration einzulesen.

Dies bedeutet, dass wir alle gängigen Konfigurationselemente in einer einzigen Datei speichern und an jeder Stelle in unsere Konfiguration aufnehmen können, an der wir sie benötigen. Da Nginx den eigentlichen Dateiinhalt dort platziert, woinclude aufgerufen wird, erben wir nicht von einem übergeordneten Kontext nach unten an ein untergeordnetes Element. Dadurch wird verhindert, dass diefastcgi_param-Werte gelöscht werden, sodass wir bei Bedarf zusätzliche Parameter einstellen können.

Zunächst können wir unsere allgemeinen FastCGI-Konfigurationswerte in einer separaten Datei in unserem Konfigurationsverzeichnis festlegen. Wir werden diese Dateifastcgi_common nennen:

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Jetzt können wir diese Datei überall dort einlesen, wo wir diese Konfigurationswerte verwenden möchten:

# server context
root /var/www/html;

location /scripts {
    include fastcgi_common;

    fastcgi_index index.php;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    include fastcgi_common;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;

    fastcgi_index index.php;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

Hier haben wir einige gängigefastcgi_param-Werte in eine Datei namensfastcgi_common in unserem Standard-Nginx-Konfigurationsverzeichnis verschoben. Diese Datei wird dann als Quelle verwendet, wenn die darin deklarierten Werte eingefügt werden sollen.

Zu dieser Konfiguration sind einige Punkte zu beachten.

Das erste ist, dass wir in der Datei, die wir beschaffen möchten, keine Werte platziert haben, die wir möglicherweise auf Standortbasis anpassen möchten. Aufgrund des oben erwähnten Interpretationsproblems, das auftritt, wenn mehrere Werte für denselben Parameter festgelegt werden, und weil Nicht-Array-Anweisungen nur einmal pro Kontext festgelegt werden können, platzieren Sie nur Elemente in der gemeinsamen Datei, die Sie nicht ändern möchten. Jede Direktive (oder jeder Parameterschlüssel), die wir möglicherweise kontextbezogen anpassen möchten, sollte nicht in der gemeinsamen Datei enthalten sein.

Das andere, was Ihnen vielleicht aufgefallen ist, ist, dass wir im zweiten Adressblock einige zusätzliche FastCGI-Parameter festgelegt haben. Dies ist die Fähigkeit, die wir uns erhofft hatten. Wir konnten nach Bedarf zusätzlichefastcgi_param-Parameter einstellen, ohne die gemeinsamen Werte zu löschen.

Verwenden Sie die Datei fastcgi_params oder die Datei fastcgi.conf

Unter Berücksichtigung der oben genannten Strategie haben die Nginx-Entwickler und viele Distribution Packaging-Teams darauf hingearbeitet, eine Reihe allgemeiner Parameter bereitzustellen, die Sie in Ihre FastCGI-Pass-Standorte aufnehmen können. Diese werden alsfastcgi_params oderfastcgi.conf bezeichnet.

Diese beiden Dateien sind weitgehend identisch. Der einzige Unterschied ergibt sich aus dem zuvor diskutierten Problem der Übergabe mehrerer Werte für einen einzelnen Parameter. Die Dateifastcgi_params enthält keine Deklaration für den ParameterSCRIPT_FILENAME, während die Dateifastcgi.conf eine Deklaration enthält.

Diefastcgi_params-Datei ist seit einem viel längeren Zeitraum verfügbar. Um zu vermeiden, dass Konfigurationen unterbrochen werden, die auffastcgi_params basieren, musste bei der Entscheidung, einen Standardwert fürSCRIPT_FILENAME anzugeben, eine neue Datei erstellt werden. Andernfalls wurde dieser Parameter möglicherweise sowohl in der Common File- als auch in der FastCGI-Übergabeposition festgelegt. Dies wird inMartin Fjordvald’s excellent post on the history of these two files ausführlich beschrieben.

Viele Paketbetreuer für beliebte Distributionen haben sich dafür entschieden, nur eine dieser Dateien einzuschließen oder deren Inhalt exakt zu spiegeln. Wenn Sie nur eine davon zur Verfügung haben, verwenden Sie diejenige, die Sie haben. Sie können es auch an Ihre Bedürfnisse anpassen.

Wenn Ihnen beide Dateien zur Verfügung stehen, ist es für die meisten FastCGI-Pass-Speicherorte wahrscheinlich besser, die Dateifastcgi.confeinzuschließen, da sie eine Deklaration für den ParameterSCRIPT_FILENAMEenthält. Dies ist normalerweise wünschenswert, aber es gibt einige Fälle, in denen Sie diesen Wert anpassen möchten.

Diese können einbezogen werden, indem auf ihren Speicherort relativ zum Nginx-Stammkonfigurationsverzeichnis verwiesen wird. Das Nginx-Stammkonfigurationsverzeichnis ist normalerweise so etwas wie/etc/nginx, wenn Nginx mit einem Paketmanager installiert wurde.

Sie können die Dateien wie folgt einschließen:

# server context

location ~ \.php$ {
    include fastcgi_params;
    # You would use "fastcgi_param SCRIPT_FILENAME . . ." here afterwards

    . . .

}

Oder so:

# server context

location ~ \.php$ {
    include fastcgi.conf;

    . . .

}

Wichtige FastCGI-Direktiven, -Parameter und -Variablen

In den obigen Abschnitten haben wir eine Reihe von Parametern festgelegt, häufig Nginx-Variablen, um andere Konzepte zu demonstrieren. Wir haben auch einige FastCGI-Direktiven ohne allzu viele Erklärungen eingeführt. In diesem Abschnitt werden einige der allgemeinen Anweisungen, die festgelegt werden müssen, Parameter, die möglicherweise geändert werden müssen, und einige Variablen erläutert, die möglicherweise die von Ihnen benötigten Informationen enthalten.

Gemeinsame FastCGI-Richtlinien

Die folgenden Anweisungen stellen einige der nützlichsten Anweisungen für die Arbeit mit FastCGI-Pässen dar:

  • fastcgi_pass: Die eigentliche Direktive, die Anforderungen im aktuellen Kontext an das Backend weiterleitet. Dies definiert den Ort, an dem der FastCGI-Prozessor erreicht werden kann.

  • fastcgi_param: Die Array-Direktive, mit der Parameter auf Werte gesetzt werden können. In den meisten Fällen wird dies in Verbindung mit Nginx-Variablen verwendet, um FastCGI-Parameter auf Werte zu setzen, die für die Anforderung spezifisch sind.

  • try_files: Keine FastCGI-spezifische Direktive, sondern eine allgemeine Direktive, die in FastCGI-Pass-Positionen verwendet wird. Dies wird häufig als Teil einer Anforderungsbereinigungsroutine verwendet, um sicherzustellen, dass die angeforderte Datei vorhanden ist, bevor sie an den FastCGI-Prozessor übergeben wird.

  • include: Auch dies ist keine FastCGI-spezifische Direktive, sondern eine, die in FastCGI-Pass-Kontexten häufig verwendet wird. In den meisten Fällen werden hiermit gemeinsame Konfigurationsdetails an mehreren Standorten angegeben.

  • fastcgi_split_path_info: Diese Anweisung definiert einen regulären Ausdruck mit zwei erfassten Gruppen. Die erste erfasste Gruppe wird als Wert für die Variable$fastcgi_script_nameverwendet. Die zweite erfasste Gruppe wird als Wert für die Variable$fastcgi_path_infoverwendet. Beide werden häufig verwendet, um die Anforderung korrekt zu analysieren, damit der Prozessor weiß, welche Teile der Anforderung die auszuführenden Dateien sind und welche Teile zusätzliche Informationen sind, die an das Skript übergeben werden müssen.

  • fastcgi_index: Dies definiert die Indexdatei, die an$fastcgi_script_name-Werte angehängt werden soll, die mit einem Schrägstrich (/) enden. Dies ist häufig nützlich, wenn der ParameterSCRIPT_FILENAME auf$document_root$fastcgi_script_name gesetzt ist und der Standortblock so konfiguriert ist, dass Anforderungen mit Informationen nach der Datei akzeptiert werden.

  • fastcgi_intercept_errors: Diese Anweisung definiert, ob vom FastCGI-Server empfangene Fehler von Nginx behandelt oder direkt an den Client übergeben werden sollen.

Die obigen Anweisungen stellen den größten Teil dessen dar, was Sie beim Entwerfen eines typischen FastCGI-Passes verwenden werden. Möglicherweise verwenden Sie diese nicht immer, aber wir können feststellen, dass sie sehr eng mit den FastCGI-Parametern und -Variablen interagieren, über die wir als Nächstes sprechen werden.

Allgemeine Variablen, die mit FastCGI verwendet werden

Bevor wir uns mit den Parametern befassen, die Sie wahrscheinlich für FastCGI-Pässe verwenden, sollten wir uns ein wenig mit einigen gängigen Nginx-Variablen befassen, die wir beim Festlegen dieser Parameter nutzen werden. Einige davon sind durch das FastCGI-Modul von Nginx definiert, die meisten stammen jedoch aus dem Core-Modul.

  • $query_string or $args: Die in der ursprünglichen Clientanforderung angegebenen Argumente.

  • $is_args: Wird gleich "?" Wenn die Anforderung Argumente enthält und andernfalls auf eine leere Zeichenfolge gesetzt wird. Dies ist nützlich, wenn Parameter erstellt werden, die möglicherweise Argumente enthalten oder nicht.

  • $request_method: Dies gibt die ursprüngliche Clientanforderungsmethode an. Dies kann hilfreich sein, um festzustellen, ob eine Operation im aktuellen Kontext zulässig sein soll.

  • $content_type: Dies wird auf den AnforderungsheaderContent-Type gesetzt. Diese Informationen werden vom Proxy benötigt, wenn es sich bei der Benutzeranforderung um einen POST handelt, um den folgenden Inhalt korrekt zu verarbeiten.

  • $content_length: Dies wird vom Client auf den Wert des HeadersContent-Length gesetzt. Diese Informationen werden für alle Client-POST-Anforderungen benötigt.

  • $fastcgi_script_name: Dies enthält die auszuführende Skriptdatei. Wenn die Anforderung mit einem Schrägstrich (/) endet, wird der Wert der Direktivefastcgi_indexan das Ende angehängt. Für den Fall, dass die Direktivefastcgi_split_path_infoverwendet wird, wird diese Variable auf die erste erfasste Gruppe gesetzt, die durch diese Direktive definiert wird. Der Wert dieser Variablen sollte das tatsächlich auszuführende Skript angeben.

  • $request_filename: Diese Variable enthält den Dateipfad für die angeforderte Datei. Dieser Wert wird ermittelt, indem der Wert des aktuellen Dokumentstamms berücksichtigt wird, wobei sowohl die Anweisungenroot undalias als auch der Wert von$fastcgi_script_name berücksichtigt werden. Dies ist eine sehr flexible Möglichkeit, den ParameterSCRIPT_FILENAMEzuzuweisen.

  • $request_uri: Die gesamte vom Client empfangene Anforderung. Dies umfasst das Skript, alle zusätzlichen Pfadinformationen sowie alle Abfragezeichenfolgen.

  • $fastcgi_path_info: Diese Variable enthält zusätzliche Pfadinformationen, die möglicherweise nach dem Skriptnamen in der Anforderung verfügbar sind. Dieser Wert enthält manchmal einen anderen Speicherort, den das auszuführende Skript kennen sollte. Diese Variable erhält ihren Wert von der zweiten erfassten Regex-Gruppe, wenn die Direktivefastcgi_split_path_infoverwendet wird.

  • $document_root: Diese Variable enthält den aktuellen Dokumentstammwert. Dies wird gemäß den Anweisungenroot oderalias festgelegt.

  • $uri: Diese Variable enthält den aktuellen URI mit angewendeter Normalisierung. Da bestimmte Anweisungen, die neu geschrieben oder intern umgeleitet werden, Auswirkungen auf den URI haben können, drückt diese Variable diese Änderungen aus.

Wie Sie sehen, stehen Ihnen bei der Festlegung der FastCGI-Parameter eine Reihe von Variablen zur Verfügung. Viele davon sind ähnlich, weisen jedoch einige geringfügige Unterschiede auf, die sich auf die Ausführung Ihrer Skripte auswirken.

Allgemeine FastCGI-Parameter

FastCGI-Parameter stellen Schlüsselwertinformationen dar, die wir dem FastCGI-Prozessor, an den wir die Anforderung senden, zur Verfügung stellen möchten. Nicht für jede Anwendung sind dieselben Parameter erforderlich. Daher müssen Sie häufig die Dokumentation der App konsultieren.

Einige dieser Parameter sind erforderlich, damit der Prozessor das auszuführende Skript korrekt identifiziert. Andere werden dem Skript zur Verfügung gestellt und ändern möglicherweise sein Verhalten, wenn es so konfiguriert ist, dass es sich auf die festgelegten Parameter stützt.

  • QUERY_STRING: Dieser Parameter sollte auf eine vom Client bereitgestellte Abfragezeichenfolge gesetzt werden. Dies sind normalerweise Schlüssel-Wert-Paare, die nach einem "?" In der URI angegeben werden. In der Regel wird dieser Parameter entweder auf die Variablen$query_string oder$args gesetzt, die beide dieselben Daten enthalten sollten.

  • REQUEST_METHOD: Dieser Parameter gibt dem FastCGI-Prozessor an, welche Art von Aktion vom Client angefordert wurde. Dies ist einer der wenigen Parameter, die eingestellt werden müssen, damit der Durchlauf ordnungsgemäß funktioniert.

  • CONTENT_TYPE: Wenn die oben festgelegte Anforderungsmethode "POST" ist, muss dieser Parameter festgelegt werden. Es gibt den Inhaltstyp an, den der FastCGI-Prozessor erwarten sollte. Dies wird fast immer nur auf die Variable$content_type gesetzt, die gemäß den Informationen in der ursprünglichen Anforderung festgelegt wird.

  • CONTENT_LENGTH: Wenn die Anforderungsmethode "POST" ist, muss dieser Parameter gesetzt werden. Dies gibt die Inhaltslänge an. Dies wird fast immer nur auf$content_length gesetzt, eine Variable, die ihren Wert aus Informationen in der ursprünglichen Clientanforderung erhält.

  • SCRIPT_NAME: Mit diesem Parameter wird der Name des Hauptskripts angegeben, das ausgeführt werden soll. Dies ist ein äußerst wichtiger Parameter, der je nach Ihren Anforderungen auf verschiedene Arten eingestellt werden kann. Oft wird dies auf$fastcgi_script_name gesetzt. Dies sollte der Anforderungs-URI sein, der Anforderungs-URI mit den angehängtenfastcgi_index, wenn er mit einem Schrägstrich endet, oder die erste erfasste Gruppe, wennfastcgi_fix_path_info verwendet wird.

  • SCRIPT_FILENAME: Dieser Parameter gibt den tatsächlichen Speicherort des auszuführenden Skripts auf der Festplatte an. Aufgrund der Beziehung zum ParameterSCRIPT_NAME schlagen einige Hilfslinien vor,$document_root$fastcgi_script_name zu verwenden. Eine andere Alternative, die viele Vorteile hat, ist die Verwendung von$request_filename.

  • REQUEST_URI: Diese sollte den vollständigen, unveränderten Anforderungs-URI enthalten, einschließlich des auszuführenden Skripts, zusätzlicher Pfadinformationen und etwaiger Argumente. Einige Anwendungen ziehen es vor, diese Informationen selbst zu analysieren. Dieser Parameter gibt ihnen die dazu notwendigen Informationen.

  • PATH_INFO: Wenncgi.fix_pathinfo in der PHP-Konfigurationsdatei auf "1" gesetzt ist, enthält diese alle zusätzlichen Pfadinformationen, die nach dem Skriptnamen hinzugefügt werden. Dies wird häufig verwendet, um ein Dateiargument zu definieren, auf das das Skript reagieren soll. Das Setzen voncgi.fix_pathinfo auf "1" kann Auswirkungen auf die Sicherheit haben, wenn die Skriptanforderungen nicht auf andere Weise bereinigt werden (wir werden dies später diskutieren). Manchmal wird dies auf die Variable$fastcgi_path_info gesetzt, die die zweite erfasste Gruppe aus der Direktivefastcgi_split_path_infoenthält. In anderen Fällen muss eine temporäre Variable verwendet werden, da dieser Wert manchmal durch eine andere Verarbeitung überlastet wird.

  • PATH_TRANSLATED: Dieser Parameter ordnet die inPATH_INFO enthaltenen Pfadinformationen einem tatsächlichen Dateisystempfad zu. Normalerweise wird dies auf$document_root$fastcgi_path_info gesetzt, aber manchmal muss die spätere Variable wie oben angegeben durch die temporär gespeicherte Variable ersetzt werden.

Überprüfen von Anforderungen vor der Übergabe an FastCGI

Ein sehr wichtiges Thema, das wir noch nicht behandelt haben, ist die sichere Übermittlung dynamischer Anforderungen an Ihren Anwendungsserver. Das Weiterleiten aller Anforderungen an die Back-End-Anwendung, unabhängig von ihrer Gültigkeit, ist nicht nur ineffizient, sondern auch gefährlich. Es ist für Angreifer möglich, böswillige Anfragen zu erstellen, um Ihren Server dazu zu bringen, beliebigen Code auszuführen.

Um dieses Problem zu beheben, sollten wir sicherstellen, dass wir nur legitime Anfragen an unsere FastCGI-Prozessoren senden. Wir können dies auf verschiedene Arten tun, abhängig von den Anforderungen unserer speziellen Konfiguration und davon, ob der FastCGI-Prozessor auf demselben System wie unsere Nginx-Instanz ausgeführt wird.

Eine Grundregel, die darüber Auskunft geben sollte, wie wir unsere Konfiguration gestalten, lautet, dass wir niemals die Verarbeitung und Interpretation von Benutzerdateien zulassen sollten. Es ist für böswillige Benutzer relativ einfach, gültigen Code in scheinbar unschuldige Dateien wie Bilder einzubetten. Sobald eine solche Datei auf unseren Server hochgeladen wurde, müssen wir sicherstellen, dass sie niemals auf unseren FastCGI-Prozessor gelangt.

Das Hauptproblem, das wir hier zu lösen versuchen, ist eines, das tatsächlich in der CGI-Spezifikation angegeben ist. In der Spezifikation können Sie eine auszuführende Skriptdatei angeben, gefolgt von zusätzlichen Pfadinformationen, die vom Skript verwendet werden können. Mit diesem Ausführungsmodell können Benutzer einen URI anfordern, der wie ein legitimes Skript aussieht, während der tatsächliche Teil, der ausgeführt wird, sich früher im Pfad befindet.

Betrachten Sie eine Anfrage für/test.jpg/index.php. Wenn Ihre Konfiguration einfach jede Anforderung, die mit.php endet, an Ihren Prozessor weiterleitet, ohne ihre Legitimität zu testen, überprüft der Prozessor, wenn er der Spezifikation folgt, diesen Speicherort und führt ihn nach Möglichkeit aus. Wenndoes notdie Datei findet, folgt sie der Spezifikation und versucht, die Datei/test.jpgauszuführen, wobei/index.php als zusätzliche Pfadinformation für das Skript markiert wird. Wie Sie sehen, kann dies in Verbindung mit der Idee der Benutzer-Uploads einige sehr unerwünschte Konsequenzen haben.

Es gibt verschiedene Möglichkeiten, um dieses Problem zu beheben. Wenn Ihre Anwendung diese zusätzlichen Pfadinformationen für die Verarbeitung nicht benötigt, ist es am einfachsten, sie in Ihrem Prozessor zu deaktivieren. Für PHP-FPM können Sie dies in derphp.ini-Datei deaktivieren. Auf Ubuntu-Systemen können Sie beispielsweise die folgende Datei bearbeiten:

sudo nano /etc/php5/fpm/php.ini

Suchen Sie einfach nach der Optioncgi.fix_pathinfo, kommentieren Sie sie aus und setzen Sie sie auf "0", um diese "Funktion" zu deaktivieren:

cgi.fix_pathinfo=0

Starten Sie Ihren PHP-FPM-Prozess neu, um die Änderung vorzunehmen:

sudo service php5-fpm restart

Dies führt dazu, dass PHP immer nur versucht, die letzte Komponente eines Pfades auszuführen. Wenn in unserem obigen Beispiel die Datei/test.jpg/index.php nicht vorhanden wäre, würde PHP korrekt fehlerhaft sein, anstatt zu versuchen,/test.jpg auszuführen.

Wenn sich unser FastCGI-Prozessor auf demselben Computer wie unsere Nginx-Instanz befindet, können Sie auch einfach das Vorhandensein der Dateien auf der Festplatte überprüfen, bevor Sie sie an den Prozessor übergeben. Wenn die Datei/test.jgp/index.phpnicht vorhanden ist, wird ein Fehler ausgegeben. Wenn dies der Fall ist, senden Sie es zur Verarbeitung an das Backend. In der Praxis führt dies zu einem großen Teil des oben beschriebenen Verhaltens:

location ~ \.php$ {
        try_files $uri =404;

        . . .

}

Wenn sich Ihre Anwendungdoesfür eine korrekte Interpretation auf das Pfadinformationsverhalten stützt, können Sie dieses Verhalten dennoch sicher zulassen, indem Sie Überprüfungen durchführen, bevor Sie entscheiden, ob die Anforderung an das Backend gesendet werden soll.

Beispielsweise könnten wir gezielt die Verzeichnisse zuordnen, in denen wir nicht vertrauenswürdige Uploads zulassen, und sicherstellen, dass sie nicht an unseren Prozessor weitergeleitet werden. Wenn das Upload-Verzeichnis unserer Anwendung beispielsweise/uploads/ lautet, können wir einen Standortblock wie diesen erstellen, der übereinstimmt, bevor reguläre Ausdrücke ausgewertet werden:

location ^~ /uploads {
}

Im Inneren können wir jede Art von Verarbeitung für PHP-Dateien deaktivieren:

location ^~ /uploads {
    location ~* \.php$ { return 403; }
}

Der übergeordnete Speicherort stimmt mit jeder Anforderung überein, die mit/uploads beginnt, und jede Anforderung, die sich mit PHP-Dateien befasst, gibt einen 403-Fehler zurück, anstatt ihn an ein Backend zu senden.

Sie können auch die Anweisungfastcgi_split_path_info verwenden, um den Teil der Anforderung, der als Skript interpretiert werden soll, und den Teil, der als zusätzliche Pfadinformationen definiert werden soll, mithilfe regulärer Ausdrücke manuell zu definieren. Auf diese Weise können Sie sich weiterhin auf die Pfadinformationsfunktion verlassen, aber genau definieren, was Sie als Skript und was als Pfad betrachten.

Zum Beispiel können wir einen Standortblock einrichten, der die erste Instanz einer Pfadkomponente, die mit.php endet, als das auszuführende Skript betrachtet. Der Rest wird als zusätzliche Pfadinformation betrachtet. Dies bedeutet, dass im Fall einer Anforderung für/test.jpg/index.php der gesamte Pfad als Skriptname ohne zusätzliche Pfadinformationen an den Prozessor gesendet werden kann.

Dieser Ort könnte ungefähr so ​​aussehen:

location ~ [^/]\.php(/|$) {

    fastcgi_split_path_info ^(.+?\.php)(.*)$;
    set $orig_path $fastcgi_path_info;

    try_files $fastcgi_script_name =404;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $orig_path;
    fastcgi_param PATH_TRANSLATED $document_root$orig_path;
}

Der obige Block sollte für PHP-Konfigurationen funktionieren, bei denencgi.fix_pathinfo auf "1" gesetzt ist, um zusätzliche Pfadinformationen zu ermöglichen. Hier stimmt unser Standortblock nicht nur mit Anforderungen überein, die mit.php enden, sondern auch mit Anforderungen mit.php, kurz bevor ein Schrägstrich (/) anzeigt, dass eine zusätzliche Verzeichniskomponente folgt.

Innerhalb des Blocks definiert die Direktivefastcgi_split_path_infozwei erfasste Gruppen mit regulären Ausdrücken. Die erste Gruppe stimmt mit dem Teil des URI vom Anfang bis zur ersten Instanz von.php überein und platziert diesen in der Variablen$fastcgi_script_name. Von diesem Punkt an werden alle Informationen in eine zweite erfasste Gruppe eingefügt, die in einer Variablen namens$fastcgi_path_info gespeichert wird.

Wir verwenden die Direktiveset, um den an dieser Stelle in$fastcgi_path_info enthaltenen Wert in einer Variablen namens$orig_path zu speichern. Dies liegt daran, dass die Variable$fastcgi_path_infodurch unsere Anweisungtry_filesin einem Moment gelöscht wird.

Wir testen den oben erfassten Skriptnamen mittry_files. Dies ist eine Dateioperation, die sicherstellt, dass sich das Skript, das wir ausführen möchten, auf der Festplatte befindet. Dies hat jedoch auch den Nebeneffekt, dass die Variable$fastcgi_path_info gelöscht wird.

Nach dem herkömmlichen FastCGI-Pass setzen wir dieSCRIPT_FILENAME wie gewohnt. Wir setzen auchPATH_INFO auf den Wert, den wir in die Variable$orig_path abgeladen haben. Obwohl unser$fastcgi_path_info gelöscht wurde, bleibt sein ursprünglicher Wert in dieser Variablen erhalten. Wir setzen auch den ParameterPATH_TRANSLATED, um die zusätzlichen Pfadinformationen dem Speicherort zuzuordnen, an dem sie auf der Festplatte vorhanden sind. Dazu kombinieren wir die Variable$document_rootmit der Variablen$orig_path.

Auf diese Weise können wir Anforderungen wie/index.php/users/view erstellen, sodass unsere Datei/index.php Informationen über das Verzeichnis/users/viewverarbeiten kann, während Situationen vermieden werden, in denen/test.jpg/index.php ausgeführt werden. Das Skript wird immer auf die kürzeste Komponente gesetzt, die mit.php endet, wodurch dieses Problem vermieden wird.

Wir könnten diese Funktion sogar mit einer Alias-Direktive ausführen, wenn wir den Speicherort unserer Skriptdateien ändern müssen. Wir müssten dies nur sowohl in unserem Standortheader als auch in der Definition vonfastcgi_split_path_infoberücksichtigen:

location ~ /test/.+[^/]\.php(/|$) {

    alias /var/www/html;

    fastcgi_split_path_info ^/test(.+?\.php)(.*)$;
    set $orig_path $fastcgi_path_info;

    try_files $fastcgi_script_name =404;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $orig_path;
    fastcgi_param PATH_TRANSLATED $document_root$orig_path;
}

Auf diese Weise können Sie Ihre Anwendungen ausführen, die den ParameterPATH_INFOicher verwenden. Denken Sie daran, dass Sie die Optioncgi.fix_pathinfoin Ihrerphp.ini-Datei auf "1" ändern müssen, damit dies ordnungsgemäß funktioniert. Möglicherweise müssen Sie auch diesecurity.limit_extensions in Ihrerphp-fpm.conf-Datei deaktivieren.

Fazit

Hoffentlich haben Sie jetzt ein besseres Verständnis für die FastCGI-Proxy-Funktionen von Nginx. Dank dieser Fähigkeit kann Nginx seine Stärken in der schnellen Verbindungsverarbeitung und der Bereitstellung statischer Inhalte ausüben und gleichzeitig die Verantwortung für dynamische Inhalte auf besser geeignete Software verlagern. Mit FastCGI kann Nginx mit einer Vielzahl von Anwendungen in Konfigurationen arbeiten, die performant und sicher sind.