BLOG | NGINX

Von OpenAPI zu NGINX als API-Gateway mithilfe einer deklarativen API

Fabrizio Fiorucci Miniaturbild
Fabrizio Fiorucci
Veröffentlicht am 17. Juni 2024

NGINX gilt seit langem als eine der besten Optionen für den Betrieb leistungsstarker Webserver und Lastenausgleichsmodule. Mit dem Aufkommen von Microservices-Architekturen und dem Bedarf an effizienter API-Verwaltung ist NGINX jedoch auch eine beliebte Wahl für den Aufbau von API-Gateways geworden.

In diesem Blog untersuchen wir, wie eine OpenAPI-Schemadefinition in eine voll funktionsfähige NGINX-Konfiguration umgewandelt wird, die als API-Gateway mit Web Application Firewall- Sicherheit und Entwicklerportal unter Verwendung eines deklarativen API-Ansatzes ausgeführt wird.

Wir bieten Ihnen Schritt-für-Schritt-Anleitungen und Einblicke, wie Sie NGINX Plus nutzen können, um Ihre API-Verwaltungsprozesse zu optimieren und eine optimale Leistung für Ihre Anwendungen sicherzustellen.

Einführung des API-Gateways

Ein API-Gateway dient als zentraler Hub zum Verwalten und Sichern der Kommunikation zwischen Clients und Backend-Diensten. Es fungiert als Reverse-Proxy, der zwischen dem Client und den Backend-Servern sitzt, eingehende Anfragen weiterleitet und an die entsprechenden Dienste verteilt. Dies ermöglicht eine effizientere Kommunikation zwischen dem Client und den Diensten und ermöglicht es dem API-Gateway, Aufgaben wie Authentifizierung , Autorisierung , Ratenbegrenzung und Caching zu übernehmen.

Darüber hinaus kann ein API-Gateway als Sicherheitsebene fungieren und die Backend-Dienste vor potenziellen Bedrohungen und Angriffen schützen. Es kann Sicherheitsmaßnahmen wie Verschlüsselung, tokenbasierte Authentifizierung und Zugriffskontrolle durchsetzen und so sicherstellen, dass nur autorisierte Benutzer auf die Dienste zugreifen können. Durch die Konsolidierung und Verwaltung dieser Sicherheitsfunktionen an einem einzigen Ort trägt das API-Gateway dazu bei, die allgemeine Sicherheitsarchitektur des Systems zu vereinfachen und die Komplexität der Implementierung von Sicherheitsmaßnahmen über mehrere Dienste hinweg zu verringern.

NGINX Declarative API-Projekt

Das von der Community unterstützte NGINX Declarative API- Projekt bietet eine Reihe deklarativer REST-APIs für den NGINX Instance Manager .

Es kann verwendet werden, um NGINX Plus-Konfigurationslebenszyklen zu verwalten und NGINX Plus-Konfigurationen mithilfe von JSON-Dienstdefinitionen zu erstellen. Bei Verwendung mit NGINX Instance Manager wird die GitOps-Integration unterstützt: Die Quelle der Wahrheit wird auf Aktualisierungen referenzierter Objekte überprüft und NGINX-Konfigurationen werden automatisch synchronisiert.

OpenAPI-Schemas können verwendet werden, um NGINX automatisch als API-Gateway zu konfigurieren. Die Erstellung von Entwicklerportalen wird durch Redocly unterstützt.

Voraussetzungen

Um die Inhalte dieses Blogs auszuführen, benötigen Sie:

  • Eine laufende Instanz von NGINX Instance Manager
  • Ein Abonnement (oder eine 30-tägige kostenlose Testversion ) für NGINX Plus und NGINX App Protect WAF . NGINX Agent muss installiert sein, um eine Verbindung zum NGINX Instance Manager herzustellen und Teil der declarativeAPITest- Instanzgruppe zu sein
  • Ein Linux-Host (Bare Metal oder VM) mit Docker und Docker-Compose zum Ausführen des NGINX Declarative API-Projekts
  • Postman zum Senden der deklarativen API-Anfragen
  • Ein Client-Host zum Ausführen von Postman

Laborübersicht

Nach der Installation und Ausführung aller Voraussetzungen zeigt der NGINX Instance Manager die NGINX Plus-Instanz mit NGINX App Protect WAF als online an.

Die NGINX Plus-Instanz ist Teil der declarativeAPITest -Instanzgruppe

Bereitstellung der deklarativen API

Das NGINX Declarative API-Projekt basiert auf der vom NGINX Instance Manager bereitgestellten REST-API und bietet die deklarative, JSON-basierte Abstraktion. Befolgen Sie diese Anweisungen, um das deklarative API-Projekt auszuführen:

1. Führen Sie „docker ps“ aus, um zu überprüfen, ob Docker ausgeführt wird:


f5@ubuntu:~$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES


2. Klonen Sie auf dem Linux-Host das Github-Repository:


f5@ubuntu:~$ git clone https://github.com/f5devcentral/NGINX-Declarative-API/
Cloning into 'NGINX-Declarative-API'...
remote: Enumerating objects: 4072, done.
remote: Counting objects: 100% (1982/1982), done.
remote: Compressing objects: 100% (1332/1332), done.
remote: Total 4072 (delta 668), reused 876 (delta 609), pack-reused 2090
Receiving objects: 100% (4072/4072), 19.05 MiB | 4.88 MiB/s, done.
Resolving deltas: 100% (1154/1154), done.
f5@ubuntu:~$


3. Wechseln Sie in das Docker-Compose-Verzeichnis:


f5@ubuntu:~$ cd NGINX-Declarative-API/contrib/docker-compose/


4. Verwenden Sie das Skript nginx-dapi.sh, um alle Container über Docker-Compose zu starten. Beim ersten Start werden alle Docker-Images automatisch erstellt:


f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ ./nginx-dapi.sh -c start
-> Updating docker images
[+] Pulling 11/11
[...]
-> Deploying NGINX Declarative API
[+] Running 4/4
 ✔ Network nginx-dapi_dapi-network  Created 0.1s 
 ✔ Container redis                  Started 1.5s 
 ✔ Container devportal              Started 1.5s 
 ✔ Container nginx-dapi             Started


5. Überprüfen Sie laufende Docker-Container:


f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ docker ps
CONTAINER ID   IMAGE                             COMMAND                  CREATED         STATUS         PORTS                                       NAMES
e29a2f783da2   nginx-declarative-api             "/deployment/env/bin…"   5 minutes ago   Up 5 minutes   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   nginx-dapi
97142840eaf7   redis                             "docker-entrypoint.s…"   5 minutes ago   Up 5 minutes   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   redis
6b50c0426643   nginx-declarative-api-devportal   "/deployment/src/sta…"   5 minutes ago   Up 5 minutes   0.0.0.0:5001->5000/tcp, :::5001->5000/tcp   devportal


6. Führen Sie auf dem Client-Host Postman aus und importieren Sie die NGINX Declarative API-Sammlung unter https://raw.githubusercontent.com/f5devcentral/NGINX-Declarative-API/main/contrib/postman/NGINX%20Declarative%20API.postman_collection.json.

7. Bearbeiten Sie die Postman-Sammlungsvariablen, um sie an Ihre Umgebung anzupassen:

8. Legen Sie die folgenden Variablen fest:

  • ncg_host - Hostname oder IP-Adresse des Linux-Hosts, auf dem die deklarative API Docker-Compose ausgeführt wird
  • ncg_port – TCP-Port für die NGINX Declarative API: 5000 ist der Standardwert
  • nim_host – Basis-URL des NGINX Instance Managers (z. B. https://nms.k8s.ie.ff.lan)
  • nim_username – NGINX Instance Manager-Authentifizierungsbenutzername
  • nim_password - Authentifizierungskennwort für NGINX Instance Manager

9. Alle Änderungen in Postman speichern

10. Navigieren Sie in der Postman-Sammlung zu Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF und öffnen Sie die Anfrage

Die JSON-Deklaration sieht wie folgt aus:


{
    "output": {
        "type": "nms",
        "nms": {
            "url": "{{nim_host}}",
            "username": "{{nim_username}}",
            "password": "{{nim_password}}",
            "instancegroup": "{{nim_instancegroup}}",
            "synctime": 0,
            "modules": [
                "ngx_http_app_protect_module"
            ],
            "certificates": [
                {
                    "type": "certificate",
                    "name": "test_cert",
                    "contents": {
                        "content": "{{github_gitops_root}}/v4.2/testcert.crt"
                    }
                },
                {
                    "type": "key",
                    "name": "test_key",
                    "contents": {
                        "content": "{{github_gitops_root}}/v4.2/testcert.key"
                    }
                }
            ],
            "policies": [
                {
                    "type": "app_protect",
                    "name": "production-policy",
                    "active_tag": "xss-blocked",
                    "versions": [
                        {
                            "tag": "xss-blocked",
                            "displayName": "Production Policy - XSS blocked",
                            "description": "This is a production-ready policy - XSS blocked",
                            "contents": {
                                "content": "{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json"
                            }
                        },
                        {
                            "tag": "xss-allowed",
                            "displayName": "Production Policy - XSS allowed",
                            "description": "This is a production-ready policy - XSS allowed",
                            "contents": {
                                "content": "{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json"
                            }
                        }
                    ]
                }
            ]
        }
    },
    "declaration": {
        "http": {
            "servers": [
                {
                    "name": "Petstore API",
                    "names": [
                        "apigw.nginx.lab"
                    ],
                    "resolver": "8.8.8.8",
                    "listen": {
                        "address": "0.0.0.0:443",
                        "http2": true,
                        "tls": {
                            "certificate": "test_cert",
                            "key": "test_key",
                            "ciphers": "DEFAULT",
                            "protocols": [
                                "TLSv1.2",
                                "TLSv1.3"
                            ]
                        }
                    },
                    "log": {
                        "access": "/var/log/nginx/apigw.nginx.lab-access_log",
                        "error": "/var/log/nginx/apigw.nginx.lab-error_log"
                    },
                    "locations": [
                        {
                            "uri": "/petstore",
                            "urimatch": "prefix",
                            "apigateway": {
                                "openapi_schema": {
                                    "content": "http://petstore.swagger.io/v2/swagger.json"
                                },
                                "api_gateway": {
                                    "enabled": true,
                                    "strip_uri": true,
                                    "server_url": "https://petstore.swagger.io/v2"
                                },
                                "developer_portal": {
                                    "enabled": true,
                                    "uri": "/petstore-devportal.html"
                                },
                                "authentication": {
                                    "client": [
                                        {
                                            "profile": "Petstore JWT Authentication"
                                        }
                                    ],
                                    "enforceOnPaths": true,
                                    "paths": [
                                        "/user/login",
                                        "/user/logout"
                                    ]
                                },
                                "authorization": [
                                    {
                                        "profile": "JWT role based authorization",
                                        "enforceOnPaths": true,
                                        "paths": [
                                            "/user/login",
                                            "/user/logout"
                                        ]
                                    }
                                ],
                                "rate_limit": [
                                    {
                                        "profile": "petstore_ratelimit",
                                        "httpcode": 429,
                                        "burst": 0,
                                        "delay": 0,
                                        "enforceOnPaths": true,
                                        "paths": [
                                            "/user/login",
                                            "/user/logout"
                                        ]
                                    }
                                ]
                            },
                            "log": {
                                "access": "/var/log/nginx/petstore-access_log",
                                "error": "/var/log/nginx/petstore-error_log"
                            },
                            "app_protect": {
                                "enabled": true,
                                "policy": "production-policy",
                                "log": {
                                    "profile_name": "secops_dashboard",
                                    "enabled": true,
                                    "destination": "127.0.0.1:514"
                                }
                            }
                        }
                    ]
                }
            ],
            "rate_limit": [
                {
                    "name": "petstore_ratelimit",
                    "key": "$binary_remote_addr",
                    "size": "10m",
                    "rate": "2r/s"
                }
            ],
            "authentication": {
                "client": [
                    {
                        "name": "Petstore JWT Authentication",
                        "type": "jwt",
                        "jwt": {
                            "realm": "Petstore Authentication",
                            "key": "{\"keys\": [{\"k\":\"ZmFudGFzdGljand0\",\"kty\":\"oct\",\"kid\":\"0001\"}]}",
                            "cachetime": 5
                        }
                    }
                ]
            },
            "authorization": [
                {
                    "name": "JWT role based authorization",
                    "type": "jwt",
                    "jwt": {
                        "claims": [
                            {
                                "name": "roles",
                                "value": [
                                    "~(devops)"
                                ],
                                "errorcode": 403
                            }
                        ]
                    }
                }
            ]
        }
    }
}


Der Ausgabeabschnitt definiert:

  • Der NGINX Instance Manager-Server und die Declarative API veröffentlichen die NGINX-Konfiguration an
  • TLS-Zertifikate und -Schlüssel - diese können über die URL der Quelle der Wahrheit referenziert werden, in der sie gespeichert sind
  • NGINX App schützt WAF-Sicherheitsrichtlinien - diese können über die URL der Quelle der Wahrheit referenziert werden, in der sie gespeichert sind

Der Deklarationsabschnitt beschreibt:

  • Der zu erstellende NGINX-Server
  • Ob TLS-Offload durchgeführt wird oder nicht
  • Wo werden Zugriffe und Fehlereinträge protokolliert?
  • Die /petstore- Basis-URI, unter der die API-Gateway-Konfiguration bereitgestellt und für Clients zugänglich gemacht wird
  • Die API Gateway-Konfiguration und das Entwicklerportal werden veröffentlicht
  • Wie NGINX App Protect WAF aktiviert wird, welche Sicherheitsrichtlinie zu verwenden ist und wo Sicherheitsverletzungen protokolliert werden
  • So authentifizieren und autorisieren Sie Clientanforderungen

Der Abschnitt „API Gateway-Deklaration“ beschreibt, wie die deklarative API ihr Ergebnis liefert.

Auf das OpenAPI-Schema wird über seine vollständige URL verwiesen:


"apigateway": {
    "openapi_schema": {
        "content": "http://petstore.swagger.io/v2/swagger.json"
    },


Die Erstellung der NGINX API Gateway-Konfiguration wird angefordert und der Upstream-Server wird definiert. Wenn NGINX Anfragen per Reverse-Proxy an den Upstream weiterleitet, wird die /petstore- Basis-URI entfernt:


"api_gateway": {
        "enabled": true,
        "strip_uri": true,
        "server_url": "https://petstore.swagger.io/v2"
    },




Die Erstellung eines Entwicklerportals und die Bereitstellung unter einer bestimmten URI sind erforderlich:


"developer_portal": {
        "enabled": true,
        "uri": "/petstore-devportal.html"
    },


Die Client-Authentifizierung basierend auf dem angegebenen Client-Authentifizierungsprofil wird für /user/login und /user/logout erzwungen:


"authentication": {
        "client": [
            {
                "profile": "Petstore JWT Authentication"
            }
        ],
        "enforceOnPaths": true,
        "paths": [
            "/user/login",
            "/user/logout"
        ]
    },


Die Client-Autorisierung basierend auf dem angegebenen Client-Autorisierungsprofil wird für /user/logout und /user/logout erzwungen:


"authorization": [
        {
            "profile": "JWT role based authorization",
            "enforceOnPaths": true,
            "paths": [
                "/user/login",
                "/user/logout"
            ]
        }
    ],




Die Ratenbegrenzung wird für /user /logout basierend auf dem angegebenen Profil angewendet:


"rate_limit": [
        {
            "profile": "petstore_ratelimit",
            "httpcode": 429,
            "burst": 0,
            "delay": 0,
            "enforceOnPaths": true,
            "paths": [
                "/user/login",
                "/user/logout"
            ]
        }
    ]
},


12. Verwenden Sie die Schaltfläche „Senden“ von Postman, um die Anforderung an die deklarative API zu veröffentlichen. Die Antwort sieht ungefähr so aus:


{
    "code": 200,
    "content": {
        "createTime": "2024-04-26T17:09:10.419574328Z",
        "details": {
            "failure": [],
            "pending": [],
            "success": [
                {
                    "name": "vm-test"
                }
            ]
        },
        "id": "1060ec49-120e-45ca-820b-5203c8b3538d",
        "message": "Instance Group config successfully published to declarativeAPITest",
        "status": "successful",
        "updateTime": "2024-04-26T17:09:10.881509913Z"
    },
    "configUid": "eecf1da6-9d8f-4e44-89cc-a470af79379d"
}


13. In dieser Phase wird die NGINX-Instanz als API-Gateway konfiguriert, die WAF-Sicherheit erzwungen und das Entwicklerportal veröffentlicht.

Testen des API-Gateways

Hinweis: Es wird davon ausgegangen, dass der FQDN apigw.nginx.lab in die IP-Adresse der virtuellen Maschine aufgelöst wird, auf der die NGINX-Instanz ausgeführt wird

1. Wechseln Sie in das jwt -Verzeichnis:


f5@ubuntu:~$ cd ~/NGINX-Declarative-API/contrib/gitops-examples/jwt


2. Greifen Sie auf einen nicht authentifizierten REST-API-Endpunkt zu:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/store/inventory
HTTP/2 200 
date: Fri, 26 Apr 2024 17:13:54 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization

{"totvs":5,"aut":1,"FORsold":1,[...]



3. Ratenbegrenzung:

    
    $ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login;curl -w '\n' -ki 
    https://apigw.nginx.lab/petstore/user/login
    HTTP/2 401 
    date: Fri, 26 Apr 2024 17:14:51 GMT
    content-type: text/html
    content-length: 179
    www-authenticate: Bearer realm="Petstore Authentication"
    <html>
    <head><title>401 Authorization Required</title></head>
    <body>
    <center><h1>401 Authorization Required</h1></center>
    <hr><center>nginx/1.25.3</center>
    </body>
    </html>
    HTTP/2 429 
    date: Fri, 26 Apr 2024 17:14:51 GMT
    content-type: text/html
    content-length: 169
    <html>
    <head><title>429 Too Many Requests</title></head>
    <body>
    <center><h1>429 Too Many Requests</h1></center>
    <hr><center>nginx/1.25.3</center>
    </body>
    </html>
    

4. Authentifizierung und gültige Autorisierung:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.devops`"
HTTP/2 200 
date: Fri, 26 Apr 2024 17:15:41 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
x-expires-after: Fri Apr 26 18:15:41 UTC 2024
x-rate-limit: 5000

{"code":200,"type":"unknown","message":"logged in user session:1714151741883"}



5. Authentifizierung und ungültige Autorisierung:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.guest`"
HTTP/2 403 
date: Fri, 26 Apr 2024 17:16:07 GMT
content-type: text/html
content-length: 153
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>


6. Sicherheitsverletzung bei NGINX App Protect WAF und Cross-Site-Scripting:


$ curl -w '\n' -ki "https://apigw.nginx.lab/petstore/store/inventory?
"
HTTP/2 200 
content-type: text/html; charset=utf-8
cache-control: no-cache
pragma: no-cache
content-length: 246
<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.<br><br>Your support ID is: 7283327928460093545<br><br><a href='javascript:history.back();'>[Go Back]</a></body></html>

7. Das Entwicklerportal erreichen Sie über


https://apigw.nginx.lab/petstore/petstore-devportal.html


Erste Schritte

Um die in diesem Beitrag besprochenen NGINX-Lösungen auszuprobieren, starten Sie noch heute eine 30-tägige kostenlose Testversion oder kontaktieren Sie uns , um Ihre Anwendungsfälle zu besprechen:

Laden Sie NGINX Agent herunter – es ist kostenlos und Open Source.


„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."