O NGINX pode registrar um log muito detalhado de cada transação que processa. Esses logs são conhecidos como logs de acesso , e você pode ajustar os detalhes registrados para diferentes serviços ou locais com 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 para um site movimentado, o volume de dados gerados pode ser enorme. Neste artigo, mostramos como registrar transações seletivamente com base em vários critérios e como usar esse conhecimento para amostrar pontos de dados sobre solicitações de forma rápida e leve.
Exceto quando indicado, esta postagem se aplica tanto ao NGINX Open Source quanto ao NGINX Plus. Para facilitar a leitura, faremos referência ao NGINX ao longo do texto.
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 principal '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format notas '$remote_addr "$request" $status';
O formato de log pode referenciar variáveis NGINX e 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. Esta diretiva especifica a localização do arquivo de log e o formato de log a ser usado:
access_log /var/log/nginx/access.log principal;
Por padrão, o NGINX registra todas as transações usando a seguinte configuração:
access_log logs/access.log combinados;
Se você definir seu próprio access_log, ele substituirá o log de acesso padrão.
Às vezes, você pode desejar registrar apenas determinadas solicitações. Isso é feito usando registro condicional, da seguinte maneira:
server {
listen 80;
set $logme 0;
if ( $uri ~ ^/secure ) {
set $logme 1;
}
# Auditores exigem um log adicional para solicitações para /secure
access_log /var/log/nginx/secure.log notes if=$logme;
# Se tivermos um log de acesso global, precisamos declará-lo aqui novamente
access_log /var/log/nginx/access.log main;
location / {
# ...
}
}
As configurações de log de acesso não são empilhadas nem herdadas; uma diretiva access_log
em um contexto substitui os logs de acesso declarados nos contextos pais.
Por exemplo, se você quiser registrar informações adicionais sobre o tráfego no URI /secure , você pode definir um log de acesso em um bloco location
/secure
{...}
. Este log de acesso substitui o log de acesso geral definido em outro lugar na configuração.
O exemplo na seção anterior aborda esse problema. Ele usa dois logs de acesso no mesmo contexto, com registro condicional que registra solicitações para /secure em um arquivo de log dedicado.
Suponha que você deseja determinar algumas informações estatísticas sobre o tráfego para seu site:
O registro de acesso geral geralmente não é um local apropriado para registrar essas informações. Talvez você não queira poluir o log de acesso com campos adicionais necessários para seu estudo e, em um site movimentado, a sobrecarga de registrar todas as transações seria muito alta.
Nesse caso, você pode registrar um conjunto limitado de campos em um log especializado. Para reduzir a carga no sistema, você também pode desejar amostrar um subconjunto de solicitações.
A configuração a seguir usa a variável $request_id
como um identificador exclusivo para cada solicitação. Ele usa um bloco split_clients
para amostrar dados registrando apenas 1% das solicitações:
split_clients $request_id $logme {
1% 1;
* 0;
}
servidor {
ouvir 80;
access_log /var/log/nginx/secure.log notas if=$logme;
# ...
}
Suponha que desejamos amostrar um ponto de dados de cada usuário (ou de 1% dos usuários), como o cabeçalho User-Agent
. Não podemos simplesmente coletar amostras de todas as solicitações porque os usuários que geram um grande número de solicitações ficam super-representados em nossos dados.
Usamos um bloco de mapa
para detectar a presença de um cookie de sessão, que nos informa se uma solicitação vem de um novo usuário ou de um usuário que já vimos antes. Em seguida, coletamos amostras de solicitações provenientes apenas de novos usuários:
map $cookie_SESSION $logme {
"" $perhaps; # Se o cookie estiver faltando, nós registramos if $perhaps
default 0;
}
split_clients $request_id $perhaps {
1% 1; # $perhaps é verdadeiro 1% do tempo
* 0;
}
server {
listen 80;
access_log /var/log/nginx/secure.log notes if=$logme;
# Opcional: Se o aplicativo não gerar um cookie de sessão, nós
# geraremos o nosso próprio
add_header Set-Cookie SESSION=1;
# ...
}
No entanto, nem todos os clientes respeitam os cookies de sessão. Por exemplo, um rastreador da web pode ignorar cookies, então cada solicitação que ele emite é identificada como vinda de um novo usuário, distorcendo nossos resultados.
Não seria ótimo se pudéssemos experimentar a partir de solicitações quando vemos algo novo pela primeira vez? O objeto 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 destes. Dessa forma, coletamos dados para cada coisa apenas uma vez.
Claramente, precisamos armazenar o estado (uma lista das coisas que 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 da configuração do NGINX Plus usando variáveis; o banco de dados oferece suporte opcional à expiração automática de entradas (o parâmetro timeout
), armazenamento persistente ( state
) e sincronização de cluster ( sync
). Para cada item que ainda não está na loja, registramos a solicitação e adicionamos o item à loja para que ele não seja registrado novamente.
No NGINX Plus R18 e versões posteriores, é muito fácil definir pares de chave-valor durante o processamento de uma transação :
# Defina uma zona keyval com parâmetros apropriadoskeyval_zone zone=clients:80m timeout=3600s;
# Crie uma variável $seen para cada $remote_addr
keyval $remote_addr $seen zone=clients;
log_format notes '$remote_addr "$request" $status';
server {
listen 80;
# se $seen estiver vazio, atualize o keyval (set $seen 1;) e registre isso
# request (set $logme 1;)
# Caso contrário, $logme não será definido e não registraremos a solicitação
# Observe que $seen é redefinido para "" após o tempo limite configurado
if ($seen = "") {
set $seen 1;
set $logme 1;
}
access_log /var/log/nginx/secure.log notes if=$logme;
localização / {
return 200 "Tudo OK: -$seen-$logme-\n";
}
localização /api {
api;
}
}
Este artigo foi inspirado por um problema do mundo real: como posso configurar o TLS de acordo com boas práticas sem excluir usuários com dispositivos legados?
As melhores práticas de TLS são um alvo móvel. O TLS 1.3 foi ratificado há um ano, mas muitos clientes só falam de versões anteriores do TLS; as cifras são declaradas "inseguras" e aposentadas, mas implementações mais antigas dependem delas; os certificados ECC oferecem maior desempenho do que o RSA, mas nem todos os clientes podem aceitar o ECC. Muitos ataques TLS dependem de um "homem no meio" que intercepta o handshake de negociação de cifra e força o cliente e o servidor a selecionar uma cifra menos segura. Portanto, é importante configurar o NGINX Plus para não oferecer suporte a cifras fracas ou legadas, mas isso pode excluir clientes legados.
No exemplo de configuração a seguir, amostramos cada cliente TLS, registrando o protocolo SSL, a cifra e o cabeçalho User-Agent
. Supondo que cada cliente selecione os protocolos mais recentes e as cifras mais seguras que ele suporta, podemos então avaliar os dados amostrados e determinar qual proporção de clientes será excluída se removermos o suporte para protocolos e cifras mais antigos.
Identificamos cada cliente por sua combinação única de endereço IP e User-Agent
, mas identificar clientes por cookie de sessão ou outro método funciona igualmente bem.
log_format sslparams '$ssl_protocol $ssl_cipher '
'$remote_addr "$http_user_agent"';
# Defina uma zona keyval com parâmetros apropriados
keyval_zone zone=clients:80m timeout=3600s;
# Crie uma variável $seen para cada combinação única de $remote_addr e
# cabeçalho 'User-Agent'
keyval $remote_addr:$http_user_agent $seen zone=clients;
server {
listen 443 ssl;
# configuração SSL NGINX padrão
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 como Mac OS X) AppleWebKit/601.1.46 (KHTML, como Gecko) Versão/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, como Gecko) Versão/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.2.1164 Mobile Safari/537.36"
Podemos então processar o arquivo usando uma variedade de métodos para determinar a dispersão dos dados:
$ cat /tmp/sslparams.log | cortar -d ' ' -f 2,2 | classificar | uniq -c | classificar -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 de baixo volume e menos seguras, verificamos os logs para determinar quais clientes as estão usando e, então, tomamos uma decisão informada sobre a remoção das cifras da configuração do NGINX Plus.
O registro condicional do NGINX pode ser usado para amostrar um subconjunto das solicitações que o NGINX gerencia e escrever um registro padrão ou de propósito especial. Essa técnica é útil se você precisar coletar uma amostra rápida de tráfego para análise estatística, como determinar a distribuição de parâmetros SSL.
Você precisa pensar em como amostrar os dados para que usuários ocupados ou spiders não sejam 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 é desejada alta confiança de precisão, você pode criar seletores sofisticados na configuração do NGINX. O armazenamento de chave-valor do NGINX Plus permite que você acumule estado e o compartilhe entre instâncias do NGINX Plus em um cluster, se necessário.
Experimente você mesmo a amostragem de solicitação com o NGINX Plus – comece seu teste gratuito de 30 dias hoje mesmo ou entre em contato conosco para discutir seus casos de uso.
"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."