BLOG | NGINX

Bereitstellen von NGINX als API-Gateway, Teil 1

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Liam Crilly Miniaturbild
Liam Crilly
Veröffentlicht am 20. Januar 2021

Dies ist der erste Blogbeitrag unserer Reihe zur Bereitstellung von NGINX Open Source und NGINX Plus als API-Gateway:

  • Dieser Beitrag enthält detaillierte Konfigurationsanweisungen für mehrere Anwendungsfälle. Es wurde ursprünglich im Jahr 2018 veröffentlicht und aktualisiert, um die aktuellen Best Practices für die API-Konfiguration widerzuspiegeln, indem verschachtelte Standortblöcke zum Weiterleiten von Anfragen verwendet werden , anstatt Regeln neu zu schreiben.
  • Teil 2 erweitert diese Anwendungsfälle und befasst sich mit einer Reihe von Sicherheitsvorkehrungen, die zum Schutz und zur Sicherung von Back-End-API-Diensten in der Produktion angewendet werden können.
  • Teil 3 erläutert, wie NGINX Open Source und NGINX Plus als API-Gateway für gRPC-Dienste bereitgestellt werden.

Notiz : Sofern nicht anders angegeben, gelten alle Informationen in diesem Beitrag sowohl für NGINX Open Source als auch für NGINX Plus. Der Einfachheit halber wird im Rest des Blogs einfach von „NGINX“ gesprochen.

Das Herzstück moderner Anwendungsarchitekturen ist die HTTP-API. HTTP ermöglicht die schnelle Erstellung und einfache Wartung von Anwendungen. Die HTTP-API bietet eine gemeinsame Schnittstelle, unabhängig vom Umfang der Anwendung, vom Mikrodienst für einen einzigen Zweck bis zum allumfassenden Monolithen. Durch die Verwendung von HTTP können die Fortschritte bei der Bereitstellung von Webanwendungen, die Hyperscale-Interneteigenschaften unterstützen, auch genutzt werden, um eine zuverlässige und leistungsstarke API-Bereitstellung zu gewährleisten.

Eine hervorragende Einführung in die Bedeutung von API-Gateways für Microservices-Anwendungen finden Sie unter „Building Microservices“: Verwenden eines API-Gateways in unserem Blog.

Als führender leistungsstarker und leichter Reverse-Proxy und Load Balancer verfügt NGINX über die erweiterten HTTP-Verarbeitungsfunktionen, die für die Handhabung des API-Verkehrs erforderlich sind. Dies macht NGINX zur idealen Plattform für den Aufbau eines API-Gateways. In diesem Blogbeitrag beschreiben wir eine Reihe gängiger API-Gateway-Anwendungsfälle und zeigen, wie NGINX so konfiguriert wird, dass diese effizient, skalierbar und leicht zu warten sind. Wir beschreiben eine vollständige Konfiguration, die die Grundlage für eine Produktionsbereitstellung bilden kann.

Einführung der Warehouse-API

Die Hauptfunktion des API-Gateways besteht darin, einen einzigen, konsistenten Einstiegspunkt für mehrere APIs bereitzustellen, unabhängig davon, wie sie im Backend implementiert oder bereitgestellt werden. Nicht alle APIs sind Microservices-Anwendungen. Unser API-Gateway muss vorhandene APIs, Monolithen und Anwendungen verwalten, die teilweise auf Microservices umgestellt werden.

In diesem Blogbeitrag beziehen wir uns auf eine hypothetische API für die Bestandsverwaltung, die „Warehouse-API“. Wir verwenden Beispielkonfigurationscode, um verschiedene Anwendungsfälle zu veranschaulichen. Die Warehouse-API ist eine RESTful-API, die JSON-Anfragen verarbeitet und JSON-Antworten erzeugt. Die Verwendung von JSON stellt jedoch bei der Bereitstellung als API-Gateway für NGINX weder eine Einschränkung noch eine Anforderung dar. NGINX ist unabhängig vom Architekturstil und den Datenformaten, die von den APIs selbst verwendet werden.

Die Warehouse-API wird als Sammlung diskreter Microservices implementiert und als einzelne API veröffentlicht. Die Inventar- und Preisressourcen werden als separate Dienste implementiert und auf verschiedenen Backends bereitgestellt. Die Pfadstruktur der API ist also:    

API
└── Lager
├── Inventar
└── Preisgestaltung

Um beispielsweise den aktuellen Lagerbestand abzufragen, sendet eine Client-Anwendung eine HTTP- GET- Anfrage an /api/warehouse/inventory .

API-Gateway-Architektur für mehrere Anwendungen

Organisieren der NGINX-Konfiguration

Ein Vorteil der Verwendung von NGINX als API-Gateway besteht darin, dass es diese Rolle erfüllen und gleichzeitig als Reverse-Proxy, Load Balancer und Webserver für vorhandenen HTTP-Verkehr fungieren kann. Wenn NGINX bereits Teil Ihres Anwendungsbereitstellungsstapels ist, ist es im Allgemeinen nicht erforderlich, ein separates API-Gateway bereitzustellen. Allerdings unterscheidet sich das erwartete Standardverhalten eines API‑Gateways teilweise von dem für browserbasierten Datenverkehr erwarteten Verhalten. Aus diesem Grund trennen wir die API-Gateway-Konfiguration von allen bestehenden (oder zukünftigen) Konfigurationen für browserbasierten Datenverkehr.

Um diese Trennung zu erreichen, erstellen wir ein Konfigurationslayout, das eine Mehrzweck-NGINX-Instanz unterstützt und eine praktische Struktur für die Automatisierung der Konfigurationsbereitstellung durch CI/CD-Pipelines bietet. Die resultierende Verzeichnisstruktur unter /etc/nginx sieht folgendermaßen aus.

etc/
└── nginx/
├── api_conf.d/ ………………………………… Unterverzeichnis für die Konfiguration pro API
│ └── warehouse_api.conf …… Definition und Richtlinie der Warehouse-API
├── api_backends.conf ………………… Die Backend-Dienste (Upstreams)
├── api_gateway.conf …………………… Konfiguration der obersten Ebene für den API-Gateway-Server
├── api_json_errors.conf ………… HTTP-Fehlerantworten im JSON-Format
├── conf.d/
│ ├── ...
│ └── existing_apps.conf
└── nginx.conf

Die Verzeichnisse und Dateinamen für alle API-Gateway-Konfigurationen haben das Präfix api_ . Jede dieser Dateien und Verzeichnisse aktiviert eine andere Funktion oder Fähigkeit des API-Gateways, wie unten ausführlich erläutert. Die Datei warehouse_api.conf ist ein generischer Ersatz für die unten beschriebenen Konfigurationsdateien, die die Warehouse-API auf unterschiedliche Weise definieren.

Definieren des API-Gateways der obersten Ebene

Die gesamte NGINX-Konfiguration beginnt mit der Hauptkonfigurationsdatei nginx.conf . Um die API-Gateway-Konfiguration einzulesen, fügen wir im http- Block in nginx.conf eine Include -Direktive hinzu, die auf die Datei mit der Gateway-Konfiguration, api_gateway.conf (Zeile 28 direkt darunter), verweist. Beachten Sie, dass die Standarddatei nginx.conf eine Include- Direktive verwendet, um die browserbasierte HTTP-Konfiguration aus dem Unterverzeichnis conf.d abzurufen (Zeile 29). In diesem Blogbeitrag wird die Include- Direktive in großem Umfang verwendet, um die Lesbarkeit zu verbessern und die Automatisierung einiger Teile der Konfiguration zu ermöglichen.

 

Die Datei api_gateway.conf definiert den virtuellen Server, der NGINX als API-Gateway für Clients verfügbar macht. Diese Konfiguration stellt alle vom API-Gateway veröffentlichten APIs an einem einzigen Einstiegspunkt bereit, https://api.example.com/ (Zeile 9), geschützt durch TLS, wie in den Zeilen 12 bis 17 konfiguriert. Beachten Sie, dass diese Konfiguration rein HTTPS ist – es gibt keinen HTTP-Listener im Klartext. Wir erwarten, dass API-Clients den richtigen Einstiegspunkt kennen und standardmäßig HTTPS-Verbindungen herstellen.

Diese Konfiguration soll statisch sein – die Details der einzelnen APIs und ihrer Backend-Dienste werden in den Dateien angegeben, auf die die Include- Direktive in Zeile 20 verweist. Die Zeilen 23 bis 26 befassen sich mit der Fehlerbehandlung und werden weiter unten im Abschnitt „Reagieren auf Fehler“ erläutert.

 

Einzeldienst vs. Microservice-API-Backends

Einige APIs können in einem einzigen Backend implementiert werden, obwohl wir aus Gründen der Ausfallsicherheit oder des Lastausgleichs normalerweise davon ausgehen, dass es mehr als eines gibt. Mit Microservices-APIs definieren wir für jeden Dienst einzelne Backends. Zusammen fungieren sie als vollständige API. Hier wird unsere Warehouse-API als zwei separate Dienste mit jeweils mehreren Backends bereitgestellt.

 

Alle Backend-API-Dienste für alle vom API-Gateway veröffentlichten APIs sind in api_backends.conf definiert. Hier verwenden wir mehrere IP-Adress-Port-Paare in jedem Upstream- Block, um anzugeben, wo der API-Code bereitgestellt wird. Es können aber auch Hostnamen verwendet werden. NGINX Plus-Abonnenten können außerdem die Vorteile des dynamischen DNS-Lastausgleichs nutzen, um neue Backends automatisch zur Laufzeitkonfiguration hinzuzufügen.

Definieren der Warehouse-API

Die Warehouse-API wird durch eine Reihe von Standortblöcken in einer verschachtelten Konfiguration definiert, wie im folgenden Beispiel dargestellt. Der äußere Standortblock ( /api/warehouse ) identifiziert den Basispfad, unter dem verschachtelte Standorte die gültigen URIs angeben, die an die Back-End-API-Dienste weitergeleitet werden. Durch die Verwendung eines äußeren Blocks können wir allgemeine Richtlinien definieren, die für die gesamte API gelten (in diesem Beispiel die Protokollierungskonfiguration in Zeile 6).

 

NGINX verfügt über ein äußerst effizientes und flexibles System zum Zuordnen der Anforderungs-URI zu einem Abschnitt der Konfiguration. Die Reihenfolge der Standortanweisungen ist nicht wichtig. Es wird die spezifischste Übereinstimmung ausgewählt. Hier definieren die verschachtelten Standorte in den Zeilen 10 und 14 zwei URIs, die spezifischer sind als der äußere Standortblock . Die Proxy_Pass- Direktive in jedem verschachtelten Block leitet Anforderungen an die entsprechende Upstream-Gruppe weiter. Die Richtlinienkonfiguration wird vom äußeren Standort übernommen, sofern nicht die Notwendigkeit besteht, für bestimmte URIs eine spezifischere Richtlinie bereitzustellen.

Alle URIs, die nicht mit einem der verschachtelten Standorte übereinstimmen, werden vom äußeren Standort verarbeitet, der eine Catch-All-Direktive (Zeile 18) enthält, die die Antwort zurückgibt 404(Nicht gefunden) für alle ungültigen URIs.

Die Wahl zwischen „Breit“ und „Weit“ Präzise Definition für APIs

Es gibt zwei Ansätze zur API-Definition – allgemein und präzise. Der am besten geeignete Ansatz für jede API hängt von den Sicherheitsanforderungen der API ab und davon, ob es wünschenswert ist, dass die Back-End-Dienste ungültige URIs verarbeiten.

In der Datei warehouse_api_simple.conf oben verwenden wir den allgemeinen Ansatz für die Warehouse-API und definieren URI-Präfixe in den Zeilen 10 und 14, sodass ein URI, der mit einem der Präfixe beginnt, an den entsprechenden Backend-Dienst weitergeleitet wird. Mit diesem umfassenden, präfixbasierten Standortabgleich sind alle API-Anfragen an die folgenden URIs gültig:

/API/Lager/Inventar
/API/Lager/Inventar/
/API/Lager/Inventar/foo
/api/warehouse/inventarfoo
/api/lager/inventarfoo/bar/

Wenn die einzige Überlegung darin besteht, jede Anfrage an den richtigen Backend-Dienst weiterzuleiten, bietet der umfassende Ansatz die schnellste Verarbeitung und kompakteste Konfiguration. Ein präziserer Ansatz hingegen ermöglicht es dem API-Gateway, den gesamten URI-Raum der API zu verstehen, indem der URI-Pfad für jede verfügbare API-Ressource explizit definiert wird. Die folgende Konfiguration für das URI-Routing in der Warehouse-API verfolgt den präzisen Ansatz und verwendet eine Kombination aus exakter Übereinstimmung ( = ) und regulären Ausdrücken ( ~ ), um jede einzelne gültige URI zu definieren.

 

Diese Konfiguration ist ausführlicher, beschreibt jedoch die von den Backend-Diensten implementierten Ressourcen genauer. Dies hat den Vorteil, dass die Backend-Dienste vor fehlerhaften Client-Anfragen geschützt werden, allerdings auf Kosten eines geringen zusätzlichen Aufwands für die reguläre Ausdrucksübereinstimmung. Mit dieser Konfiguration akzeptiert NGINX einige URIs und lehnt andere als ungültig ab:

Gültige URIs   Ungültige URIs
/API/Lager/Inventar   /API/Lager/Inventar/
/API/Lager/Inventar/Regal/foo   /api/warehouse/inventarfoo
/API/Lager/Inventar/Regal/Foo/Box/Bar   /API/Lager/Inventar/Regal
/API/Lager/Inventar/Regal/-/Box/-   /API/Lager/Inventar/Regal/Foo/Bar
/api/warehouse/preise/baz   /API/Warehouse/Preise
    /api/warehouse/preise/baz/pub

Durch die Verwendung einer präzisen API-Definition können vorhandene API-Dokumentationsformate die Konfiguration des API-Gateways steuern. Es ist möglich, die NGINX-API-Definitionen aus der OpenAPI-Spezifikation (früher Swagger genannt) zu automatisieren. Ein Beispielskript zu diesem Zweck finden Sie in den Gists zu diesem Blogbeitrag.

Umschreiben von Clientanforderungen zur Verarbeitung schwerwiegender Änderungen

Im Zuge der Weiterentwicklung von APIs ist es manchmal erforderlich, Änderungen vorzunehmen, die die strikte Abwärtskompatibilität aufheben und eine Aktualisierung der Clients erfordern. Ein solches Beispiel ist, wenn eine API-Ressource umbenannt oder verschoben wird. Im Gegensatz zu einem Webbrowser kann ein API-Gateway seinen Clients keine Umleitung (Code301 ( Dauerhaft verschoben) ) mit der Angabe des neuen Standorts. Wenn es unpraktisch ist, API-Clients zu ändern, können wir Client-Anfragen glücklicherweise spontan umschreiben.

Im folgenden Beispiel verwenden wir denselben allgemeinen Ansatz wie in warehouse_api_simple.conf oben, aber in diesem Fall ersetzt die Konfiguration eine frühere Version der Warehouse-API, in der der Preisdienst als Teil des Inventardienstes implementiert wurde. Die Umschreibedirektive in Zeile 3 wandelt Anfragen an die alte Preisressource in Anfragen an den neuen Preisdienst um.

 

Auf Fehler reagieren

Einer der Hauptunterschiede zwischen HTTP-APIs und browserbasiertem Datenverkehr besteht in der Art und Weise, wie Fehler an den Client übermittelt werden. Wenn NGINX als API-Gateway bereitgestellt wird, konfigurieren wir es so, dass Fehler auf eine Weise zurückgegeben werden, die für die API-Clients optimal ist.

Die API-Gateway-Konfiguration der obersten Ebene enthält einen Abschnitt, der definiert, wie mit Fehlerantworten umgegangen wird.

 

Die error_page- Direktive in Zeile 23 gibt an, dass NGINX, wenn eine Anfrage keiner der API-Definitionen entspricht, die 400( Ungültige Anfrage ) Fehler anstelle des Standard 404( Nicht gefunden ) Fehler. Dieses (optionale) Verhalten erfordert, dass API-Clients nur Anfragen an die gültigen URIs stellen, die in der API-Dokumentation enthalten sind, und verhindert, dass nicht autorisierte Clients die URI-Struktur der über das API-Gateway veröffentlichten APIs entdecken.

Zeile 24 bezieht sich auf Fehler, die von den Backend-Diensten selbst generiert werden . Nicht behandelte Ausnahmen können Stapeltraces oder andere vertrauliche Daten enthalten, die nicht an den Client gesendet werden sollen. Diese Konfiguration fügt eine weitere Schutzebene hinzu, indem eine standardisierte Fehlerantwort an den Client gesendet wird.

Die vollständige Liste der standardisierten Fehlerantworten ist in einer separaten Konfigurationsdatei definiert, auf die durch die Include- Direktive in Zeile 25 verwiesen wird. Die ersten Zeilen dieser Datei werden unten angezeigt. Diese Datei kann geändert werden, wenn ein anderes Fehlerformat als JSON bevorzugt wird. Der Wert default_type in Zeile 26 von api_gateway.conf kann entsprechend geändert werden. Sie können auch eine separate Include- Direktive im Richtlinienabschnitt jeder API haben, um auf eine andere Datei mit Fehlerantworten zu verweisen, die die globalen Antworten überschreiben.

Mit dieser Konfiguration erhält eine Client-Anforderung für eine ungültige URI die folgende Antwort. [Terminal]$ curl -i https://api.example.com/foo HTTP/1.1 400 Ungültige Anforderung Server: nginx/1.19.5 Content-Type: application/json Content-Length: 39 Verbindung: Keep-Alive {"status":400,"message":"Ungültige Anfrage"}[/terminal]

Implementieren der Authentifizierung

Es ist ungewöhnlich, APIs ohne eine Form der Authentifizierung zu ihrem Schutz zu veröffentlichen. NGINX bietet mehrere Ansätze zum Schutz von APIs und zur Authentifizierung von API-Clients. Informationen zu Ansätzen, die auch für normale HTTP-Anfragen gelten, finden Sie in der Dokumentation zu IP-adressbasierten Zugriffskontrolllisten (ACLs), zur digitalen Zertifikatauthentifizierung und zur HTTP-Basisauthentifizierung . Hier konzentrieren wir uns auf API‑spezifische Authentifizierungsmethoden.

API-Schlüsselauthentifizierung

API-Schlüssel sind ein gemeinsames Geheimnis, das dem Client und dem API-Gateway bekannt ist. Ein API-Schlüssel ist im Wesentlichen ein langes und komplexes Passwort, das dem API-Client als langfristige Anmeldeinformation ausgestellt wird. Das Erstellen von API-Schlüsseln ist einfach: kodieren Sie einfach eine Zufallszahl wie in diesem Beispiel.

$ openssl rand -base64 18 7B5zIqmRGXmrJTFmKa99vcit

In Zeile 2 der API-Gateway-Konfigurationsdatei der obersten Ebene, api_gateway.conf , fügen wir eine Datei namens api_keys.conf ein, die einen API-Schlüssel für jeden API-Client enthält, der durch den Namen des Clients oder eine andere Beschreibung identifiziert wird. Hier ist der Inhalt dieser Datei:

Die API-Schlüssel werden innerhalb eines Kartenblocks definiert. Die Map- Direktive akzeptiert zwei Parameter. Das erste definiert, wo der API-Schlüssel zu finden ist, in diesem Fall im APIKEY -HTTP-Header der Client-Anforderung, wie in der Variable $http_apikey erfasst. Der zweite Parameter erstellt eine neue Variable ( $api_client_name ) und setzt sie auf den Wert des zweiten Parameters in der Zeile, in der der erste Parameter mit dem Schlüssel übereinstimmt.

Wenn ein Client beispielsweise den API-Schlüssel 7B5zIqmRGXmrJTFmKa99vcit vorlegt, wird die Variable $api_client_name auf client_one gesetzt. Mit dieser Variable können Sie nach authentifizierten Clients suchen und sie zur detaillierteren Überwachung in Protokolleinträge aufnehmen. Das Format des Map- Blocks ist einfach und lässt sich leicht in Automatisierungs-Workflows integrieren, die die Datei api_keys.conf aus einem vorhandenen Anmeldeinformationsspeicher generieren.

Hier aktivieren wir die API-Schlüsselauthentifizierung, indem wir die „allgemeine“ Konfiguration ( warehouse_api_simple.conf ) ändern, um eine auth_request- Direktive in den Richtlinienabschnitt aufzunehmen, die die Authentifizierungsentscheidung an einen angegebenen Ort delegiert.

 

Mit der Direktive auth_request (Zeile 7) können wir beispielsweise die Authentifizierung von einem externen Authentifizierungsserver wie OAuth 2.0 Token Introspection durchführen lassen. In diesem Beispiel fügen wir stattdessen die Logik zum Validieren von API-Schlüsseln zur API-Gateway-Konfigurationsdatei der obersten Ebene hinzu , und zwar in Form des folgenden Standortblocks namens /_validate_apikey .

 

Die interne Direktive in Zeile 30 bedeutet, dass auf diesen Speicherort nicht direkt von externen Clients zugegriffen werden kann (nur per auth_request ). Von den Clients wird erwartet, dass sie ihren API-Schlüssel im APIKEY -HTTP-Header angeben. Wenn dieser Header fehlt oder leer ist (Zeile 32), senden wir eine401 (Unautorisierte) Antwort, um dem Client mitzuteilen, dass eine Authentifizierung erforderlich ist. Zeile 35 behandelt den Fall, dass der API-Schlüssel keinem der Schlüssel im Map- Block entspricht. In diesem Fall setzt der Standardparameter in Zeile 2 von api_keys.conf $api_client_name auf eine leere Zeichenfolge. Wir senden eine403 (Verbotene) Antwort, um dem Client mitzuteilen, dass die Authentifizierung fehlgeschlagen ist. Wenn keine dieser Bedingungen zutrifft, ist der API-Schlüssel gültig und der Standort gibt einen 204(Kein Inhalt) Antwort.

Mit dieser Konfiguration implementiert die Warehouse-API jetzt die API-Schlüsselauthentifizierung.

$ curl https://api.example.com/api/warehouse/pricing/item001 {"status":401,"message":"Nicht autorisiert"} $ curl -H "apikey: thisIsInvalid" https://api.example.com/api/warehouse/pricing/item001 {"status":403,"message":"Verboten"} $ curl -H "apikey: 7B5zIqmRGXmrJTFmKa99vcit" https://api.example.com/api/warehouse/pricing/item001 {"sku":"item001","price":179.99}

JWT-Authentifizierung

JSON Web Tokens (JWTs) werden zunehmend zur API-Authentifizierung verwendet. Native JWT-Unterstützung ist exklusiv für NGINX Plus und ermöglicht die Validierung von JWTs, wie in „Authentifizieren von API-Clients mit JWT und NGINX Plus“ in unserem Blog beschrieben. Eine Beispielimplementierung finden Sie unter „Steuern des Zugriffs auf bestimmte Methoden“ in Teil 2.

Zusammenfassung

Dieser erste Blog einer Reihe beschreibt detailliert eine Komplettlösung für die Bereitstellung von NGINX Open Source und NGINX Plus als API-Gateway. Der komplette Satz der in diesem Blog besprochenen Dateien kann in unserem GitHub Gist-Repository überprüft und heruntergeladen werden.

Schauen Sie sich die anderen Beiträge dieser Reihe an:

  • Teil 2 untersucht fortgeschrittenere Anwendungsfälle zum Schutz von Backend-Diensten vor böswilligen oder sich schlecht verhaltenden Clients.
  • Teil 3 erläutert, wie NGINX als API-Gateway für gRPC-Dienste bereitgestellt wird.

Um NGINX Plus auszuprobieren, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen.


„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."