Lista dinâmica de bloqueio de IPs com NGINX Plus e fail2ban

NGINX | 19 de setembro de 2017

Foto: Arnold Reinhold – Obra própria, CC BY‑SA 3.0

Talvez você não perceba, mas seu site está sob ameaça constante. Se ele usa WordPress, bots tentam enviar spam. Se há uma página de login, ocorrem ataques de força bruta para descobrir senhas. Você também pode considerar os rastreadores dos mecanismos de busca como visitantes indesejados.

Defender seu site contra atividades indesejadas, suspeitas e maliciosas não é uma tarefa fácil. Os firewalls de aplicações web, como o NGINX App Protect e os Módulos certificados do NGINX Plus disponibilizados por nossos parceiros, são ferramentas eficazes e devem ser considerados parte da sua estratégia de segurança. Na maioria dos ambientes, segurança nunca é demais, e uma abordagem em várias camadas é invariavelmente a mais eficaz.

Nesta publicação do blog, discutiremos o uso do fail2ban como mais uma camada da pilha de segurança da Web. O Fail2ban é um sistema de detecção de intrusões (IDS) que monitora continuamente os arquivos de log em busca de atividades suspeitas e, em seguida, executa uma ou mais ações pré-configuradas. Normalmente, o fail2ban monitora tentativas de login malsucedidas e bloqueia (bane) o endereço IP responsável por determinado período. Essa é uma defesa simples, porém eficaz, contra ataques de força bruta a senhas.

No NGINX Plus R13 introduzimos um armazenamento nativo de chave-valor e uma nova API do NGINX Plus. Isso possibilita uma ampla variedade de soluções de configuração dinâmica, nas quais a configuração e o comportamento do NGINX Plus podem ser controlados por um sistema externo. Neste post, também mostraremos como o fail2ban pode ser usado para reconfigurar automaticamente o NGINX Plus para ignorar solicitações de endereços IP que tenham causado vários eventos de falha de autenticação.

Editor – No NGINX Plus R16 e versões posteriores, os armazenamentos de chave-valor podem ser sincronizados entre todas as instâncias do NGINX Plus em um cluster. (O compartilhamento de estado em um cluster também está disponível para outros recursos do NGINX Plus.) Para obter detalhes, consulte nosso blog e o Guia de Administração do NGINX Plus.

Como usar um armazenamento de chave-valor para bloquear endereços IP

O armazenamento de chave-valor do NGINX Plus é uma solução nativa e residente na memória, com três características principais:

  1. Os pares de chave-valor são representados como objetos JSON
  2. Os pares de chave-valor são gerenciados inteiramente por meio de uma API
  3. Os valores ficam disponíveis para o NGINX Plus como variáveis de configuração comuns

O armazenamento de chave-valor é definido pela criação de uma zona de memória compartilhada nomeada pela diretiva keyval_zone. Esse armazenamento de chave-valor pode então ser preenchido com um conjunto inicial de valores usando o método HTTP POST para enviar um objeto JSON à API. A diretiva keyval define então qual variável existente será usada como chave de pesquisa ($remote_addr no exemplo) e o nome de uma nova variável ($num_failures) que será avaliada com base no valor correspondente a essa chave.

Configuração e gerenciamento do armazenamento de chave-valor do NGINX Plus

A API é habilitada ao designar um bloco location como o endpoint da API do NGINX Plus.


server {
    listen 1111;
    allow  127.0.0.1; # Only allow access from localhost,
    deny   all;       # and prevent remote access.

    location /api {
        api write=on; # The NGINX Plus API endpoint in read/write mode
    }
}
keyval_zone zone=denylist:1M;
keyval $remote_addr $num_failures zone=denylist;

server {
    listen 80;

    location / {
        root /usr/share/nginx/html;
        if ($num_failures) {
            return 403;
        }
    }
}

vim: syntax=nginx

Antes de adicionarmos pares de chave-valor a ele, uma solicitação do conteúdo do armazenamento denylist retorna um objeto JSON vazio.

$ curl http://localhost:1111/api/6/http/keyvals/denylist{}

O armazenamento de chave-valor agora pode ser preenchido com um par inicial de chave-valor usando o método HTTP POST (na forma do argumento -d do comando curl) para enviar um novo objeto JSON. Vários pares de chave-valor podem ser enviados via POST para um armazenamento de chave-valor vazio e, depois disso, apenas um por vez.

$ curl -iX POST -d '{"10.0.0.1":"1"}' http://localhost:1111/api/6/http/keyvals/denylistHTTP/1.1 201 Created
...

Um par chave-valor é removido ao executar uma requisição PATCH na chave com o valor null.

$ curl -iX PATCH -d '{"10.0.0.1":null}' http://localhost:1111/api/6/http/keyvals/denylistHTTP/1.1 204 No Content
...

Todos os pares de chave-valor podem ser removidos do armazenamento de chave-valor enviando o método DELETE.

$ curl -iX DELETE http://localhost:1111/api/6/http/keyvals/denylistHTTP/1.1 204 No Content
...

Agora é possível configurar uma implementação simples de lista de bloqueio de IPs.


server {
    listen 1111;
    allow  127.0.0.1; # Only allow access from localhost,
    deny   all;       # and prevent remote access.

    location /api {
        api write=on; # The NGINX Plus API endpoint in read/write mode
    }
}
keyval_zone zone=denylist:1M;
keyval $remote_addr $num_failures zone=denylist;

server {
    listen 80;

    location / {
        root /usr/share/nginx/html;
        if ($num_failures) {
            return 403;
        }
    }
}

vim: syntax=nginx

Este trecho de configuração define a porta 80 como um servidor web. A linha 18 avalia a variável $num_failures como a parte do valor do par chave-valor na zona de memória compartilhada denylist que corresponde à chave associada à variável $remote_addr (endereço IP do cliente). Esse processo de avaliação de $num_failures pode ser expresso de forma mais clara sequencialmente:

  1. Passe o valor de $remote_addr para o armazenamento de chave-valor denylist key‑value store
  2. Se $remote_addr corresponder exatamente a uma chave, obtenha o valor desse par de chave-valor
  3. Retorne o valor como $num_failures

Assim, se o endereço IP do cliente tiver sido enviado via POST para o armazenamento de chave-valor, todas as solicitações resultarão em um erro HTTP 403 Forbidden. A vantagem dessa abordagem, em vez de usar um bloco map para a mesma finalidade, é que a lista de bloqueio de IPs pode ser controlada por um sistema externo.

Como usar o fail2ban para gerenciar dinamicamente a lista de bloqueio de IPs

Como mencionado acima, o fail2ban é comumente usado para detectar atividades suspeitas e/ou maliciosas em arquivos de log e, em seguida, tomar medidas para proteger o sistema. A ação padrão é configurar o iptables para descartar todos os pacotes provenientes do endereço IP registrado. Embora seja eficaz para bloquear um agente mal-intencionado, essa abordagem proporciona uma experiência muito ruim para usuários legítimos, especialmente se eles tiverem esquecido a senha. Para esses usuários, é como se o site simplesmente estivesse indisponível.

A configuração a seguir descreve uma ação do fail2ban que envia o endereço IP infrator para o armazenamento de chave-valor da lista de bloqueio do NGINX Plus. Em seguida, o NGINX Plus exibe uma página de erro mais útil e, simultaneamente, aplica um limite de taxa de solicitações a esse endereço IP para que, caso as ações façam parte de um ataque, ele seja efetivamente neutralizado.

Pressupomos uma instalação padrão do fail2ban no mesmo host que o NGINX Plus, com toda a configuração no diretório /etc/fail2ban.

A ação do fail2ban a seguir usa a API do NGINX Plus para adicionar e remover endereços IP “banidos” no armazenamento de chave-valor denylist, da mesma forma que no exemplo simples acima. Colocamos o arquivo nginx-plus-denylist.conf no diretório /etc/fail2ban/action.d.


[Definition]
actionban = curl -s -o /dev/null -d '{"":""}' http://localhost:1111/api/6/http/keyvals/denylist
actionunban = curl -s -o /dev/null -X PATCH -d '{"":null}' http://localhost:1111/api/6/http/keyvals/denylist

A seguinte jail do fail2ban habilita o filtro integrado que detecta tentativas de login malsucedidas usando o módulo de autenticação HTTP básica do NGINX e aplica a ação definida em nginx-plus-denylist.conf. Muitos outros filtros integrados estão disponíveis, e é possível criar facilmente novos filtros para detectar atividades indesejadas nos logs de acesso do NGINX.


[DEFAULT]
bantime   = 120
banaction = nginx-plus-denylist
 
[nginx-http-auth]
enabled = true

O trecho de configuração do NGINX Plus a seguir aplica a autenticação HTTP Basic à página padrão “Welcome to NGINX”. Colocamos o arquivo password_site.conf no diretório /etc/nginx/conf.d.


server {
    listen 1111;
    allow  127.0.0.1; # Only allow access from localhost,
    deny   all;       # and prevent remote access.

    location /api {
        api write=on; # The NGINX Plus API endpoint in read/write mode
    }
}

keyval_zone zone=denylist:1M state=denylist.json;
keyval $remote_addr $num_failures zone=denylist;

limit_req_zone $binary_remote_addr zone=20permin:10M rate=20r/m;

server {
    listen 80;
    root /usr/share/nginx/html;

    location / {
        auth_basic "closed site";
        auth_basic_user_file users.htpasswd;

        if ($num_failures) {
            rewrite ^.* /banned.html;
        }
    }

    location = /banned.html {
        limit_req zone=20permin burst=100;
    }
}

vim: syntax=nginx

A linha 11 define a keyval_zone como no arquivo denylist_keyval.conf acima, mas com a adição do parâmetro state, que especifica um arquivo no qual o estado do armazenamento de chave-valor é salvo, fazendo com que ele persista quando o NGINX Plus é interrompido e reiniciado. O armazenamento de chave-valor persiste durante recarregamentos normais da configuração sem que seja necessário especificar um arquivo de estado. (As linhas 1–10 não são exibidas, mas são iguais às do arquivo denylist_keyval.conf.)

A linha 14 cria uma zona de memória compartilhada chamada 20permin e especifica uma taxa máxima de 20 solicitações por minuto para cada endereço IP de cliente. Esse limite de taxa é aplicado quando um endereço IP é adicionado à lista de bloqueio pelo fail2ban (linha 30).

O bloco server define um servidor web que escuta na porta padrão (80), em que todas as solicitações são correspondidas pelo bloco location / (linha 20). A diretiva root (linha 18) especifica onde está localizado o nosso conteúdo web. As linhas 21–22 especificam que a autenticação HTTP Basic é obrigatória para este site e que o banco de dados de senhas dos usuários autorizados está no arquivo users.htpasswd. A documentação da diretiva auth_basic_user_file descreve o formato desse arquivo. Observe que esta configuração não usa criptografia, pois se destina a testes. Qualquer site de produção que use autenticação HTTP Basic deve utilizar SSL/TLS para garantir a segurança do transporte.

Quando o endereço IP do cliente é incluído na lista de bloqueio (linhas 24–26), em vez de retornar o código de erro HTTP 403, reescrevemos a solicitação como /banned.html, que é então processada no bloco location que corresponde exatamente a esse URI (linha 29). Isso retorna uma página web estática que explica que o endereço IP do usuário foi bloqueado devido ao excesso de falhas de login. Também é aplicado um limite de taxa restritivo (linha 30), que impede que um cliente mal-intencionado consuma recursos do sistema desnecessariamente.

Página exibida aos usuários quando o endereço IP deles é incluído na lista de bloqueio

(O HTML desta página da Web de exemplo está disponível como banned.html no repositório do GitHub desta publicação.)

Podemos simular sucessivas tentativas de login malsucedidas e a atividade correspondente do fail2ban da seguinte forma.

$ curl -i http://admin:password@www.example.com/HTTP/1.1 401 Unauthorized ...
$ curl -i http://admin:admin@www.example.com/
HTTP/1.1 401 Unauthorized ...
$ curl -i http://admin:pass1234@www.example.com/
HTTP/1.1 401 Unauthorized ...
$ curl -i http://admin:letmein@www.example.com/
HTTP/1.1 401 Unauthorized ...
$ curl -i http://admin:fred@www.example.com/
HTTP/1.1 401 Unauthorized ...
$ curl http://admin:P@ssw0rd@www.example.com/
<!DOCTYPE html>
<html>
<head>
<title>Banned</title>
...
$ tail -f /var/log/fail2ban.log
2017-09-15 13:55:18,903 fail2ban.filter   [28498]: INFO    [nginx-http-auth] Found 172.16.52.1
2017-09-15 13:55:28,836 fail2ban.filter   [28498]: INFO    [nginx-http-auth] Found 172.16.52.1
2017-09-15 13:57:49,228 fail2ban.filter   [28498]: INFO    [nginx-http-auth] Found 172.16.52.1
2017-09-15 13:57:50,286 fail2ban.filter   [28498]: INFO    [nginx-http-auth] Found 172.16.52.1
2017-09-15 13:57:52,015 fail2ban.filter   [28498]: INFO    [nginx-http-auth] Found 172.16.52.1
2017-09-15 13:57:52,088 fail2ban.actions  [28498]: NOTICE  [nginx-http-auth] Ban 172.16.52.1
2017-09-15 13:59:52,379 fail2ban.actions  [28498]: NOTICE  [nginx-http-auth] Unban 172.16.52.1

Agora, essa solução de lista de bloqueio dinâmica de IPs pode ser executada sem nenhuma outra alteração de configuração. O Fail2ban monitora os arquivos de log do NGINX e adiciona os endereços IP bloqueados ao armazenamento de chave-valor do NGINX Plus usando a API. Após 120 segundos (o bantime configurado em jail.local), o endereço IP infrator é removido da lista de bloqueio, novamente usando a API do NGINX Plus, e as tentativas de login provenientes desse endereço voltam a ser aceitas.

A API e o armazenamento de chave-valor do NGINX Plus possibilitam esse tipo de integração. Agora, é possível implementar soluções avançadas de configuração sem precisar criar arquivos de configuração do NGINX Plus nem realizar recarregamentos. Gostaríamos muito de saber como você está usando esses recursos para criar suas próprias soluções de configuração dinâmica. Deixe um comentário abaixo.

Para experimentar o NGINX Plus, inicie hoje mesmo sua avaliação gratuita de 30 dias ou entre em contato conosco para conversar sobre seus casos de uso.


Compartilhar

Sobre o autor

Liam Crilly
Liam CrillyDiretor Sênior de Gestão de Produtos

Mais publicações de Liam Crilly

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.