Amostragem de requisições com logs condicionais do NGINX

NGINX | 24 de abril de 2019

O NGINX pode registrar um log bastante detalhado de cada transação que processa. Esses logs são conhecidos como logs de acesso, e você pode ajustar com precisão o nível de detalhamento registrado para diferentes serviços ou locais usando um formato de arquivo de log personalizável.

Por padrão, o NGINX registra todas as transações que processa. Isso pode ser necessário para fins de conformidade ou segurança, mas, em um site com muito tráfego, o volume de dados gerado pode ser excessivo. Neste artigo, mostramos como registrar transações de forma seletiva com base em vários critérios e como usar esse conhecimento para coletar amostras de dados sobre solicitações de maneira rápida e leve.

Salvo indicação em contrário, esta publicação se aplica tanto ao NGINX Open Source quanto ao NGINX Plus. Para facilitar a leitura, usaremos NGINX ao longo do texto.

Contexto – Visão geral rápida da configuração do log de acesso do NGINX

Os logs de acesso do NGINX são definidos usando a diretiva log_format. Você pode definir vários formatos de log nomeados diferentes; por exemplo, um formato de log completo chamado main e um formato de log abreviado chamado notes para registrar três pontos de dados sobre uma solicitação:

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';

O formato de log pode fazer referência a variáveis do NGINX e a outros valores calculados no momento do registro.

Em seguida, use a diretiva access_log para instruir o NGINX a registrar uma transação assim que ela for concluída. Essa diretiva especifica o local do arquivo de log e o formato de log a ser usado:

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

Por padrão, o NGINX registra todas as transações usando a seguinte configuração:

access_log logs/access.log combined;

Se você definir seu próprio access_log, ele substituirá o log de acesso padrão.

Registro condicional

Às vezes, você pode querer registrar apenas determinadas requisições. Isso é feito usando o registro condicional, da seguinte forma:

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 / {
        # ...
    }
}

Os logs de acesso não são herdados

As configurações de log de acesso não são cumulativas nem herdadas; uma diretiva access_log em um contexto substitui os logs de acesso declarados nos contextos superiores.

Por exemplo, se você quiser registrar informações adicionais sobre o tráfego para o URI /secure, poderá definir um log de acesso em um bloco location /secure {...}. Esse log de acesso substitui o log de acesso geral definido em outra parte da configuração.

O exemplo na seção anterior resolve esse problema. Ele usa dois logs de acesso no mesmo contexto, com registro condicional que registra as solicitações para /secure em um arquivo de log dedicado.

Desafios dos logs de acesso

Suponha que você queira obter algumas informações estatísticas sobre o tráfego do seu site:

  • Qual é a distribuição geográfica típica dos usuários?
  • Quais cifras e protocolos SSL/TLS meus usuários utilizam?
  • Qual é a distribuição dos navegadores da web?

O log de acesso geral muitas vezes não é o local adequado para registrar essas informações. Talvez você não queira sobrecarregar o log de acesso com os campos adicionais necessários para o seu estudo e, em um site com muito tráfego, o custo de registrar todas as transações seria alto demais.

Nesse caso, você pode registrar um conjunto limitado de campos em um log especializado. Para reduzir a carga no sistema, também pode ser conveniente coletar uma amostra de um subconjunto das solicitações.

Técnicas de amostragem

Amostragem de 1% das solicitações

A configuração a seguir usa a variável $request_id como um identificador exclusivo para cada solicitação. Ela usa um bloco split_clients para coletar uma amostra dos dados, registrando apenas 1% das solicitações:

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

server {
    listen 80;

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

    # ...
}

Amostragem de 1% dos usuários únicos

Suponha que desejemos coletar um ponto de dados de cada usuário (ou de 1% dos usuários), como o cabeçalho User-Agent. Não podemos simplesmente fazer a amostragem de todas as solicitações, pois os usuários que geram um grande número de solicitações ficariam super-representados em nossos dados.

Usamos um bloco map para detectar a presença de um cookie de sessão, que indica se uma solicitação vem de um novo usuário ou de um usuário que já vimos antes. Em seguida, coletamos amostras apenas das solicitações provenientes de novos usuários:

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;

    # ...
}

Amostragem de itens únicos

No entanto, nem todos os clientes respeitam os cookies de sessão. Por exemplo, um rastreador da web pode ignorar os cookies, fazendo com que cada solicitação enviada por ele seja identificada como proveniente de um novo usuário, distorcendo nossos resultados.

Não seria ótimo se pudéssemos coletar uma amostra das requisições na primeira vez que víssemos uma nova coisa? Essa coisa pode ser um novo endereço IP, um novo valor de cookie de sessão, um novo cabeçalho User-Agent, um cabeçalho de host nunca visto antes ou até mesmo uma combinação desses elementos. Dessa forma, coletamos dados de cada coisa apenas uma vez.

Claramente, precisamos armazenar o estado (uma lista das coisas que já vimos) e, para isso, recorremos ao armazenamento de chave-valor do NGINX Plus. O armazenamento de chave-valor mantém um banco de dados de chave-valor na memória, que pode ser acessado por meio de variáveis na configuração do NGINX Plus; opcionalmente, o banco de dados oferece suporte à expiração automática de entradas (parâmetro timeout), ao armazenamento persistente (state) e à sincronização de clusters (sync). Para cada coisa que ainda não esteja no armazenamento, registramos a solicitação e adicionamos a coisa ao armazenamento para que ela não seja registrada novamente.

No NGINX Plus R18 e versões posteriores, é muito fácil definir pares de chave-valor durante o processamento de uma transação:

# 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;
    }
}

Um exemplo do mundo real – Amostragem de parâmetros TLS

Este artigo foi inspirado por um problema do mundo real: como posso configurar o TLS de acordo com as boas práticas sem excluir usuários que utilizam dispositivos antigos?

As práticas recomendadas de TLS estão em constante evolução. O TLS 1.3 foi ratificado há um ano, mas muitos clientes só são compatíveis com versões anteriores do TLS; conjuntos de cifras são declarados “inseguros” e descontinuados, mas implementações mais antigas dependem deles; certificados ECC oferecem melhor desempenho do que os RSA, mas nem todos os clientes são compatíveis com ECC. Muitos ataques a TLS dependem de um “intermediário” que intercepta o handshake de negociação do conjunto de cifras e força o cliente e o servidor a selecionar uma opção menos segura. Portanto, é importante configurar o NGINX Plus para não oferecer suporte a conjuntos de cifras fracos ou legados, mas isso pode excluir clientes legados.

No exemplo de configuração a seguir, coletamos uma amostra de cada cliente TLS, registrando o protocolo SSL, a cifra e o cabeçalho User-Agent. Partindo do pressuposto de que cada cliente seleciona os protocolos mais recentes e as cifras mais seguras compatíveis, podemos avaliar os dados coletados e determinar qual proporção de clientes será excluída se removermos o suporte a protocolos e cifras mais antigos.

Identificamos cada cliente por sua combinação exclusiva de endereço IP e User-Agent, mas identificá-los por cookie de sessão ou outro método funciona igualmente bem.

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;

    # ...
}

Isso gera um arquivo de log com entradas como as seguintes:

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"

Em seguida, podemos processar o arquivo usando diversos métodos para determinar a dispersão dos dados:

$ 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 as cifras menos seguras e de baixo volume de uso, verificamos os logs para determinar quais clientes as estão utilizando e, então, tomamos uma decisão fundamentada sobre a remoção dessas cifras da configuração do NGINX Plus.

Conclusão

O registro condicional do NGINX pode ser usado para coletar uma amostra de um subconjunto das solicitações gerenciadas pelo NGINX e gravá-la em um log padrão ou específico para determinada finalidade. Essa técnica é útil quando é necessário obter rapidamente uma amostra do tráfego para análise estatística, como para determinar a distribuição dos parâmetros SSL.

É preciso pensar com cuidado em como fazer a amostragem dos dados para que usuários muito ativos ou rastreadores não fiquem super-representados. Você pode usar variáveis na configuração do NGINX, juntamente com as diretivas map e split_clients, para selecionar e filtrar solicitações.

Para situações em que a decisão é mais complexa ou em que se deseja um alto grau de confiança na precisão, você pode criar seletores sofisticados na configuração do NGINX. O armazenamento de chave-valor do NGINX Plus permite acumular o estado e compartilhá-lo entre instâncias do NGINX Plus em um cluster, se necessário.

Experimente você mesmo a amostragem de solicitações com o NGINX Plus — comece sua avaliação gratuita de 30 dias hoje mesmo ou entre em contato conosco para conversar sobre seus casos de uso.


Compartilhar

Sobre o autor

Owen Garrett
Owen GarrettDiretor Sênior de Gestão de Produtos

Mais publicações de Owen Garrett

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
NGINX | 10/10/2024

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade

Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
NGINX | 17/06/2024

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa

Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
NGINX | 14/07/2021

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor

Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
NGINX | 02/04/2019

Distribuição segura de chaves privadas SSL com o NGINX

Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
NGINX | 24/05/2018

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX

O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
NGINX | 19/04/2016

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus

Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.