BLOG | NGINX

Authentification et routage basé sur le contenu avec JWT et NGINX Plus

NGINX-Partie-de-F5-horiz-black-type-RGB
Miniature d'Alan Murphy
Alan Murphy
Publié le 1er mars 2018

NGINX Plus Release 10 a introduit la prise en charge du déchargement de l'authentification des services Web et API avec les jetons Web JSON (JWT, prononcés « jots »). Depuis la sortie de R10, nous avons continué à augmenter les fonctionnalités de chaque nouvelle version.

À partir de NGINX Plus R14 , NGINX Plus prend en charge les JWT qui contiennent des revendications imbriquées et des données de tableau. Lorsqu'il est utilisé dans un scénario de passerelle API, NGINX Plus peut utiliser des JWT pour authentifier les clients qui demandent des connexions aux services back-end et aux destinations API.

On m’a parfois demandé de fournir une configuration de base qui utilise NGINX Plus pour authentifier les JWT, puis de prendre des décisions d’équilibrage de charge plus avancées en fonction des informations JWT. La solution la plus simple consiste simplement à autoriser l’accès à un service si l’authentification réussit, et à bloquer ou rediriger la connexion en cas d’échec.

La procédure pas à pas décrite dans cet article est une preuve de concept complète pour l’authentification JWT et le routage basé sur le contenu à l’aide de NGINX Plus. Pour couvrir la plus large gamme de possibilités et réduire le besoin de connaissances préalables ou d'expérience avec les JWT, j'ai créé une procédure pas à pas « JWT 101 », vous permettant de déployer cette solution (avec des exemples et un contexte) sans aucune connaissance préalable des JWT.

Si vous avez déjà de l'expérience avec JWT ou si vous disposez déjà de JWT dans votre environnement, vous pouvez ignorer les deux premières sections et adapter les extraits de configuration NGINX Plus fournis en fonction de votre environnement et commencer à prendre des décisions d'équilibrage de charge avancées en fonction de vos données de réclamation JWT.

Prérequis

Ce document suppose une nouvelle installation de NGINX Plus, avec des fichiers de configuration par défaut aux emplacements suivants :

  • /etc/nginx/nginx.conf
  • /etc/nginx/conf.d/default.conf

Pour plus d'informations sur l'installation et la prise en main de NGINX Plus, consultez le Guide d'administration de NGINX Plus .

Toutes les commandes CLI supposent le privilège root , donc les utilisateurs non root doivent disposer des autorisations sudo dans leur environnement.

Pour des informations générales supplémentaires et pour d'autres cas d'utilisation des JWT avec NGINX Plus, consultez les liens suivants :

Création d'un JWT et de la clé de signature associée

Les instructions ci-dessous vous guident dans la création d'un JWT à partir de zéro avec des données de charge utile spécifiques à notre exemple, à titre d'illustration de la façon de configurer NGINX Plus pour le traitement de base des revendications JWT. Si vous utilisez un JWT existant au lieu de l’exemple, vous devez vous assurer que votre fichier « secrets » contient la chaîne codée en Base64URL qui correspond à la clé de signature que vous utilisez pour créer le JWT. Vous devrez probablement également modifier les revendications dans la charge utile JWT.

Note: Quelle que soit la manière dont vous générez votre JWT, vous devez utiliser l'encodage Base64URL, qui gère correctement le remplissage (effectué avec le caractère = ) ainsi que d'autres caractères non compatibles HTTP généralement utilisés dans l'encodage Base64. De nombreux outils gèrent cela automatiquement, mais les encodeurs Base64 manuels basés sur CLI et certains outils de création de JWT ne le font pas. Pour plus d'informations sur l'encodage Base64URL, voir l'encodage base64url de Brock Allen et RFC 4648 .

Pour cet exemple, nous utilisons l'interface graphique de jwt.io (qui effectue correctement l'encodage Base64URL) pour créer un JWT HS256 symétrique. La capture d'écran suivante montre à quoi ressemble l'interface graphique après avoir entré les valeurs spécifiées dans les instructions ci-dessous et la signature vérifiée.

En travaillant dans l'interface graphique de jwt.io , générez un JWT HS256 en vérifiant ou en insérant les valeurs indiquées dans les champs de la colonne Decoded à droite :

  1. Vérifiez que la valeur par défaut suivante apparaît dans le champ EN-TÊTE , en modifiant le contenu pour qu'il corresponde si nécessaire :

    { "alg": "HS256",
    "type": "JWT"
    }
  2. Dans le champ VÉRIFIER LA SIGNATURE , remplacez la valeur dans la case (par défaut, secret ) par nginx123 . (Vous effectuez cette modification avant de saisir des données dans le champ CHARGE UTILE pour éviter un problème qui se produit si vous inversez les deux étapes.)

  3. Remplacez le contenu du champ PAYLOAD par ce qui suit :

    { "exp": 1545091200,
    "nom": "Créer un nouvel utilisateur",
    "sub": "cuser",
    "gname": "wheel",
    "guid": "10",
    "fullName": "John Doe", "uname": "jdoe", "uid": "222",
    "sudo": vrai,
    "dept": "IT",
    "url": "http://secure.example.com"
    }

    Note: La revendication exp définit la date et l'heure d'expiration du JWT, en le représentant comme une heure d'époque UNIX (le nombre de secondes depuis minuit UTC le 1er janvier 1970 ). La valeur de l'échantillon représente minuit UTC le 18 décembre 2018 . Pour ajuster la date d'expiration, modifiez l'heure de l'époque.

  4. Vérifiez que la barre sous les champs est bleue et indique Signature vérifiée .

  5. Copiez la valeur de la colonne Encodée de gauche dans un fichier ou une mémoire tampon. Il s'agit du texte intégral du JWT que l'utilisateur jdoe doit présenter pour accéder à http://secure.example.com , et nous l'utiliserons dans nos tests ci-dessous . Nous affichons ici le JWT avec des sauts de ligne à des fins d’affichage, mais il doit être présenté à NGINX Plus sous la forme d’une chaîne sur une seule ligne.

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOi
    JDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkI
    joiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIy
    Iiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGx
    lLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

En travaillant sur l'hôte NGINX Plus, suivez ces étapes pour créer le fichier de clé que NGINX Plus utilise pour vérifier les JWT signés avec nginx123 :

  1. Exécutez cette commande pour générer la chaîne codée en Base64URL correspondant à la chaîne de signature. (Les commandes tr effectuent les substitutions de caractères requises pour l'encodage Base64URL.)

    # echo -n nginx123 | base64 | tr '+/' '-_' | tr -d '=' bmdpbngxMjM
  2. Dans le répertoire /etc/nginx/ , créez le fichier clé appelé api_secret.jwk à utiliser par NGINX Plus pour vérifier les signatures JWT. Insérer le contenu suivant. La valeur dans le champ k est la forme codée en Base64URL de nginx123 , que nous avons générée à l’étape précédente.

    {"clés": [{
    "k": "bmdpbngxMjM",
    "kty": "oct"
    }]
    }

Configuration de NGINX Plus pour gérer les JWT

Les instructions de cette section configurent NGINX Plus pour valider le JWT inclus dans une demande et pour présenter une ressource protégée si le client est autorisé (plutôt que la page par défaut vue par les clients non autorisés). Nous définissons également un nouveau format de journal qui capture les informations liées à JWT.

Configuration de la validation JWT et du routage basé sur le contenu

Dans ces instructions, nous suivons la meilleure pratique standard consistant à renommer le fichier de configuration default.conf afin que NGINX Plus ne le lise pas et à créer une nouvelle configuration spécifiquement pour les tests. Cela vous permet de restaurer facilement la configuration par défaut lorsque vous avez terminé les tests ou s'il y a un problème pendant les tests.

  1. Renommer default.conf :

    # mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
  2. Créez un nouveau fichier de configuration appelé jwt-test.conf dans /etc/nginx/conf.d/ , avec le contenu suivant. Ils configurent la journalisation spécifique à JWT, la validation JWT et le routage basé sur le contenu (une analyse complète suit l'extrait).

    serveur {
    écoute 80 ;
    journal d'accès /var/log/nginx/host.access.log jwt ;
    
    emplacement / {
    racine /usr/share/nginx/html ;
    index index.html index.htm ;
    
    # Validation JWT
    auth_jwt "Domaine de test JWT" token=$arg_myjwt ;
    fichier de clé d'authentification jwt /etc/nginx/api_secret.jwk ;
    journal d'erreur /var/log/nginx/host.jwt.error.log débogage ;
    
    si ( $jwt_claim_uid = 222 ) {
    add_header X-jwt-claim-uid "$jwt_claim_uid" toujours ;
    add_header X-jwt-status "Redirection vers $jwt_claim_url" toujours ;
    retour 301 $jwt_claim_url ;
    }
    
    if ( $jwt_claim_uid != 222 ) {
    add_header X-jwt-claim-uid "$jwt_claim_uid" toujours;
    add_header X-jwt-status "Utilisateur invalide, pas de redirection" toujours;
    }
    }
    }

Les directives du bloc d'emplacement indiquent à NGINX Plus comment gérer les requêtes HTTP qui incluent un JWT. (Pour plus d'informations sur la configuration de journalisation définie par la directive access_log , consultez la section suivante .) NGINX Plus exécute ces étapes :

  1. Extrait le JWT de l'argument myjwt sur la chaîne de requête (comme spécifié par l'argument token de la directive auth_jwt ).

  2. Décode le JWT à l'aide de la clé de signature spécifiée par la directive auth_jwt_key-file (ici, api_secret.jwk ). Il agit sur la charge utile comme suit (ces actions sont inhérentes au traitement JWT et n'ont pas de directives NGINX Plus correspondantes) :

    • Vérifie que le JWT n’a pas expiré ; c’est-à-dire que la date d’expiration spécifiée par la revendication exp dans la charge utile n’est pas dans le passé.
    • Crée une paire clé-valeur pour chaque revendication dans la charge utile. Le nom de la clé est une variable de la forme $ jwt_claim_claim-name (par exemple, $jwt_claim_uid pour la revendication uid ).
  3. Enregistre toutes les erreurs dans /var/log/nginx/host.jwt.error.log au niveau de débogage .

  4. Teste si la valeur de $jwt_claim_uid est222 (comme spécifié par les deux directives if ) et envoie la réponse appropriée au client. Voici comment les informations du JWT sont utilisées pour effectuer un routage basé sur le contenu.

    • Si la valeur est222 , NGINX Plus envoie une réponse qui redirige le client (la directive de retour ) vers l'URL spécifiée dans la revendication d'URL du JWt. À des fins de débogage, il ajoute deux en-têtes à la réponse (les directives add_header ) : le premier capture la valeur de la revendication uid et le second enregistre le fait que le client a été redirigé.
    • Si la valeur n'est pas222 NGINX Plus sert la page d'index par défaut (telle que définie par les directives root et index dans le même bloc d'emplacement ). Toujours à des fins de débogage, il ajoute des en-têtes qui capturent la valeur de la revendication uid et enregistrent le fait que le client n'a pas eu accès à l'URL spécifiée dans le JWT.

    Note: L'utilisation de la directive if pour évaluer les variables n'est généralement pas considérée comme une bonne pratique , et nous recommandons généralement d'utiliser la directive map à la place. Cependant, pour les besoins de cet exemple simple, la directive if fonctionne comme prévu.

En effet, la configuration donne accès aux ressources protégées uniquement aux utilisateurs autorisés. Autrement dit, les utilisateurs disposant d’un JWT valide ont accès à l’URL spécifiée dans le JWT, tandis que les utilisateurs sans JWT valide ont accès à une page par défaut.

Enregistrement des données JWT

Nous terminons la configuration de la gestion JWT pour le routage basé sur le contenu en définissant un format de journalisation appelé jwt , qui est référencé par la directive access_log dans jwt-test.conf . Il capture les données JWT dans le journal d'accès.

  1. Ajoutez la directive log_format suivante à /etc/nginx/nginx.conf :

    log_format jwt '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
    '$jwt_header_alg $jwt_claim_uid $jwt_claim_url';

    Ce format inclut les deux revendications JWT utilisées dans cette procédure pas à pas ( uid et url ), mais vous pouvez enregistrer toutes les données de revendication JWT avec le nom de variable correspondant à la revendication, sous la forme $jwt_claim_claim ‑name .

  2. Enregistrez nginx.conf , puis exécutez la commande suivante pour tester la configuration complète (y compris le nouveau fichier jwt-test.conf ) pour la validité syntaxique. Corrigez toutes les erreurs signalées.

    # nginx -t
  3. Recharger NGINX Plus.

    # nginx -s recharger
    

Tester la configuration

À l’aide d’un navigateur ou d’un outil CLI comme curl , nous pouvons tester que NGINX Plus valide correctement le JWT, authentifie le client qui le présente et effectue un routage basé sur le contenu. (Pour tester uniquement l'authentification et la validation, mais pas le routage basé sur le contenu, commentez les deux blocs if dans jwt-test.conf .)

Pour exécuter le test, nous incluons l'argument myjwt sur l'URL de la requête, en fournissant le texte intégral du JWT dans lequel la revendication d'URL est222 . Nous avons encore ajouté des sauts de ligne à des fins d'affichage.

http://example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiO
jE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW
1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9
lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5le
GFtcGxlLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

Voici la commande curl correspondante (sans saut de ligne, vous pouvez donc la copier et la coller si vous le souhaitez) :

# curl -v exemple.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmd WxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.YYQ CNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

Parce que la valeur de la revendication uid dans le JWT est222 , nous nous attendons à ce que NGINX Plus affiche le contenu de la page restreinte, http://secure.example.com .

Nous testons maintenant pour vérifier que lorsque la revendication d'URL dans le JWT n'est pas222 , NGINX Plus n'affiche pas le contenu de la page restreinte, mais présente à la place la page index.html du serveur local, dans ce cas http://example.com/index.html .

Nous commençons par générer un autre JWT sur jwt.io avec une revendication uid autre que222 ; à titre d'exemple, nous le faisons111 . Voici l'URL de la requête avec ce JWT :

http://example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiO
jE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW
1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9
lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5l
eGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg

La commande curl est :

# curl -v exemple.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmd WxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg

Dans ce cas, nous nous attendons à ce que NGINX Plus diffuse http://example.com/index.html .

Dans les deux conditions de test, vous pouvez utiliser un outil d’inspection d’en-tête (tel que curl ou les outils de développement fournis avec certains navigateurs) pour vérifier que les nouveaux en-têtes, X-jwt-claim-uid et X-jwt-status , ont été ajoutés à la réponse.

Si vous rencontrez des problèmes lors des tests, vérifiez les journaux d'accès et d'erreurs dans /var/log/nginx/host.jwt* . Le journal des erreurs révèle notamment des problèmes de vérification, d'accès à votre fichier de vérification, etc.

Passer le JWT dans un cookie

Dans notre exemple de base, NGINX Plus extrait le JWT de l’argument myjwt sur l’URL de la requête. NGINX Plus prend également en charge la transmission du JWT dans un cookie (pour plus de détails, consultez la documentation de référence NGINX JWT ). Dans jwt-test.conf , modifiez la directive auth_jwt afin que le premier élément du paramètre token soit $cookie au lieu de $arg :

auth_jwt « Domaine de test JWT » token=$cookie_myjwt ;

Pour fournir le JWT dans un cookie appelé myjwt , la commande curl appropriée est :

# curl -v --cookie myjwt= Exemple de texte JWT.com/index.html

Testez par vous-même le routage basé sur le contenu avec les JWT : démarrez votre essai gratuit de 30 jours de NGINX 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."