Muestreo de solicitudes con el registro condicional de NGINX

NGINX | 24 de abril de 2019

NGINX puede registrar información muy detallada de cada transacción que procesa. Estos registros se conocen como «registros de acceso» y permiten ajustar con precisión el nivel de detalle que se registra para distintos servicios o ubicaciones mediante un formato de archivo de registro personalizable.

De forma predeterminada, NGINX registra todas las transacciones que procesa. Esto puede ser necesario por motivos de cumplimiento normativo o de seguridad, pero, en un sitio web con mucho tráfico, el volumen de datos generado puede resultar abrumador. En este artículo, mostramos cómo registrar transacciones de forma selectiva según diversos criterios y cómo aprovechar estos conocimientos para obtener muestras de datos sobre las solicitudes de forma rápida y ligera.

Salvo que se indique lo contrario, esta publicación se aplica tanto a NGINX Open Source como a NGINX Plus. Para facilitar la lectura, nos referiremos a NGINX en todo el texto.

Contexto: breve descripción general de la configuración del registro de acceso de NGINX

Los registros de acceso de NGINX se definen mediante la directiva log_format. Puede definir varios formatos de registro con nombres diferentes; por ejemplo, un formato de registro completo denominado main y un formato de registro abreviado denominado notes para registrar tres datos sobre una solicitud:

log_format main  '$remote_addr - $remote_user [$time_local] "$request" '
                 '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" "$http_x_forwarded_for"';

log_format notes '$remote_addr "$request" $status';

El formato de registro puede hacer referencia a variables de NGINX y a otros valores calculados en el momento de registrar la información.

A continuación, utilice la directiva access_log para indicar a NGINX que registre una transacción una vez finalizada. Esta directiva especifica la ubicación del archivo de registro y el formato de registro que se debe utilizar:

access_log  /var/log/nginx/access.log main;

De forma predeterminada, NGINX registra todas las transacciones mediante la siguiente configuración:

access_log logs/access.log combined;

Si defines tu propio access_log, este sobrescribe (sustituye) el registro de acceso predeterminado.

Registro condicional

A veces, puede que solo quieras registrar determinadas solicitudes. Esto se hace mediante el registro condicional, de la siguiente manera:

server {
    listen 80;

    set $logme 0;
    if ( $uri ~ ^/secure ) {
        set $logme 1;
    }

    # Auditors require an additional log for requests to /secure
    access_log  /var/log/nginx/secure.log notes if=$logme;

    # If we have a global access log, we need to re-declare it here
    access_log  /var/log/nginx/access.log main;

    location / {
        # ...
    }
}

Los registros de acceso no se heredan

La configuración de los registros de acceso no se acumula ni se hereda; una directiva access_log en un contexto sobrescribe (reemplaza) los registros de acceso declarados en los contextos superiores.

Por ejemplo, si desea registrar información adicional sobre el tráfico dirigido al URI /secure, puede definir un registro de acceso en un bloque location /secure {...}. Este registro de acceso sustituye al registro de acceso general definido en otra parte de la configuración.

El ejemplo de la sección anterior resuelve este problema. Utiliza dos registros de acceso en el mismo contexto, con un registro condicional que guarda las solicitudes a /secure en un archivo de registro específico.

Desafíos de los registros de acceso

Supongamos que desea obtener información estadística sobre el tráfico de su sitio web:

  • ¿Cuál es la distribución geográfica habitual de los usuarios?
  • ¿Qué cifrados y protocolos SSL/TLS utilizan mis usuarios?
  • ¿Cuál es la distribución de los navegadores web?

El registro de acceso general no suele ser el lugar adecuado para guardar esta información. Es posible que no quiera sobrecargarlo con los campos adicionales necesarios para su estudio y, en un sitio con mucho tráfico, el coste de registrar todas las transacciones sería demasiado elevado.

En este caso, puede registrar un conjunto limitado de campos en un registro especializado. Para reducir la carga del sistema, también puede optar por tomar una muestra de un subconjunto de solicitudes.

Técnicas de muestreo

Muestreo del 1 % de las solicitudes

La siguiente configuración utiliza la variable $request_id como identificador único para cada solicitud. Utiliza un bloque split_clients para obtener una muestra de los datos registrando solo el 1 % de las solicitudes:

split_clients $request_id $logme {
    1%     1;
    *      0;
}

server {
    listen 80;

    access_log  /var/log/nginx/secure.log notes if=$logme;

    # ...
}

Muestreo del 1 % de los usuarios únicos

Supongamos que queremos tomar como muestra un dato de cada usuario (o del 1 % de los usuarios), como la cabecera User-Agent. No podemos limitarnos a tomar muestras de todas las solicitudes, ya que los usuarios que generan un gran número de solicitudes quedarían sobrerrepresentados en nuestros datos.

Usamos un bloque map para detectar la presencia de una cookie de sesión, que nos indica si una solicitud procede de un usuario nuevo o de uno que ya hemos visto antes. A continuación, solo tomamos muestras de las solicitudes procedentes de usuarios nuevos:

map $cookie_SESSION $logme {
    "" $perhaps;  # If the cookie is missing, we log if $perhaps
    default   0;
}

split_clients $request_id $perhaps {
    1%        1;  # $perhaps is true 1% of the time
    *         0;
}

server {
    listen 80;

    access_log  /var/log/nginx/secure.log notes if=$logme;
  
    # Optional: If the application does not generate a session cookie, we 
    # generate our own
    add_header Set-Cookie SESSION=1;

    # ...
}

Muestreo de elementos únicos

Sin embargo, no todos los clientes aceptan las cookies de sesión. Por ejemplo, un rastreador web podría ignorar las cookies, por lo que cada solicitud que realiza se identifica como procedente de un usuario nuevo, lo que distorsiona nuestros resultados.

¿No sería estupendo poder tomar muestras de las solicitudes la primera vez que detectamos algo nuevo? Ese «algo» puede ser una dirección IP nueva, un valor nuevo de cookie de sesión, un encabezado algo nuevo, un encabezado de host nunca visto o incluso una combinación de estos elementos. De este modo, solo tomamos una muestra de los datos de cada «User-Agent» una vez.algo only once.

Está claro que necesitamos almacenar el estado (una lista de los elementos que hemos visto) y, para ello, recurrimos al almacén de clave-valor de NGINX Plus. El almacén de clave-valor mantiene una base de datos de clave-valor en memoria a la que se puede acceder desde la configuración de NGINX Plus mediante variables; opcionalmente, la base de datos admite la caducidad automática de las entradas (el parámetro timeout), el almacenamiento persistente (state) y la sincronización del clúster (sync). Para cada elemento que aún no esté en el almacén, registramos la solicitud y añadimos el elemento al almacén para que no vuelva a registrarse.

En NGINX Plus R18 y versiones posteriores, es muy fácil establecer pares clave-valor mientras se procesa una transacción:

# Define a keyval zone with appropriate parameterskeyval_zone zone=clients:80m timeout=3600s;

# Create a variable $seen for each unique $remote_addr
keyval $remote_addr $seen zone=clients;

log_format notes '$remote_addr "$request" $status';

server {
    listen 80;

    # if $seen is empty, update the keyval (set $seen 1;) and log this 
    # request (set $logme 1;)
    # Otherwise, $logme is not set and we don't log the request
    # Note that $seen resets to "" after the configured timeout
    if ($seen = "") {
       set $seen 1;
       set $logme 1;
    }
    access_log  /var/log/nginx/secure.log notes if=$logme;

    location / {
        return 200 "All OK: -$seen-$logme-\n";
    }

    location /api {
        api;
    }
}

Un ejemplo real: muestreo de parámetros TLS

Este artículo surgió a raíz de un problema real: ¿cómo puedo configurar TLS siguiendo las buenas prácticas sin excluir a los usuarios con dispositivos antiguos?

Las prácticas recomendadas de TLS evolucionan constantemente. TLS 1.3 se ratificó hace un año, pero muchos clientes solo son compatibles con versiones anteriores de TLS; algunos cifrados se declaran «inseguros» y se retiran, pero las implementaciones más antiguas dependen de ellos; los certificados ECC ofrecen un mayor rendimiento que los RSA, pero no todos los clientes admiten ECC. Muchos ataques contra TLS se basan en un «intermediario» que intercepta el protocolo de enlace de negociación del cifrado y obliga al cliente y al servidor a seleccionar un cifrado menos seguro. Por tanto, es importante configurar NGINX Plus para que no admita cifrados débiles u obsoletos, aunque esto podría excluir a los clientes antiguos.

En el siguiente ejemplo de configuración, tomamos una muestra de cada cliente TLS y registramos el protocolo SSL, el cifrado y la cabecera User-Agent correspondiente. Suponiendo que cada cliente selecciona los protocolos más recientes y los cifrados más seguros que admite, podemos evaluar los datos de la muestra y determinar qué proporción de clientes quedaría excluida si dejamos de admitir protocolos y cifrados antiguos.

Identificamos a cada cliente por su combinación única de dirección IP y User-Agent, aunque también se puede identificar a los clientes mediante una cookie de sesión u otro método.

log_format sslparams '$ssl_protocol $ssl_cipher '
                     '$remote_addr "$http_user_agent"';

# Define a keyval zone with appropriate parameters
keyval_zone zone=clients:80m timeout=3600s;

# Create a variable $seen for each unique combination of $remote_addr and 
# 'User-Agent' header
keyval $remote_addr:$http_user_agent $seen zone=clients;

server {
    listen 443 ssl;

    # default NGINX SSL configuration
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    if ($seen = "") {
       set $seen 1;
       set $logme 1;
    }
    access_log  /tmp/sslparams.log sslparams if=$logme;

    # ...
}

Esto genera un archivo de registro con entradas como la siguiente:

TLSv1.2 AES128-SHA 1.1.1.1 "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 2.2.2.2 "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 3.3.3.3 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:58.0) Gecko/20100101 Firefox/58.0"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 4.4.4.4 "Mozilla/5.0 (Android 4.4.2; Tablet; rv:65.0) Gecko/65.0 Firefox/65.0"
TLSv1 AES128-SHA 5.5.5.5 "Mozilla/5.0 (Android 4.4.2; Tablet; rv:65.0) Gecko/65.0 Firefox/65.0"
TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 6.6.6.6 "Mozilla/5.0 (Linux; U; Android 5.0.2; en-US; XT1068 Build/LXB22.46-28) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.2.1164 Mobile Safari/537.36"

A continuación, podemos procesar el archivo utilizando diversos métodos para determinar la dispersión de los datos:

$ cat /tmp/sslparams.log | cut -d ' ' -f 2,2 | sort | uniq -c | sort -rn | perl -ane 'printf "%30s %s\n", $F[1], "="x$F[0];'    ECDHE-RSA-AES128-GCM-SHA256 =========================
    ECDHE-RSA-AES256-GCM-SHA384 ========
                     AES128-SHA ====
    ECDHE-RSA-CHACHA20-POLY1305 ==
        ECDHE-RSA-AES256-SHA384 ==

Identificamos los cifrados menos seguros y con menor volumen de uso, consultamos los registros para determinar qué clientes los utilizan y, a continuación, tomamos una decisión fundamentada sobre si eliminarlos de la configuración de NGINX Plus.

Conclusión

El registro condicional de NGINX permite tomar una muestra de un subconjunto de las solicitudes que gestiona NGINX y escribirla en un registro estándar o específico. Esta técnica resulta útil si necesita obtener rápidamente una muestra del tráfico para realizar un análisis estadístico, como determinar la distribución de los parámetros SSL.

Debe plantearse detenidamente cómo muestrear los datos para evitar que los usuarios con mucha actividad o los rastreadores estén sobrerrepresentados. Puede utilizar variables en la configuración de NGINX, junto con las directivas «map» y «split_clients», para seleccionar y filtrar solicitudes.

En situaciones en las que la decisión sea más compleja o se requiera un alto grado de confianza en la precisión, puede crear selectores avanzados en la configuración de NGINX. El almacén de clave-valor de NGINX Plus permite acumular el estado y compartirlo entre las instancias de NGINX Plus de un clúster si es necesario.

Pruebe usted mismo el muestreo de solicitudes con NGINX Plus: comience hoy mismo su prueba gratuita de 30 días o póngase en contacto con nosotros para hablar sobre sus casos de uso.


Compartir
Etiquetas: SSL/TLS, F5 NGINX, Tech, 2016

Sobre el autor

Owen Garrett
Owen GarrettDirector sénior de Gestión de Producto

Más artículos de Owen Garrett

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.