Saltar a contenido

phantom-token: Módulo Phantom Token de NGINX

Instalación

Puedes instalar este módulo en cualquier distribución basada en RHEL, incluyendo, pero no limitado a:

  • RedHat Enterprise Linux 7, 8, 9 y 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 y Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-phantom-token
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-phantom-token

Habilita el módulo añadiendo lo siguiente en la parte superior de /etc/nginx/nginx.conf:

load_module modules/ngx_curity_http_phantom_token_module.so;

Este documento describe nginx-module-phantom-token v2.0.0 lanzado el 22 de mayo de 2025.


Quality Availability

Módulo de NGINX que introspecciona tokens de acceso de acuerdo con RFC 7662, produciendo un "token fantasma" que puede ser enviado a APIs y servicios web de backend. Lee más sobre el enfoque del Token Fantasma.

Este módulo, cuando está habilitado, filtra las solicitudes entrantes, denegando el acceso a aquellas que no tienen un token de acceso OAuth válido presentado en un encabezado Authorization. De este encabezado, se extrae el access_token y se introspecciona utilizando el endpoint configurado. El Curity Identity Server responde a esta solicitud de acuerdo con el estándar. Para un token de acceso activo, el cuerpo de la respuesta del Curity Identity Server contiene el JWT que reemplaza el token de acceso en el encabezado de la solicitud que es reenviada por NGINX al backend. Si el token no es válido o está ausente, no se realiza ninguna solicitud al backend y se le da al llamador un error 401, no autorizado. Este flujo se muestra en el siguiente diagrama:

Integración de NGINX / Curity

Las llamadas iniciales de la aplicación (web o nativa) se realizan utilizando OpenID Connect (OIDC). La parte importante es que el token que se emite es un token de acceso opaco. Es un GUID o UUID o unos pocos puñados de bytes aleatorios; no hay datos relacionados con la identidad en este token. Es un fantasma de los datos reales del usuario, de ahí el nombre -- token fantasma. La aplicación presenta el token al gateway de NGINX de acuerdo con la especificación de Uso de Token Bearer (es decir, RFC 6750). Este estándar dice que la aplicación debe enviar el token fantasma en el encabezado de solicitud Authorization.

Una vez que el servidor NGINX recibe el token de acceso, este módulo entrará en acción. Usando una configuración como la que se muestra a continuación, este módulo interrogará la solicitud, encontrará el token y hará una llamada lateral al Curity Identity Server. Esta solicitud de servicio web se realizará utilizando el estándar de Introspección de Token (RFC 7662) con un tipo Accept de application/jwt (como se define en RFC 7519). Esto hará que el Curity Identity Server devuelva no JSON, sino solo un JWT. Luego, el módulo reenviará el token JWT a las APIs y microservicios de backend.

Si el módulo también está configurado para almacenar en caché los resultados de la llamada al Curity Identity Server (lo cual debería ser para casos de producción), el token fantasma se utilizará como clave de caché para el token JWT correspondiente. Esto eliminará la necesidad de llamadas posteriores al Curity Identity Server mientras este le indique al módulo de NGINX que puede almacenar en caché el JWT.

La conclusión es una puerta de enlace API muy simple que es extremadamente rápida, altamente escalable y sin campanas ni silbatos que se interpongan. Todo el código está aquí, ¡así que es fácil de cambiar y usar con otros servidores OAuth incluso!

Directivas de Configuración del Módulo

La versión 2.0 introdujo un CAMBIO IMPORTANTE para usar directivas de configuración actualizadas.\ Consulta instrucciones de configuración anteriores para configurar versiones anteriores.

Directivas de Configuración Requeridas

Las directivas en esta subsección son obligatorias; si alguna de estas se omite, el módulo se deshabilitará.

phantom_token

Sintaxis: phantom_token on | off

Predeterminado: off

Contexto: location

phantom_token_introspection_endpoint

Sintaxis: phantom_token_introspection_endpoint string

Predeterminado:

Contexto: location

Directivas de Configuración Opcionales

Las siguientes directivas son opcionales y no necesitan ser configuradas.

phantom_token_realm

Sintaxis: phantom_token_realm string

Predeterminado: api

Contexto: location

El nombre del ámbito protegido o del ámbito de protección que debe utilizarse cuando un cliente no proporciona un token de acceso.

Ejemplo de configuración:

location / {
   ...
   phantom_token_realm "myGoodRealm";
}   

phantom_token_scopes

Sintaxis: phantom_token_scopes string

Predeterminado:

Contexto: location

La lista de ámbitos separados por espacios que el servidor debe informar al cliente que son requeridos cuando no proporciona un token de acceso.

Ejemplo de configuración:

location / {
   ...
   phantom_token_scopes "scope_a scope_b scope_c";
}

phantom_token_scope

Sintaxis: phantom_token_scope string

Predeterminado:

Contexto: location

Un arreglo de ámbitos que el servidor debe informar al cliente que son requeridos cuando no proporciona un token de acceso. Si phantom_token_scopes también está configurado, ese valor tendrá prioridad sobre estos.

Ejemplo de configuración:

location / {
   ...
   phantom_token_scope "scope_a";
   phantom_token_scope "scope_b";
   phantom_token_scope "scope_c";
}

Ejemplo de Configuración

Cargando el Módulo

Si el módulo se descarga de GitHub o se compila como una biblioteca compartida (el valor predeterminado) y no se compila explícitamente en NGINX, necesitará ser cargado utilizando la directiva load_module. Esto debe hacerse en la parte principal de la configuración de NGINX:

load_module modules/ngx_curity_http_phantom_token_module.so;

El archivo puede ser una ruta absoluta o relativa. Si no es absoluto, debe ser relativo al directorio raíz de NGINX.

Parámetros de NGINX para el Endpoint de Introspección

También debes configurar los siguientes parámetros de NGINX para la subsolicitud de introspección:

location curity {
    internal;
    proxy_pass_request_headers off;
    proxy_set_header Accept "application/jwt";
    proxy_set_header Content-Type "application/x-www-form-urlencoded";
    proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
    proxy_pass "https://curity.example.com/oauth/v2/oauth-introspect";
}
Configuración de Introspección Descripción
internal Previene que el endpoint de introspección esté disponible externamente.
proxy_pass_request_headers Establecer en off para evitar usar los encabezados de la solicitud principal en la subsolicitud de introspección.
Encabezado Accept Configurar un valor fijo de application/jwt.
Encabezado Content-Type Configurar un valor fijo de application/x-www-form-urlencoded.
Encabezado Authorization Configurar una credencial básica con el ID del cliente de introspección y el secreto del cliente.

Para obtener la credencial básica, concatena el ID del cliente, un carácter de dos puntos y el secreto del cliente, luego codifícalos en base64. El siguiente comando proporciona un ejemplo.

echo -n "my_client_id:my_client_secret" | base64

Configuración Simple

La siguiente es una configuración simple que podría usarse en entornos de demostración o desarrollo donde el proxy inverso de NGINX está en el mismo host que el Curity Identity Server:

server {
    location /api {
        phantom_token on;
        phantom_token_introspection_endpoint curity;
        proxy_pass https://example.com/api;
    }

    location curity {
        internal;
        proxy_pass_request_headers off;
        proxy_set_header Accept "application/jwt";
        proxy_set_header Content-Type "application/x-www-form-urlencoded";
        proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
        proxy_pass "https://curity.example.com/oauth/v2/oauth-introspect";
    }
}

Configuración Compleja

La siguiente es una configuración más compleja donde el proxy inverso de NGINX está en un host separado del Curity Identity Server:

server {
    server_name server1.example.com;
    location /api {
        phantom_token on;
        phantom_token_introspection_endpoint curity;
        phantom_token_realm "myGoodAPI";
        phantom_token_scopes "scope_a scope_b scope_c";
        proxy_pass https://example.com/api;
    }

    location curity {
        internal;
        proxy_pass_request_headers off;
        proxy_set_header Accept "application/jwt";
        proxy_set_header Content-Type "application/x-www-form-urlencoded";
        proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
        proxy_pass "https://server2.example.com:8443/oauth/v2/oauth-introspect";
    }
}

server {
    listen 8443;
    server_name server2.example.com;
    location / {
        proxy_pass "https://curity.example.com";
    }
}

Configuración Más Avanzada con Servidores Separados y Caché

Este módulo aprovecha la directiva proxy_cache incorporada en NGINX. Para poder almacenar en caché las solicitudes realizadas al endpoint de introspección, además de proxy_cache_path en el contexto http y proxy_cache en el contexto de location, debes agregar las siguientes 3 directivas en el contexto de location del endpoint de introspección.

  • proxy_cache_methods POST; Las solicitudes POST no se almacenan en caché por defecto.
  • proxy_cache_key $request_body; La clave de la caché está relacionada con el access_token enviado en la solicitud original. Diferentes solicitudes que utilizan el mismo access_token alcanzan la misma caché.
  • proxy_ignore_headers Set-Cookie; NGINX no almacenará en caché la respuesta si el encabezado Set-Cookie no es ignorado.
http {
    proxy_cache_path /path/to/cache/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
                     inactive=60m use_temp_path=off;
    server {
        server_name server1.example.com;
        location /api {
            phantom_token on;
            phantom_token_introspection_endpoint curity;
            phantom_token_scopes "scope_a scope_b scope_c";
            phantom_token_realm "myGoodAPI";
            proxy_pass https://example.com/api;
        }

        location curity {
            internal;            
            proxy_pass_request_headers off;
            proxy_set_header Accept "application/jwt";
            proxy_set_header Content-Type "application/x-www-form-urlencoded";
            proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";

            proxy_cache_methods POST;
            proxy_cache my_cache;
            proxy_cache_key $request_body;
            proxy_ignore_headers Set-Cookie;

            proxy_pass "https://server2.example.com:8443/oauth/v2/oauth-introspect";
        }
    }

    server {
        listen 8443;
        server_name server2.example.com;
        location / {
            proxy_pass "https://curity.example.com";
        }
    }
}   

Configuración Sin Caché

Se recomienda almacenar en caché los resultados de la llamada al Curity Identity Server para evitar activar una solicitud de introspección para cada solicitud de API. Si deseas desactivar la caché, debes extender el proxy_buffer_size predeterminado para asegurarte de que el módulo pueda leer JWTs grandes. Hazlo actualizando la configuración de la solicitud de introspección como en el siguiente ejemplo.

http {
    server {
        server_name server1.example.com;
        location /api {
            phantom_token on;
            phantom_token_introspection_endpoint curity;
            phantom_token_scopes "scope_a scope_b scope_c";
            phantom_token_realm "myGoodAPI";
            proxy_pass https://example.com/api;
        }

        location curity {
            internal;
            proxy_pass_request_headers off;
            proxy_set_header Accept "application/jwt";
            proxy_set_header Content-Type "application/x-www-form-urlencoded";
            proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";

            proxy_ignore_headers Set-Cookie;
            proxy_buffer_size 16k;
            proxy_buffers 4 16k;

            proxy_pass "https://server2.example.com:8443/oauth/v2/oauth-introspect";
        }
    }

    server {
        listen 8443;
        server_name server2.example.com;
        location / {
            proxy_pass "https://curity.example.com";
        }
    }
}   

Más Información

Para más información sobre el Curity Identity Server, sus capacidades y cómo usarlo para emitir tokens fantasma para microservicios, visita curity.io. Para información de fondo sobre cómo usar el Curity Identity Server para asegurar el acceso a la API, consulta nuestros recursos de seguridad de API.

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-phantom-token.