feat(elite): expansao da stack elite 2026 - modulos, performance, forense e upgrade zero-downtime
This commit is contained in:
parent
e932ca8f7d
commit
78c3c82a69
23
README.md
23
README.md
|
|
@ -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`.
|
3. Faça o commit e push para a branch `producao`.
|
||||||
|
|
||||||
### 2. Sincronização no Servidor (SSH)
|
### 2. Sincronização no Servidor (SSH)
|
||||||
1. Entre no servidor e vá para `/etc/nginx`.
|
1. Entre no servidor e vá para o diretório de scripts: `cd /etc/nginx/scripts/`.
|
||||||
2. Execute `sudo git pull origin producao`.
|
2. Execute o deploy seguro: `sudo python3 deploy_pathfinder.py`.
|
||||||
3. Valide a sintaxe: `sudo nginx -t`.
|
- **Nota:** Este script faz backup automático e rollback se a configuração estiver errada.
|
||||||
4. Recarregue o Nginx: `sudo systemctl reload nginx`.
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ 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
|
## 🚀 Manutenção e Logs
|
||||||
|
|
||||||
### Recompilar WAF (Se necessário)
|
### Recompilar WAF e Performance (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:
|
Se o Nginx for atualizado ou se precisar habilitar o Brotli, o módulo precisará ser recompilado usando o script:
|
||||||
`sudo ./scripts/install_modsecurity.sh`
|
`sudo ./scripts/setup_pathfinder.sh`
|
||||||
|
|
||||||
### Validação
|
### Validação
|
||||||
Sempre teste a configuração antes do reload:
|
Sempre teste a configuração antes do reload:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
upstream ferreirareal_backend {
|
upstream ferreirareal_backend {
|
||||||
server 172.112.1.2:8081;
|
server 172.17.0.3:8081;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,19 @@
|
||||||
|
|
||||||
# Load essential modules
|
# Load essential modules
|
||||||
load_module modules/ngx_http_modsecurity_module.so;
|
load_module modules/ngx_http_modsecurity_module.so;
|
||||||
# load_module modules/ngx_http_brotli_filter_module.so;
|
load_module modules/ngx_http_geoip2_module.so;
|
||||||
# load_module modules/ngx_http_brotli_static_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;
|
user nginx;
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
|
|
@ -57,6 +68,7 @@ http {
|
||||||
include snippets/security_headers.conf; # Novos headers de 2026
|
include snippets/security_headers.conf; # Novos headers de 2026
|
||||||
include snippets/security_maps.conf;
|
include snippets/security_maps.conf;
|
||||||
include snippets/rate_limit.conf;
|
include snippets/rate_limit.conf;
|
||||||
|
include snippets/bandwidth_limit.conf;
|
||||||
|
|
||||||
# Ativação Global da Blacklist
|
# Ativação Global da Blacklist
|
||||||
include /etc/nginx/dynamic/blacklist.conf;
|
include /etc/nginx/dynamic/blacklist.conf;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
@ -3,78 +3,89 @@
|
||||||
|
|
||||||
log_format detailed_proxy escape=json
|
log_format detailed_proxy escape=json
|
||||||
'{'
|
'{'
|
||||||
# Timestamps e Identificadores
|
# Timestamps e Identificadores
|
||||||
'"@timestamp":"$time_iso8601",'
|
'"@timestamp":"$time_iso8601",'
|
||||||
'"time_local":"$time_local",'
|
'"time_local":"$time_local",'
|
||||||
'"msec":"$msec",'
|
'"msec":"$msec",'
|
||||||
'"request_id":"$request_id",'
|
'"hostname":"$hostname",'
|
||||||
'"hostname":"$hostname",'
|
'"worker_pid":$pid,'
|
||||||
'"worker_pid":$pid,'
|
|
||||||
|
|
||||||
# Informações de Conexão e Cliente
|
# Informações de Conexão e Cliente
|
||||||
'"remote_addr":"$remote_addr",'
|
'"remote_addr":"$remote_addr",'
|
||||||
'"remote_port":$remote_port,'
|
'"remote_port":$remote_port,'
|
||||||
'"server_addr":"$server_addr",'
|
'"server_addr":"$server_addr",'
|
||||||
'"server_port":"$server_port",'
|
'"server_port":"$server_port",'
|
||||||
'"real_ip":"$http_x_forwarded_for",'
|
'"real_ip":"$http_x_forwarded_for",'
|
||||||
'"http_x_real_ip":"$http_x_real_ip",'
|
'"http_x_real_ip":"$http_x_real_ip",'
|
||||||
'"remote_user":"$remote_user",'
|
'"remote_user":"$remote_user",'
|
||||||
|
|
||||||
# Detalhes da Requisição HTTP
|
# Detalhes da Requisição HTTP
|
||||||
'"request":"$request",'
|
'"request":"$request",'
|
||||||
'"request_method":"$request_method",'
|
'"request_method":"$request_method",'
|
||||||
'"scheme":"$scheme",'
|
'"scheme":"$scheme",'
|
||||||
'"server_protocol":"$server_protocol",'
|
'"server_protocol":"$server_protocol",'
|
||||||
'"host_header":"$host",'
|
'"host_header":"$host",'
|
||||||
'"request_uri":"$request_uri",'
|
'"request_uri":"$request_uri",'
|
||||||
'"uri":"$uri",'
|
'"uri":"$uri",'
|
||||||
'"document_uri":"$document_uri",'
|
'"document_uri":"$document_uri",'
|
||||||
'"args":"$args",'
|
'"args":"$args",'
|
||||||
'"query_string":"$query_string",'
|
'"query_string":"$query_string",'
|
||||||
'"request_length":$request_length,'
|
'"request_length":$request_length,'
|
||||||
|
|
||||||
# Headers da Requisição
|
# Headers da Requisição
|
||||||
'"http_referer":"$http_referer",'
|
'"http_referer":"$http_referer",'
|
||||||
'"http_user_agent":"$http_user_agent",'
|
'"http_user_agent":"$http_user_agent",'
|
||||||
'"http_accept_encoding":"$http_accept_encoding",'
|
'"http_accept_encoding":"$http_accept_encoding",'
|
||||||
'"http_accept_language":"$http_accept_language",'
|
'"http_accept_language":"$http_accept_language",'
|
||||||
|
|
||||||
# Detalhes da Resposta
|
# Detalhes da Resposta
|
||||||
'"status":$status,'
|
'"status":$status,'
|
||||||
'"body_bytes_sent":$body_bytes_sent,'
|
'"body_bytes_sent":$body_bytes_sent,'
|
||||||
'"bytes_sent":$bytes_sent,'
|
'"bytes_sent":$bytes_sent,'
|
||||||
'"sent_http_content_type":"$sent_http_content_type",'
|
'"sent_http_content_type":"$sent_http_content_type",'
|
||||||
'"sent_http_cache_control":"$sent_http_cache_control",'
|
'"sent_http_cache_control":"$sent_http_cache_control",'
|
||||||
|
|
||||||
# Performance e Conexão
|
# Performance e Conexão
|
||||||
'"request_time":$request_time,'
|
'"request_time":$request_time,'
|
||||||
'"connection":"$connection",'
|
'"connection":"$connection",'
|
||||||
'"connection_requests":$connection_requests,'
|
'"connection_requests":$connection_requests,'
|
||||||
|
|
||||||
# SSL/TLS
|
# SSL/TLS
|
||||||
'"ssl_protocol":"$ssl_protocol",'
|
'"ssl_protocol":"$ssl_protocol",'
|
||||||
'"ssl_cipher":"$ssl_cipher",'
|
'"ssl_cipher":"$ssl_cipher",'
|
||||||
'"ssl_session_reused":"$ssl_session_reused",'
|
'"ssl_session_reused":"$ssl_session_reused",'
|
||||||
|
'"ssl_early_data":"$ssl_early_data",'
|
||||||
|
|
||||||
# Upstream
|
# Upstream
|
||||||
'"upstream_addr":"$upstream_addr",'
|
'"upstream_addr":"$upstream_addr",'
|
||||||
'"upstream_status":"$upstream_status",'
|
'"upstream_status":"$upstream_status",'
|
||||||
'"upstream_connect_time":"$upstream_connect_time",'
|
'"upstream_bytes_received":$upstream_bytes_received,'
|
||||||
'"upstream_header_time":"$upstream_header_time",'
|
'"upstream_bytes_sent":$upstream_bytes_sent,'
|
||||||
'"upstream_response_time":"$upstream_response_time",'
|
'"upstream_connect_time":"$upstream_connect_time",'
|
||||||
'"upstream_cache_status":"$upstream_cache_status",'
|
'"upstream_header_time":"$upstream_header_time",'
|
||||||
|
'"upstream_response_time":"$upstream_response_time",'
|
||||||
|
'"upstream_cache_status":"$upstream_cache_status",'
|
||||||
|
|
||||||
# Compressão e Performance Modernos
|
# Compressão e Performance Modernos
|
||||||
'"content_encoding":"$sent_http_content_encoding",'
|
'"content_encoding":"$sent_http_content_encoding",'
|
||||||
'"compression_ratio":"$gzip_ratio",'
|
'"compression_ratio":"$gzip_ratio",'
|
||||||
'"is_global_asset":"$is_global_asset",'
|
'"is_global_asset":"$is_global_asset",'
|
||||||
'"cache_key":"$pathfinder_cache_key",'
|
'"cache_key":"$pathfinder_cache_key",'
|
||||||
|
|
||||||
# Variáveis Customizadas de Segurança
|
# Variáveis Customizadas de Segurança
|
||||||
'"is_bad_bot":"$is_bad_bot",'
|
'"is_bad_bot":"$is_bad_bot",'
|
||||||
'"is_suspicious_uri":"$is_suspicious_uri",'
|
'"is_suspicious_uri":"$is_suspicious_uri",'
|
||||||
'"block_request":"$block_request",'
|
'"block_request":"$block_request",'
|
||||||
'"risk_level":"$risk_level",'
|
'"risk_level":"$risk_level",'
|
||||||
'"security_score":"$security_score",'
|
'"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"'
|
||||||
|
'}'
|
||||||
'}';
|
'}';
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ proxy_connect_timeout 60s;
|
||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 60s;
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
# Security: Hide Backend Technology
|
# --- Pathfinder Security: Ofuscação Avançada de Bordas ---
|
||||||
proxy_hide_header X-Powered-By;
|
# Remove headers que revelam tecnologia
|
||||||
proxy_hide_header Server;
|
more_clear_headers 'Server' 'X-Powered-By' 'X-AspNet-Version' 'X-Runtime';
|
||||||
|
# Define um Server personalizado (Camo)
|
||||||
|
more_set_headers 'Server: Pathfinder';
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ add_header Strict-Transport-Security "max-age=63072000" always;
|
||||||
|
|
||||||
# HTTP/3 (QUIC) Alt-Svc
|
# HTTP/3 (QUIC) Alt-Svc
|
||||||
add_header Alt-Svc 'h3=":443"; ma=86400';
|
add_header Alt-Svc 'h3=":443"; ma=86400';
|
||||||
|
http3 on;
|
||||||
|
quic_retry on;
|
||||||
|
# http3_max_concurrent_streams 128; # Opcional: Tuning
|
||||||
|
|
||||||
# OCSP Stapling
|
# OCSP Stapling
|
||||||
ssl_stapling on;
|
ssl_stapling on;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue