feat(elite): expansao da stack elite 2026 - modulos, performance, forense e upgrade zero-downtime

This commit is contained in:
João Pedro Toledo Goncalves 2026-02-07 00:20:07 -03:00
parent e932ca8f7d
commit 78c3c82a69
14 changed files with 311 additions and 112 deletions

View File

@ -62,10 +62,19 @@ Siga este procedimento para colocar um novo sistema no ar com segurança máxima
3. Faça o commit e push para a branch `producao`.
### 2. Sincronização no Servidor (SSH)
1. Entre no servidor e vá para `/etc/nginx`.
2. Execute `sudo git pull origin producao`.
3. Valide a sintaxe: `sudo nginx -t`.
4. Recarregue o Nginx: `sudo systemctl reload nginx`.
1. Entre no servidor e vá para o diretório de scripts: `cd /etc/nginx/scripts/`.
2. Execute o deploy seguro: `sudo python3 deploy_pathfinder.py`.
- **Nota:** Este script faz backup automático e rollback se a configuração estiver errada.
---
## ⚡ Automação de Deploy (Safe-Rollback)
O Pathfinder inclui um script robusto para evitar downtime:
- `scripts/deploy_pathfinder.py`:
- Faz backup datado de `/etc/nginx` e `/etc/fail2ban`.
- Sincroniza os novos arquivos da pasta temporária.
- Valida com `nginx -t`.
- **Auto-Rollback**: Se houver erro (ex: módulo Brotli faltando), ele restaura os backups originais e reinicia os serviços em milissegundos.
---
@ -93,9 +102,9 @@ Rode os comandos abaixo (substitua o domínio):
## 🚀 Manutenção e Logs
### Recompilar WAF (Se necessário)
Se o Nginx for atualizado para uma versão superior à 1.29.5, o módulo ModSecurity precisará ser recompilado usando o script:
`sudo ./scripts/install_modsecurity.sh`
### Recompilar WAF e Performance (Se necessário)
Se o Nginx for atualizado ou se precisar habilitar o Brotli, o módulo precisará ser recompilado usando o script:
`sudo ./scripts/setup_pathfinder.sh`
### Validação
Sempre teste a configuração antes do reload:

View File

@ -0,0 +1,27 @@
# /etc/fail2ban/action.d/nginx-pathfinder-action.conf
#
# Ação Híbrida Pathfinder Proxy (2026).
# 1. Bloqueia o IP no Firewall (UFW).
# 2. Insere o IP no snippet 'blacklist.conf' do Nginx para bloqueio de aplicação.
# *****************************************************************************
[Definition]
# Comando executado ao iniciar a jail
actionstart = touch /etc/nginx/snippets/blacklist.conf
# Comando de banimento: Firewall + Nginx Blacklist
# Usamos 'prepend' no UFW para garantir que o bloqueio venha antes de qualquer permissão.
actionban = ufw prepend deny from <ip> to any
printf "deny <ip>;\n" >> /etc/nginx/snippets/blacklist.conf
nginx -s reload
# Comando de desbanimento: Remove do Firewall e do arquivo Nginx
# O sed remove a linha exata 'deny IP;' do snippet.
actionunban = ufw delete deny from <ip> to any
sed -i "/deny <ip>;/d" /etc/nginx/snippets/blacklist.conf
nginx -s reload
[Init]
# Nome da jail para logs de auditoria
name = nginx-pathfinder

View File

@ -0,0 +1,18 @@
# /etc/fail2ban/filter.d/nginx-pathfinder-filter.conf
#
# Filtro inteligente para o Pathfinder Proxy (2026).
# Monitora o log JSON 'detailed_proxy' e extrai falhas baseadas no Security Score.
# *****************************************************************************
[Definition]
# Captura IPs com Security Score 2 (Perigo Alto) ou 3 (Ataque Crítico)
# Também valida via risk_level para redundância de segurança.
failregex = ^.*"remote_addr":"<HOST>".*"security_score":"(2|3)".*$
^.*"remote_addr":"<HOST>".*"risk_level":"(PERIGO_ALTO_RISCO_TENTATIVA_VAZAMENTO|ATAQUE_CRITICO_BLOQUEIO_DE_EXPLORACAO)".*$
# Ignora tráfego limpo ou suspeito de baixo nível (Score 0 e 1)
ignoreregex = ^.*"security_score":"(0|1)".*$
# Nota: O formato JSON do Nginx é processado aqui como uma string única por linha.
# <HOST> é a tag padrão do Fail2Ban para identificar o endereço IP.

View File

@ -0,0 +1,24 @@
# /etc/fail2ban/jail.d/nginx-pathfinder-jail.conf
#
# Jail Unificada Pathfinder Proxy (2026).
# Centraliza a proteção de todos os sites monitorando o Score de Risco do Nginx.
# *****************************************************************************
[nginx-pathfinder-jail]
enabled = true
port = http,https
filter = nginx-pathfinder-filter
action = nginx-pathfinder-action
logpath = /var/log/nginx/access_json.log
backend = auto
# --- Política de Tolerância Zero ---
# Se o Nginx marcou o tráfego como PERIGO ou ATAQUE (Score 2 ou 3),
# o banimento é executado na PRIMEIRA ocorrência.
maxretry = 1
# Tempo de observação (não muito relevante com maxretry=1, mas mantido por padrão)
findtime = 1d
# Tempo de banimento: 7 dias (IPs maliciosos persistentes)
bantime = 7d

View File

@ -1,34 +0,0 @@
[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/*.error.log
maxretry = 1
[nginx-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = /var/log/nginx/*.access.log
maxretry = 2
[nginx-deny]
enabled = true
port = http,https
filter = nginx-deny
logpath = /var/log/nginx/*.error.log
maxretry = 1
[nginx-unauthorized]
enabled = true
port = http,https
filter = nginx-unauthorized
logpath = /var/log/nginx/*.access.log
maxretry = 3
[nginx-bad-request]
enabled = true
port = http,https
filter = nginx-bad-request
logpath = /var/log/nginx/*.error.log
maxretry = 1

View File

@ -1,5 +1,5 @@
upstream ferreirareal_backend {
server 172.112.1.2:8081;
server 172.17.0.3:8081;
}
server {

View File

@ -2,8 +2,19 @@
# Load essential modules
load_module modules/ngx_http_modsecurity_module.so;
# load_module modules/ngx_http_brotli_filter_module.so;
# load_module modules/ngx_http_brotli_static_module.so;
load_module modules/ngx_http_geoip2_module.so;
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
load_module modules/ngx_http_cache_purge_module.so;
load_module modules/ngx_http_upstream_fair_module.so;
load_module modules/ngx_http_echo_module.so;
load_module modules/ngx_http_headers_more_filter_module.so;
load_module modules/ngx_http_subs_filter_module.so;
load_module modules/ngx_otel_module.so;
load_module modules/ngx_http_cookie_flag_filter_module.so;
load_module modules/ngx_http_lower_upper_case_module.so;
load_module modules/ngx_http_image_filter_module.so;
load_module modules/ngx_http_ssl_fingerprint_module.so;
user nginx;
worker_processes auto;
@ -57,6 +68,7 @@ http {
include snippets/security_headers.conf; # Novos headers de 2026
include snippets/security_maps.conf;
include snippets/rate_limit.conf;
include snippets/bandwidth_limit.conf;
# Ativação Global da Blacklist
include /etc/nginx/dynamic/blacklist.conf;

View File

@ -0,0 +1,10 @@
# --- Pathfinder Bandwidth Control ---
# Evita que um único usuário sature o link de saída (Upload do servidor)
# 1. Limite por requisição (ex: Downloads de arquivos grandes)
# Ativa o limite apenas após 10MB transferidos na conexão atual
limit_rate_after 10m;
# 2. Velocidade máxima (ex: 1MB/s por requisição)
# Pode ser sobrescrito dentro dos blocos 'location' ou 'server'
limit_rate 1m;

View File

@ -0,0 +1,15 @@
# --- Pathfinder Static Asset Fingerprinting ---
# Permite o uso de 'Cache-Control: immutable' para arquivos com hash no nome.
# Lógica: Se o arquivo termina em .[hash].ext e não existir fisicamente,
# o Nginx tenta carregar o arquivo original (sem o hash).
# Ex: main.a1b2c3d4.js -> tenta main.js no backend/disco local.
location ~* "^(.+)\.[0-9a-f]{8,}\.(js|css|png|jpg|jpeg|gif|ico|svg|webp|avif|woff2?|wasm)$" {
# 1. Tenta o arquivo original se o versionado não existir
try_files $uri $1.$2 =404;
# 2. Força cache imutável (Já que o conteúdo muda se o nome mudar)
add_header Cache-Control "public, max-age=31536000, immutable";
add_header X-Pathfinder-Cache "Immutable-Asset";
}

View File

@ -7,7 +7,6 @@ log_format detailed_proxy escape=json
'"@timestamp":"$time_iso8601",'
'"time_local":"$time_local",'
'"msec":"$msec",'
'"request_id":"$request_id",'
'"hostname":"$hostname",'
'"worker_pid":$pid,'
@ -55,10 +54,13 @@ log_format detailed_proxy escape=json
'"ssl_protocol":"$ssl_protocol",'
'"ssl_cipher":"$ssl_cipher",'
'"ssl_session_reused":"$ssl_session_reused",'
'"ssl_early_data":"$ssl_early_data",'
# Upstream
'"upstream_addr":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"upstream_bytes_received":$upstream_bytes_received,'
'"upstream_bytes_sent":$upstream_bytes_sent,'
'"upstream_connect_time":"$upstream_connect_time",'
'"upstream_header_time":"$upstream_header_time",'
'"upstream_response_time":"$upstream_response_time",'
@ -76,5 +78,14 @@ log_format detailed_proxy escape=json
'"block_request":"$block_request",'
'"risk_level":"$risk_level",'
'"security_score":"$security_score",'
'"is_internal_ip":"$is_internal"'
'"is_internal_ip":"$is_internal",'
# --- Pathfinder Extra Metadata (Seção Especial) ---
'"pathfinder_meta": {'
'"request_uuid": "$request_id",'
'"ja3_fingerprint": "$http_ssl_ja3",'
'"h2_fingerprint": "$http2_fingerprint",'
'"version": "2026.1",'
'"engine": "Pathfinder-Elite"'
'}'
'}';

View File

@ -14,6 +14,8 @@ proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Security: Hide Backend Technology
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
# --- Pathfinder Security: Ofuscação Avançada de Bordas ---
# Remove headers que revelam tecnologia
more_clear_headers 'Server' 'X-Powered-By' 'X-AspNet-Version' 'X-Runtime';
# Define um Server personalizado (Camo)
more_set_headers 'Server: Pathfinder';

View File

@ -7,6 +7,9 @@ add_header Strict-Transport-Security "max-age=63072000" always;
# HTTP/3 (QUIC) Alt-Svc
add_header Alt-Svc 'h3=":443"; ma=86400';
http3 on;
quic_retry on;
# http3_max_concurrent_streams 128; # Opcional: Tuning
# OCSP Stapling
ssl_stapling on;

View File

@ -0,0 +1,10 @@
# --- Pathfinder Monitoring: Stub Status ---
# Fornece métricas básicas de conexões ativas para o Zabbix/Prometheus.
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 172.17.0.0/24; # Range interna itguys
deny all;
}

View File

@ -0,0 +1,92 @@
import os
import subprocess
import shutil
import sys
from datetime import datetime
# Configurações
PASSWORD = "vR7Ag$Pk"
TMP_SYNC_BASE = "/tmp/pathfinder_sync"
TARGETS = {
"nginx": {
"src": f"{TMP_SYNC_BASE}/nginx/",
"dst": "/etc/nginx",
"bak": "/etc/nginx.bak",
"test_cmd": ["nginx", "-t"]
},
"fail2ban": {
"src": f"{TMP_SYNC_BASE}/fail2ban/",
"dst": "/etc/fail2ban",
"bak": "/etc/fail2ban.bak",
"test_cmd": ["fail2ban-server", "-t"] # Apenas teste de config
}
}
def run_sudo(cmd):
"""Executa comando com sudo -S e retorna (rc, stdout, stderr)."""
p = subprocess.Popen(['sudo', '-S'] + cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
out, err = p.communicate(input=PASSWORD + '\n')
return p.returncode, out, err
def log(msg):
print(f"[*] {msg}")
def deploy():
log("Iniciando Deploy Seguro Pathfinder...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 1. Backups preventivos
for name, config in TARGETS.items():
log(f"Criando backup de {name}...")
run_sudo(['cp', '-rp', config['dst'], f"{config['bak']}_{timestamp}"])
# Mantém também um backup 'fixo' para o rollback rápido do script
run_sudo(['rm', '-rf', config['bak']])
run_sudo(['cp', '-rp', config['dst'], config['bak']])
# 2. Aplicação das novas configurações
for name, config in TARGETS.items():
if not os.path.exists(config['src']):
log(f"Aviso: Fonte {config['src']} não encontrada. Pulando {name}.")
continue
log(f"Aplicando novas configurações em {name}...")
# Copia o conteúdo da pasta temporária para o destino
run_sudo(['cp', '-rf', os.path.join(config['src'], '.'), config['dst']])
# 3. Validação Crítica (Nginx)
log("Validando configuração do Nginx...")
rc, out, err = run_sudo(TARGETS['nginx']['test_cmd'])
if rc != 0:
log("ERRO DETECTADO NA CONFIGURAÇÃO!")
print(f"\nDetalhes do Erro:\n{err}\n")
rollback()
sys.exit(1)
log("Configuração validada com sucesso.")
# 4. Reinicialização de Serviços
log("Reiniciando serviços...")
run_sudo(['systemctl', 'restart', 'nginx'])
run_sudo(['systemctl', 'restart', 'fail2ban'])
log("Deploy finalizado com sucesso!")
def rollback():
log("EXECUTANDO ROLLBACK AUTOMÁTICO...")
for name, config in TARGETS.items():
log(f"Restaurando {name} do backup...")
run_sudo(['rm', '-rf', config['dst']])
run_sudo(['cp', '-rp', config['bak'], config['dst']])
log("Tentando reiniciar serviços após rollback...")
run_sudo(['systemctl', 'restart', 'nginx'])
run_sudo(['systemctl', 'restart', 'fail2ban'])
log("Rollback concluído. Servidor estabilizado.")
if __name__ == "__main__":
if os.getuid() == 0:
log("Erro: Não execute como root diretamente. O script usa sudo internamente.")
sys.exit(1)
deploy()