Autenticación y enrutamiento basado en contenido con JWT y NGINX Plus

NGINX | 01 de marzo de 2018

Versión 10 de NGINX Plus incorporó la posibilidad de delegar la autenticación de servicios web y API mediante tokens web JSON (JWT, pronunciado «yots»). Desde el lanzamiento de R10, hemos seguido ampliando las funcionalidades con cada nueva versión.

A partir de NGINX Plus R14, NGINX Plus admite JWT que contienen declaraciones anidadas y datos de matriz. Cuando se utiliza en un escenario de puerta de enlace de API, NGINX Plus puede usar JWT para autenticar a los clientes que solicitan conexiones a servicios de backend y destinos de API.

En alguna ocasión me han pedido que proporcione una configuración básica que utilice NGINX Plus para autenticar JWT y que, a continuación, tome decisiones más avanzadas de equilibrio de carga basándose en la información de los JWT. La solución más sencilla consiste simplemente en permitir el acceso a un servicio si la autenticación se realiza correctamente y bloquear o redirigir la conexión si no es así.

La guía paso a paso de esta publicación es una prueba de concepto completa de la autenticación mediante JWT y el enrutamiento basado en contenido con NGINX Plus. Para abarcar el mayor número posible de situaciones y reducir la necesidad de conocimientos previos o experiencia con los JWT, he creado una guía «Introducción a los JWT» que permite implementar esta solución (con ejemplos e información de contexto) sin tener conocimientos previos sobre los JWT.

Si ya tiene experiencia con JWT o ya dispone de JWT en su entorno, puede omitir las dos primeras secciones y adaptar los fragmentos de configuración de NGINX Plus proporcionados a su entorno para empezar a tomar decisiones avanzadas de equilibrio de carga basadas en los datos de las declaraciones de sus JWT.

Requisitos previos

Este documento presupone una instalación nueva de NGINX Plus, con los archivos de configuración predeterminados en las siguientes ubicaciones:

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

Para obtener más información sobre cómo instalar NGINX Plus y empezar a utilizarlo, consulte la Guía de administración de NGINX Plus.

Todos los comandos de la CLI presuponen privilegios de root, por lo que los usuarios que no sean root deben tener permisos de sudo en su entorno.

Para obtener más información de contexto y conocer otros casos de uso de los JWT con NGINX Plus, consulte los siguientes enlaces:

Creación de un JWT y una clave de firma asociada

Las instrucciones que aparecen a continuación le guiarán en la creación de un JWT desde cero con datos de carga útil específicos de nuestro ejemplo, para mostrar cómo configurar NGINX Plus para el procesamiento básico de las declaraciones de un JWT. Si utiliza un JWT existente en lugar del de ejemplo, debe asegurarse de que el archivo de «secretos» contenga la cadena codificada en Base64URL que corresponda a la clave de firma utilizada para crear el JWT. Probablemente también tendrá que modificar las declaraciones de la carga útil del JWT.

Nota: Independientemente de cómo genere su JWT, debe utilizar la codificación Base64URL, que gestiona correctamente el relleno (realizado con el carácter =), así como otros caracteres no compatibles con HTTP que suelen utilizarse en la codificación Base64. Muchas herramientas se encargan de ello automáticamente, pero los codificadores Base64 manuales basados en CLI y algunas herramientas de creación de JWT no lo hacen. Para obtener más información sobre la codificación Base64URL, consulte Codificación base64url de Brock Allen y RFC 4648.

Para este ejemplo, utilizamos la interfaz gráfica de jwt.io (que realiza correctamente la codificación Base64URL) para crear un JWT HS256 simétrico. La siguiente captura de pantalla muestra el aspecto de la interfaz gráfica después de introducir los valores especificados en las instrucciones que aparecen a continuación y verificar la firma.

En la interfaz gráfica de jwt.io, genere un JWT HS256 verificando o introduciendo los valores indicados en los campos de la columna «Decodificado» situada a la derecha:

  1. Compruebe que el siguiente valor predeterminado aparezca en el campo «ENCABEZADO» y, si es necesario, modifique el contenido para que coincida:
{  "alg": "HS256",
  "typ": "JWT"
}
  1. En el campo «VERIFICAR FIRMA», sustituya el valor del cuadro (de forma predeterminada, «secret») por «nginx123». (Realice este cambio antes de introducir datos en el campo «CARGA ÚTIL» para evitar un problema que se produce si invierte el orden de estos dos pasos).
  2. Sustituya el contenido del campo «PAYLOAD» por lo siguiente:
{  "exp": 1545091200,
  "name": "Create New User",
  "sub": "cuser",
  "gname": "wheel",
  "guid": "10",
  "fullName": "John Doe",
  "uname": "jdoe",
  "uid": "222",
  "sudo": true,
  "dept": "IT",
  "url": "http://secure.example.com"
}
  1. Nota: La declaración exp establece la fecha y hora de caducidad del JWT, expresándolas como tiempo Unix (el número de segundos transcurridos desde la medianoche UTC del 1 de enero de 1970). El valor de ejemplo representa la medianoche UTC del 18 de diciembre de 2018. Para ajustar la fecha de caducidad, cambie el tiempo Unix.
  2. Compruebe que la barra situada debajo de los campos sea azul y muestre «Firma verificada».
  3. Copie el valor de la columna Codificado de la izquierda en un archivo o búfer. Es el texto completo del JWT que el usuario jdoe debe presentar para acceder a http://secure.example.com, y lo utilizaremos en nuestras pruebas más adelante. Aquí mostramos el JWT con saltos de línea para facilitar su visualización, pero debe proporcionarse a NGINX Plus como una cadena de una sola línea.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOi
    JDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkI
    joiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIy
    Iiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGx
    lLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

En el host de NGINX Plus, siga estos pasos para crear el archivo de claves que NGINX Plus utiliza para verificar los JWT firmados con nginx123:

  1. Ejecute este comando para generar la cadena codificada en Base64URL correspondiente a la cadena de firma. (Los comandos tr realizan las sustituciones de caracteres necesarias para la codificación Base64URL.)
# echo -n nginx123 | base64 | tr '+/' '-_' | tr -d '='bmdpbngxMjM
  1. En el directorio /etc/nginx/, cree el archivo de claves llamado api_secret.jwk, que NGINX Plus utilizará para verificar las firmas JWT. Inserte el siguiente contenido. El valor del campo k es la forma codificada en Base64URL de nginx123, que generamos en el paso anterior.
{"keys":    [{
        "k":"bmdpbngxMjM",
        "kty":"oct"
    }]
}

Configuración de NGINX Plus para gestionar JWT

Las instrucciones de esta sección configuran NGINX Plus para validar el JWT incluido en una solicitud y mostrar un recurso protegido si el cliente está autorizado (en lugar de la página predeterminada que ven los clientes no autorizados). También se define un nuevo formato de registro que recopila información relacionada con los JWT.

Configuración de la validación de JWT y del enrutamiento basado en contenido

En estas instrucciones seguimos la práctica recomendada habitual de cambiar el nombre del archivo de configuración default.conf para que NGINX Plus no lo lea y de crear una nueva configuración específica para las pruebas. De este modo, podrá restaurar fácilmente la configuración predeterminada cuando termine las pruebas o si surge algún problema durante ellas.

  1. Cambie el nombre de default.conf:
# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
  1. Cree un nuevo archivo de configuración llamado jwt-test.conf en /etc/nginx/conf.d/, con el siguiente contenido. Este configura el registro específico de JWT, la validación de JWT y el enrutamiento basado en el contenido (después del fragmento se incluye un análisis completo).
server {
    listen       80;
    access_log  /var/log/nginx/host.access.log jwt;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;

        # JWT validation
        auth_jwt "JWT Test Realm" token=$arg_myjwt;
        auth_jwt_key_file /etc/nginx/api_secret.jwk;
        error_log /var/log/nginx/host.jwt.error.log debug;

        if ( $jwt_claim_uid = 222 ) {
            add_header X-jwt-claim-uid "$jwt_claim_uid" always;
            add_header X-jwt-status "Redirect to $jwt_claim_url" always;
            return 301 $jwt_claim_url;
        }

        if ( $jwt_claim_uid != 222 ) {
            add_header X-jwt-claim-uid "$jwt_claim_uid" always;
            add_header X-jwt-status "Invalid user, no redirect" always;
        }
    }
}

Las directivas del bloque location indican a NGINX Plus cómo gestionar las solicitudes HTTP que incluyen un JWT. (Para obtener información sobre la configuración de registro definida por la directiva access_log, consulte la sección siguiente.) NGINX Plus lleva a cabo los siguientes pasos:

  1. Extrae el JWT del argumento myjwt de la cadena de consulta de la solicitud (tal como especifica el argumento token de la directiva auth_jwt).
  2. Descodifica el JWT mediante la clave de firma especificada por la directiva auth_jwt_key-file (en este caso, api_secret.jwk). Procesa la carga útil de la siguiente manera (estas acciones son inherentes al procesamiento de JWT y no tienen directivas de NGINX Plus correspondientes):
    • Comprueba que el JWT no haya caducado; es decir, que la fecha de caducidad especificada por la declaración exp en la carga útil no sea anterior a la fecha actual.
    • Crea un par clave-valor para cada declaración de la carga útil. El nombre de la clave es una variable con el formato $jwt_claim_claim-name (por ejemplo, $jwt_claim_uid para la declaración uid).
  3. Registra cualquier error en /var/log/nginx/host.jwt.error.log con el nivel debug.
  4. Comprueba si el valor de $jwt_claim_uid es 222 (tal como especifican las dos directivas if) y envía la respuesta adecuada al cliente. Así es como se utiliza la información del JWT para realizar un enrutamiento basado en el contenido.Nota: El uso de la directiva if para evaluar variables no suele considerarse una práctica recomendada, por lo que normalmente recomendamos utilizar en su lugar la directiva map. Sin embargo, para este sencillo ejemplo, la directiva if funciona según lo previsto.
    • Si el valor es 222, NGINX Plus envía una respuesta que redirige al cliente (mediante la directiva return) a la URL especificada en la declaración url del JWT. Para facilitar la depuración, añade dos encabezados a la respuesta (mediante las directivas add_header): el primero captura el valor de la declaración uid y el segundo registra que el cliente ha sido redirigido.
    • Si el valor no es 222, NGINX Plus sirve la página de índice predeterminada (tal como se define mediante las directivas root e index del mismo bloque location). De nuevo, con fines de depuración, añade encabezados que capturan el valor de la declaración uid y registran que el cliente no obtuvo acceso a la URL especificada en el JWT.

En la práctica, la configuración permite que solo los usuarios autorizados accedan a los recursos protegidos. Es decir, los usuarios con un JWT válido pueden acceder a la URL especificada en el JWT, mientras que los usuarios sin un JWT válido acceden a una página predeterminada.

Registro de datos de JWT

Completamos la configuración de la gestión de JWT para el enrutamiento basado en contenido definiendo un formato de registro denominado «jwt», al que hace referencia la directiva «access_log» en jwt-test.conf. Este formato captura los datos del JWT en el registro de acceso.

  1. Añada la siguiente directiva «log_format» a /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';
  1. Este formato incluye las dos declaraciones JWT que se utilizan en esta guía (uid y url), pero puede registrar los datos de cualquier declaración JWT mediante el nombre de variable correspondiente a la declaración, con el formato $jwt_claim_claim‑name.
  2. Guarde nginx.conf y, a continuación, ejecute el siguiente comando para comprobar que la sintaxis de la configuración completa (incluido el nuevo archivo jwt-test.conf) sea válida. Corrija los errores que se indiquen.
# nginx -t
  1. Recargue NGINX Plus.
# nginx -s reload

Probar la configuración

Mediante un navegador o una herramienta de línea de comandos como curl, podemos comprobar que NGINX Plus valida correctamente el JWT, autentica al cliente que lo presenta y realiza el enrutamiento basado en el contenido. (Para probar únicamente la autenticación y la validación, pero no el enrutamiento basado en el contenido, comenta los dos bloques if de jwt-test.conf.)

Para ejecutar la prueba, incluimos el argumento «myjwt» en la URL de la solicitud y proporcionamos el texto completo del JWT en el que la declaración «url» tiene el valor «222». De nuevo, hemos añadido saltos de línea para facilitar su visualización.

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

Este es el comando curl correspondiente (sin saltos de línea, para que puedas copiarlo y pegarlo si lo deseas):

# curl -v example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4

Dado que el valor de la declaración uid del JWT es 222, esperamos que NGINX Plus muestre el contenido de la página restringida, http://secure.example.com.

Ahora realizamos una prueba para verificar que, cuando la notificación url del JWT no es 222, NGINX Plus no muestra el contenido de la página restringida, sino que presenta la página index.html del servidor local, en este caso http://example.com/index.html.

Empezamos generando otro JWT en jwt.io con una declaración uid distinta de 222; a modo de ejemplo, le asignamos el valor 111. Esta es la URL de solicitud con ese JWT:

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

El comando curl es:

# curl -v example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg

En este caso, esperamos que NGINX Plus sirva http://example.com/index.html.

En ambas condiciones de prueba, puede utilizar una herramienta de inspección de cabeceras (como curl o las herramientas para desarrolladores incluidas en algunos navegadores) para comprobar que las nuevas cabeceras, X-jwt-claim-uid y X-jwt-status, se hayan añadido a la respuesta.

Si tiene algún problema durante las pruebas, consulte los registros de acceso y de errores en /var/log/nginx/host.jwt*. En particular, el registro de errores muestra problemas relacionados con la verificación, el acceso al archivo de verificación, etc.

Pasar el JWT en una cookie

En nuestro ejemplo básico, NGINX Plus extrae el JWT del argumento myjwt de la URL de la solicitud. NGINX Plus también permite pasar el JWT en una cookie (para obtener más información, consulte la documentación de referencia de NGINX JWT). En jwt-test.conf, modifique la directiva auth_jwt para que el primer elemento del parámetro token sea $cookie en lugar de $arg:

auth_jwt "JWT Test Realm" token=$cookie_myjwt;

Para proporcionar el JWT en una cookie llamada myjwt, el comando curl adecuado es:

# curl -v --cookie myjwt=JWT-text example.com/index.html

Prueba por ti mismo el enrutamiento basado en contenido con JWT: comienza hoy tu prueba gratuita de 30 días de NGINX o contacta con nosotros para hablar sobre tus casos de uso.


Compartir

Sobre el autor

Alan Murphy
Alan MurphyResponsable sénior de producto

Más artículos de Alan Murphy

Artículos relacionados del blog

Únete a F5 NGINX en AppWorld 2024
NGINX | 04/01/2024

Únete a F5 NGINX en AppWorld 2024

Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
NGINX | 26/09/2023

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source

La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.

Exposición de APIs en Kubernetes
NGINX | 10/03/2022

Exposición de APIs en Kubernetes

La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
NGINX | 23/08/2021

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto

Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.

Presentamos NGINX Plus R22
NGINX | 09/06/2020

Presentamos NGINX Plus R22

NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.

Presentamos NGINX Plus R17
NGINX | 11/12/2018

Presentamos NGINX Plus R17

NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.