BLOG | NGINX

Déploiement de NGINX en tant que passerelle API, partie 1

NGINX-Partie-de-F5-horiz-black-type-RGB
Miniature de Liam Crilly
Liam Crilly
Publié le 20 janvier 2021

Il s'agit du premier article de blog de notre série sur le déploiement de NGINX Open Source et NGINX Plus en tant que passerelle API :

  • Cet article fournit des instructions de configuration détaillées pour plusieurs cas d'utilisation. Initialement publié en 2018, il a été mis à jour pour refléter les meilleures pratiques actuelles en matière de configuration d'API, en utilisant des blocs d'emplacement imbriqués pour acheminer les requêtes, au lieu de règles de réécriture.
  • La partie 2 étend ces cas d’utilisation et examine une gamme de mesures de protection qui peuvent être appliquées pour protéger et sécuriser les services API back-end en production.
  • La partie 3 explique comment déployer NGINX Open Source et NGINX Plus en tant que passerelle API pour les services gRPC.

Note : Sauf indication contraire, toutes les informations contenues dans cet article s’appliquent à la fois à NGINX Open Source et à NGINX Plus. Pour faciliter la lecture, le reste du blog fait simplement référence à « NGINX ».

Au cœur des architectures d'applications modernes se trouve l'API HTTP. HTTP permet de créer rapidement des applications et de les maintenir facilement. L'API HTTP fournit une interface commune, quelle que soit l'échelle de l'application, d'un microservice à usage unique à un monolithe englobant. En utilisant HTTP, les avancées en matière de diffusion d’applications Web qui prennent en charge les propriétés Internet à grande échelle peuvent également être utilisées pour fournir une diffusion d’API fiable et hautes performances.

Pour une excellente introduction à l’importance des passerelles API pour les applications de microservices, voir Création de microservices : Utilisation d'une passerelle API sur notre blog.

En tant que proxy inverse et équilibreur de charge léger et hautes performances, NGINX dispose des capacités de traitement HTTP avancées nécessaires à la gestion du trafic API. Cela fait de NGINX la plateforme idéale pour créer une passerelle API. Dans cet article de blog, nous décrivons un certain nombre de cas d'utilisation courants de passerelle API et montrons comment configurer NGINX pour les gérer de manière efficace, évolutive et facile à entretenir. Nous décrivons une configuration complète, qui peut constituer la base d'un déploiement de production.

Présentation de l'API Warehouse

La fonction principale de la passerelle API est de fournir un point d’entrée unique et cohérent pour plusieurs API, quelle que soit la manière dont elles sont implémentées ou déployées au niveau du backend. Toutes les API ne sont pas des applications de microservices. Notre passerelle API doit gérer les API, les monolithes et les applications existants subissant une transition partielle vers les microservices.

Dans cet article de blog, nous faisons référence à une API hypothétique pour la gestion des stocks, l'« API d'entrepôt ». Nous utilisons un exemple de code de configuration pour illustrer différents cas d'utilisation. L'API Warehouse est une API RESTful qui consomme des requêtes JSON et produit des réponses JSON. L'utilisation de JSON n'est cependant pas une limitation ou une exigence de NGINX lorsqu'il est déployé en tant que passerelle API ; NGINX est indépendant du style architectural et des formats de données utilisés par les API elles-mêmes.

L'API Warehouse est implémentée sous la forme d'un ensemble de microservices distincts et publiée sous la forme d'une API unique. Les ressources d'inventaire et de tarification sont implémentées sous la forme de services distincts et déployées sur différents backends. La structure du chemin de l’API est donc :    

API
└── entrepôt
├── inventaire
└── prix

À titre d’exemple, pour interroger l’inventaire actuel de l’entrepôt, une application cliente envoie une requête HTTP GET à /api/warehouse/inventory .

Architecture de passerelle API pour applications multiples

Organisation de la configuration NGINX

L’un des avantages de l’utilisation de NGINX comme passerelle API est qu’il peut remplir ce rôle tout en agissant simultanément comme proxy inverse, équilibreur de charge et serveur Web pour le trafic HTTP existant. Si NGINX fait déjà partie de votre pile de distribution d’applications, il est généralement inutile de déployer une passerelle API distincte. Cependant, certains comportements par défaut attendus d’une passerelle API diffèrent de ceux attendus pour le trafic basé sur un navigateur. C'est pour cette raison que nous séparons la configuration de la passerelle API de toute configuration existante (ou future) pour le trafic basé sur un navigateur.

Pour réaliser cette séparation, nous créons une disposition de configuration qui prend en charge une instance NGINX polyvalente et fournit une structure pratique pour automatiser le déploiement de la configuration via des pipelines CI/CD. La structure du répertoire résultant sous /etc/nginx ressemble à ceci.

etc/
└── nginx/
├── api_conf.d/ ………………………………… Sous-répertoire pour la configuration par API
│ └── warehouse_api.conf …… Définition et politique de l'API Warehouse
├── api_backends.conf ………………… Les services backend (en amont)
├── api_gateway.conf ………………… Configuration de niveau supérieur pour le serveur de passerelle API
├── api_json_errors.conf ………… Réponses d'erreur HTTP au format JSON
├── conf.d/
│ ├── ...
│ └── existing_apps.conf
└── nginx.conf

Les répertoires et les noms de fichiers pour toutes les configurations de passerelle API sont préfixés par api_ . Chacun de ces fichiers et répertoires active une fonctionnalité ou une capacité différente de la passerelle API, comme expliqué en détail ci-dessous. Le fichier warehouse_api.conf est un substitut générique des fichiers de configuration décrits ci-dessous qui définissent l’API Warehouse de différentes manières.

Définition de la passerelle API de niveau supérieur

Toute configuration NGINX commence par le fichier de configuration principal, nginx.conf . Pour lire la configuration de la passerelle API, nous ajoutons une directive include dans le bloc http de nginx.conf qui référence le fichier contenant la configuration de la passerelle, api_gateway.conf (ligne 28 juste en dessous). Notez que le fichier nginx.conf par défaut utilise une directive include pour extraire la configuration HTTP basée sur le navigateur à partir du sous-répertoire conf.d (ligne 29). Cet article de blog utilise largement la directive include pour faciliter la lisibilité et permettre l'automatisation de certaines parties de la configuration.

 

Le fichier api_gateway.conf définit le serveur virtuel qui expose NGINX comme passerelle API aux clients. Cette configuration expose toutes les API publiées par la passerelle API à un seul point d'entrée, https://api.example.com/ (ligne 9), protégé par TLS comme configuré sur les lignes 12 à 17. Notez que cette configuration est purement HTTPS – il n’y a pas d’écouteur HTTP en texte brut. Nous attendons des clients API qu’ils connaissent le point d’entrée correct et qu’ils établissent des connexions HTTPS par défaut.

Cette configuration est destinée à être statique – les détails des API individuelles et de leurs services back-end sont spécifiés dans les fichiers référencés par la directive include à la ligne 20. Les lignes 23 à 26 traitent de la gestion des erreurs et sont décrites dans la section Réponse aux erreurs ci-dessous.

 

Service unique ou service unique Backends d'API de microservices

Certaines API peuvent être implémentées sur un seul backend, même si nous nous attendons normalement à ce qu'il y en ait plusieurs, pour des raisons de résilience ou d'équilibrage de charge. Avec les API de microservices, nous définissons des backends individuels pour chaque service ; ensemble, ils fonctionnent comme l'API complète. Ici, notre API Warehouse est déployée sous la forme de deux services distincts, chacun avec plusieurs backends.

 

Tous les services API backend, pour toutes les API publiées par la passerelle API, sont définis dans api_backends.conf . Ici, nous utilisons plusieurs paires adresse IP-port dans chaque bloc en amont pour indiquer où le code API est déployé, mais les noms d’hôte peuvent également être utilisés. Les abonnés NGINX Plus peuvent également profiter de l'équilibrage de charge DNS dynamique pour ajouter automatiquement de nouveaux backends à la configuration d'exécution.

Définition de l'API Warehouse

L'API Warehouse est définie par un certain nombre de blocs d'emplacement dans une configuration imbriquée, comme illustré par l'exemple suivant. Le bloc d'emplacement externe ( /api/warehouse ) identifie le chemin de base, sous lequel les emplacements imbriqués spécifient les URI valides qui sont acheminés vers les services API back-end. L’utilisation d’un bloc externe nous permet de définir des politiques communes qui s’appliquent à l’ensemble de l’API (dans cet exemple, la configuration de journalisation à la ligne 6).

 

NGINX dispose d'un système hautement efficace et flexible pour faire correspondre l'URI de la demande à une section de la configuration. L’ordre des directives de localisation n’est pas important : la correspondance la plus spécifique est choisie. Ici, les emplacements imbriqués sur les lignes 10 et 14 définissent deux URI qui sont plus spécifiques que le bloc d'emplacement externe ; la directive proxy_pass dans chaque bloc imbriqué achemine les requêtes vers le groupe en amont approprié. La configuration de la politique est héritée de l’emplacement externe, sauf s’il est nécessaire de fournir une politique plus spécifique pour certains URI.

Tous les URI qui ne correspondent pas à l'un des emplacements imbriqués sont gérés par l'emplacement externe, qui inclut une directive fourre-tout (ligne 18) qui renvoie la réponse 404(Non trouvé) pour tous les URI non valides.

Choisir large ou large Définition précise des API

Il existe deux approches pour la définition des API : large et précise. L’approche la plus adaptée à chaque API dépend des exigences de sécurité de l’API et de la nécessité ou non pour les services back-end de gérer les URI non valides.

Dans warehouse_api_simple.conf ci-dessus, nous utilisons l'approche large pour l'API Warehouse, en définissant des préfixes d'URI sur les lignes 10 et 14 de telle sorte qu'un URI qui commence par l'un des préfixes soit transmis par proxy au service backend approprié. Avec cette correspondance d’emplacement large basée sur un préfixe, les requêtes API adressées aux URI suivants sont toutes valides :

/api/entrepôt/inventaire
/api/entrepôt/inventaire/
/api/entrepôt/inventaire/foo
/api/entrepôt/inventairefoo
/api/warehouse/inventoryfoo/barre/

Si la seule considération est de proxyer chaque demande vers le service backend approprié, l'approche large fournit le traitement le plus rapide et la configuration la plus compacte. D'autre part, une approche plus précise permet à la passerelle API de comprendre l'espace URI complet de l'API en définissant explicitement le chemin URI pour chaque ressource API disponible. En adoptant une approche précise, la configuration suivante pour le routage d'URI dans l'API Warehouse utilise une combinaison de correspondance exacte ( = ) et d'expressions régulières ( ~ ) pour définir chaque URI valide.

 

Cette configuration est plus détaillée, mais décrit plus précisément les ressources implémentées par les services backend. Cela présente l’avantage de protéger les services back-end des requêtes client malformées, au prix d’une petite surcharge supplémentaire pour la correspondance des expressions régulières. Avec cette configuration en place, NGINX accepte certains URI et en rejette d'autres comme non valides :

URI valides   URI non valides
/api/entrepôt/inventaire   /api/entrepôt/inventaire/
/api/entrepôt/inventaire/étagère/foo   /api/entrepôt/inventairefoo
/api/entrepôt/inventaire/étagère/foo/boîte/bar   /api/entrepôt/inventaire/étagère
/api/entrepôt/inventaire/étagère/-/boîte/-   /api/entrepôt/inventaire/étagère/foo/bar
/api/entrepôt/tarification/baz   /api/entrepôt/tarification
    /api/entrepôt/tarification/baz/pub

L’utilisation d’une définition d’API précise permet aux formats de documentation d’API existants de piloter la configuration de la passerelle API. Il est possible d'automatiser les définitions de l'API NGINX à partir de la spécification OpenAPI (anciennement appelée Swagger). Un exemple de script à cet effet est fourni parmi les Gists de cet article de blog.

Réécriture des requêtes client pour gérer les modifications radicales

À mesure que les API évoluent, il est parfois nécessaire d'apporter des modifications qui rompent la stricte compatibilité descendante et nécessitent la mise à jour des clients. Un tel exemple est lorsqu’une ressource API est renommée ou déplacée. Contrairement à un navigateur Web, une passerelle API ne peut pas envoyer à ses clients une redirection (code301 (Déplacé de façon permanente) ) en nommant le nouvel emplacement. Heureusement, lorsqu’il n’est pas pratique de modifier les clients API, nous pouvons réécrire les demandes des clients à la volée.

Dans l’exemple suivant, nous utilisons la même approche générale que dans warehouse_api_simple.conf ci-dessus, mais dans ce cas, la configuration remplace une version précédente de l’API Warehouse où le service de tarification a été implémenté dans le cadre du service d’inventaire. La directive de réécriture de la ligne 3 convertit les demandes adressées à l'ancienne ressource de tarification en demandes adressées au nouveau service de tarification.

 

Répondre aux erreurs

L’une des principales différences entre les API HTTP et le trafic basé sur un navigateur réside dans la manière dont les erreurs sont communiquées au client. Lorsque NGINX est déployé en tant que passerelle API, nous le configurons pour renvoyer les erreurs de la manière la mieux adaptée aux clients API.

La configuration de la passerelle API de niveau supérieur inclut une section qui définit comment gérer les réponses d’erreur.

 

Le page_d'erreur La directive sur la ligne 23 spécifie que lorsqu'une requête ne correspond à aucune des définitions d'API, NGINX renvoie la 400 (Mauvais Demande) erreur au lieu de la valeur par défaut 404 (Pas Trouvé) erreur. Ce comportement (facultatif) nécessite que les clients API effectuent des requêtes uniquement aux URI valides inclus dans la documentation de l'API et empêche les clients non autorisés de découvrir la structure URI des API publiées via la passerelle API.

La ligne 24 fait référence aux erreurs générées par les services backend eux-mêmes . Les exceptions non gérées peuvent contenir des traces de pile ou d’autres données sensibles que nous ne souhaitons pas envoyer au client. Cette configuration ajoute un niveau de protection supplémentaire en envoyant une réponse d'erreur standardisée au client.

La liste complète des réponses d'erreur standardisées est définie dans un fichier de configuration séparé référencé par la directive include sur la ligne 25, dont les premières lignes sont présentées ci-dessous. Ce fichier peut être modifié si un format d'erreur autre que JSON est préféré, avec la valeur default_type sur la ligne 26 de api_gateway.conf modifiée pour correspondre. Vous pouvez également avoir une directive d'inclusion distincte dans la section de politique de chaque API pour référencer un fichier différent de réponses d'erreur qui remplacent les réponses globales.

Avec cette configuration en place, une demande client pour un URI non valide reçoit la réponse suivante. [terminal]$ curl -i https://api.example.com/foo HTTP/1.1 400 Bad Request Server: nginx/1.19.5 Content-Type: application/json Content-Length: 39 Connexion : keep-alive {"status":400,"message":"Mauvaise demande"}[/terminal]

Mise en œuvre de l'authentification

Il est inhabituel de publier des API sans une certaine forme d’authentification pour les protéger. NGINX propose plusieurs approches pour protéger les API et authentifier les clients API. Pour plus d’informations sur les approches qui s’appliquent également aux requêtes HTTP classiques, consultez la documentation relative aux listes de contrôle d’accès (ACL) basées sur l’adresse IP , à l’authentification par certificat numérique et à l’authentification HTTP de base . Ici, nous nous concentrons sur les méthodes d’authentification spécifiques à l’API.

Authentification par clé API

Les clés API sont un secret partagé connu du client et de la passerelle API. Une clé API est essentiellement un mot de passe long et complexe délivré au client API en tant qu’identifiant à long terme. La création de clés API est simple : il suffit d’encoder un nombre aléatoire comme dans cet exemple.

$ openssl rand -base64 18 7B5zIqmRGXmrJTFmKa99vcit

Sur la ligne 2 du fichier de configuration de la passerelle API de niveau supérieur, api_gateway.conf , nous incluons un fichier appelé api_keys.conf , qui contient une clé API pour chaque client API, identifié par le nom du client ou une autre description. Voici le contenu de ce fichier :

Les clés API sont définies dans un bloc de carte . La directive map prend deux paramètres. Le premier définit où trouver la clé API, dans ce cas dans l'en-tête HTTP apikey de la requête client telle que capturée dans la variable $http_apikey . Le deuxième paramètre crée une nouvelle variable ( $api_client_name ) et la définit sur la valeur du deuxième paramètre sur la ligne où le premier paramètre correspond à la clé.

Par exemple, lorsqu'un client présente la clé API 7B5zIqmRGXmrJTFmKa99vcit , la variable $api_client_name est définie sur client_one . Cette variable peut être utilisée pour vérifier les clients authentifiés et incluse dans les entrées de journal pour un audit plus détaillé. Le format du bloc de carte est simple et facile à intégrer dans les flux de travail d'automatisation qui génèrent le fichier api_keys.conf à partir d'un magasin d'informations d'identification existant.

Ici, nous activons l’authentification par clé API en modifiant la configuration « large » ( warehouse_api_simple.conf ) pour inclure une directive auth_request dans la section policy qui délègue la décision d’authentification à un emplacement spécifié.

 

Avec la directive auth_request (ligne 7), nous pouvons, par exemple, faire gérer l'authentification par un serveur d'authentification externe tel que OAuth 2.0 token introspection . Dans cet exemple, nous ajoutons plutôt la logique de validation des clés API au fichier de configuration de la passerelle API de niveau supérieur , sous la forme du bloc d’emplacement suivant appelé /_validate_apikey .

 

La directive interne de la ligne 30 signifie que cet emplacement n'est pas accessible directement par des clients externes (uniquement par auth_request ). Les clients doivent présenter leur clé API dans l’en-tête HTTP apikey . Si cet en-tête est manquant ou vide (ligne 32), nous envoyons un401 Réponse (non autorisée) pour indiquer au client qu'une authentification est requise. La ligne 35 gère le cas où la clé API ne correspond à aucune des clés du bloc de carte - auquel cas le paramètre par défaut de la ligne 2 de api_keys.conf définit $api_client_name sur une chaîne vide - et nous envoyons un403 Réponse (interdite) pour indiquer au client que l'authentification a échoué. Si aucune de ces conditions ne correspond, la clé API est valide et l'emplacement renvoie un 204(Pas de contenu) réponse.

Avec cette configuration en place, l’API Warehouse implémente désormais l’authentification par clé API.

$ curl https://api.example.com/api/warehouse/pricing/item001 {"status":401,"message": "Non autorisé"} $ curl -H "apikey: thisIsInvalid" https://api.example.com/api/warehouse/pricing/item001 {"status":403,"message": "Interdit"} $ curl -H "apikey: 7B5zIqmRGXmrJTFmKa99vcit" https://api.example.com/api/warehouse/pricing/item001 {"sku":"item001","prix":179,99}

Authentification JWT

Les jetons Web JSON (JWT) sont de plus en plus utilisés pour l'authentification API. La prise en charge native de JWT est exclusive à NGINX Plus, permettant la validation des JWT comme décrit dans Authentification des clients API avec JWT et NGINX Plus sur notre blog. Pour un exemple d’implémentation, voir Contrôle de l’accès à des méthodes spécifiques dans la partie 2.

Résumé

Ce premier blog d'une série détaille une solution complète pour déployer NGINX Open Source et NGINX Plus en tant que passerelle API. L'ensemble complet des fichiers abordés dans ce blog peut être consulté et téléchargé à partir de notre référentiel GitHub Gist .

Découvrez les autres articles de cette série :

  • La partie 2 explore des cas d’utilisation plus avancés pour protéger les services back-end contre les clients malveillants ou mal élevés.
  • La partie 3 explique comment déployer NGINX en tant que passerelle API pour les services gRPC.

Pour essayer NGINX Plus, démarrez votre essai gratuit de 30 jours dès aujourd'hui ou contactez-nous pour discuter de vos cas d'utilisation.


« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."