From f1445a760979e01f5dc833f34f6c1d5325c2947b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Toledo?= Date: Tue, 27 Jan 2026 10:12:13 -0300 Subject: [PATCH] Refactor: Migrate from Nginx/ModSec to Caddy Proxy --- Caddyfile | 25 +++++ TODO.md | 49 +++++++++ .../conf.d}/gps.oestepan.com.br.conf | 0 .../conf.d}/internal_networks.conf | 0 .../modsec.conf.template | 0 .../modsec_includes.conf | 0 .../exchange-rule-exceptions.conf | 0 .../modsec_rules}/gitea-rule-exceptions.conf | 0 .../modsec_rules}/global-exceptions.conf | 0 .../grafana-rule-exceptions.conf | 0 .../nextcloud-rule-exceptions.conf | 0 .../modsec_rules}/zabbix-rule-exceptions.conf | 0 .../modsec_rules}/zammad-rule-exceptions.conf | 0 nginx.conf => _backup/nginx.conf | 0 {scripts => _backup/scripts}/git_sync.sh | 0 {scripts => _backup/scripts}/inject_acme.sh | 0 {scripts => _backup/scripts}/pre-flight.sh | 0 {scripts => _backup/scripts}/reload.ps1 | 0 {scripts => _backup/scripts}/reload.sh | 0 {scripts => _backup/scripts}/renew_ssl.sh | 0 .../snippets}/acme_challenge.conf | 0 .../snippets}/cache_zones.conf | 0 .../snippets}/custom_errors.conf | 0 .../snippets}/docker_resolver.conf | 0 .../snippets}/log_formats.conf | 0 .../snippets}/rate_limit.conf | 0 .../snippets}/security_maps.conf | 0 deploy.sh | 23 ++--- docker-compose.yml | 95 ++---------------- fail2ban/db/fail2ban.sqlite3 | Bin 73728 -> 73728 bytes 30 files changed, 93 insertions(+), 99 deletions(-) create mode 100644 Caddyfile create mode 100644 TODO.md rename {conf.d => _backup/conf.d}/gps.oestepan.com.br.conf (100%) rename {conf.d => _backup/conf.d}/internal_networks.conf (100%) rename modsec.conf.template => _backup/modsec.conf.template (100%) rename modsec_includes.conf => _backup/modsec_includes.conf (100%) rename {modsec_rules => _backup/modsec_rules}/exchange-rule-exceptions.conf (100%) rename {modsec_rules => _backup/modsec_rules}/gitea-rule-exceptions.conf (100%) rename {modsec_rules => _backup/modsec_rules}/global-exceptions.conf (100%) rename {modsec_rules => _backup/modsec_rules}/grafana-rule-exceptions.conf (100%) rename {modsec_rules => _backup/modsec_rules}/nextcloud-rule-exceptions.conf (100%) rename {modsec_rules => _backup/modsec_rules}/zabbix-rule-exceptions.conf (100%) rename {modsec_rules => _backup/modsec_rules}/zammad-rule-exceptions.conf (100%) rename nginx.conf => _backup/nginx.conf (100%) rename {scripts => _backup/scripts}/git_sync.sh (100%) rename {scripts => _backup/scripts}/inject_acme.sh (100%) rename {scripts => _backup/scripts}/pre-flight.sh (100%) rename {scripts => _backup/scripts}/reload.ps1 (100%) rename {scripts => _backup/scripts}/reload.sh (100%) rename {scripts => _backup/scripts}/renew_ssl.sh (100%) rename {snippets => _backup/snippets}/acme_challenge.conf (100%) rename {snippets => _backup/snippets}/cache_zones.conf (100%) rename {snippets => _backup/snippets}/custom_errors.conf (100%) rename {snippets => _backup/snippets}/docker_resolver.conf (100%) rename {snippets => _backup/snippets}/log_formats.conf (100%) rename {snippets => _backup/snippets}/rate_limit.conf (100%) rename {snippets => _backup/snippets}/security_maps.conf (100%) diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..ac3d66d --- /dev/null +++ b/Caddyfile @@ -0,0 +1,25 @@ +{ + # Global Options + email admin@oestepan.com.br + # Enable Admin API for the watcher to trigger reloads + admin :2019 +} + +# Import dynamic sites +import sites/* + +# Default Site: Traccar GPS +gps.oestepan.com.br { + # Reverse Proxy to the backend service + reverse_proxy host.docker.internal:8083 { + # Trust original IPs + header_up X-Real-IP {remote_host} + header_up X-Forwarded-For {remote_host} + header_up X-Forwarded-Proto {scheme} + } + + # Enable logging + log { + output file /var/log/caddy/gps.access.log + } +} diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e5c562e --- /dev/null +++ b/TODO.md @@ -0,0 +1,49 @@ +# Relatório de Diagnóstico e Pontos de Dor (Pain Points) + +Este documento sumariza os problemas arquiteturais e técnicos identificados durante a tentativa de estabilizar o stack `nginx-pathfinder-proxy`. O objetivo é fornecer um contexto claro para um futuro Agente de IA simplificar a solução. + +## 1. Arquitetura Excessivamente Complexa (Split Container) +Atualmente, temos dois containers NGINX separados: +1. **Frontend (`modsecurity`)**: Recebe a internet, faz WAF, termina SSL. +2. **Backend (`nginx-proxy`)**: Recebe do WAF, faz roteamento, gerencia certificados (Certbot), roda scripts. + +**Problemas Causados:** +- **Inferno de Permissões (Permission Hell):** O Backend (onde roda o Certbot) gera certificados no volume compartilhado como `root`. O Frontend tenta ler esses arquivos e falha com `Permission denied` porque roda com outro UID/GID. Tentar corrigir com `chmod` é frágil e inseguro. +- **Configuração Duplicada:** É preciso configurar o Nginx duas vezes. Uma no Frontend (para saber onde estão os certs) e uma no Backend (para saber como tratar a requisição na porta 8080). +- **SSL "Ping-Pong":** O Backend gerencia a renovação, mas o Frontend é quem *usa* o certificado. Isso exige reload sincronizado em dois containers diferentes. + +## 2. Problema do "Ovo e a Galinha" (SSL Bootstrap) +- O NGINX **não sobe** se o arquivo de certificado não existir. +- O Certbot **não gera** o certificado se o NGINX não estiver rodando (para responder o desafio HTTP-01). +- **Solução Atual (Gambiarra):** Criamos um script complexo (`pre-flight.sh` + `renew_ssl.sh`) que gera certificados falsos (self-signed) só para o NGINX subir, e depois tenta baixar os reais. Isso adiciona muita lógica propensa a falhas. + +## 3. Fragilidade de Deploy (Portainer / Docker) +- **Mount Error:** O Portainer falha ao tentar montar arquivos de configuração (`modsec_conf/...`) que ainda não foram baixados pelo git no host. +- **Solução Atual:** Tivemos que "assar" (bake) as configurações dentro da imagem Docker (`COPY conf.d ...`). Isso tira a agilidade de alterar uma config no git e dar deploy rápido; agora exige rebuild da imagem. + +## 4. Scripts de Automação Frágeis +- O script `pre-flight.sh` tenta fazer `git clone/pull` dentro do container. Isso gera erros de "Resource busy" quando tenta limpar pastas montadas via volume. +- Lógica de `sed/grep` para ler arquivos `.conf` e achar domínios é suscetível a erros de sintaxe no arquivo de config. + +--- + +# Recomendação de Simplificação (Para o Próximo Agente) + +### Opção A: Single "Super" Container (Recomendada) +Unificar tudo em um único container. +- **Base:** Usar a imagem oficial com ModSecurity já compilado (ou compilar num multi-stage build). +- **Benefício:** Resolve problemas de permissão (mesmo processo lê e escreve). Resolve problema de setup (um único serviço). Remove complexidade de rede (sem proxy pass interno desnecessário). + +### Opção B: Caddy Server (Radical) +Substituir NGINX + Certbot por **Caddy**. +- **Benefício:** Caddy tem HTTPS automático (resolve o problema do Ovo/Galinha nativamente). +- **WAF:** Existe plugin de WAF para Caddy (Coraza), mas exige validação se atende os requisitos de segurança do Oestepan. + +### Opção C: NGINX Proxy Manager (GUI) +Usar uma solução pronta como NGINX Proxy Manager. +- Tem interface web. +- Gerencia SSL sozinho. +- Pode ser difícil integrar ModSecurity customizado. + +### Resumo do Pedido de Refatoração +> "Simplificar a infraestrutura eliminando a separação Frontend/Backend. Criar um container único que faça WAF + Proxy + SSL Management, eliminando scripts complexos de bootstrap e problemas de permissão de volume." diff --git a/conf.d/gps.oestepan.com.br.conf b/_backup/conf.d/gps.oestepan.com.br.conf similarity index 100% rename from conf.d/gps.oestepan.com.br.conf rename to _backup/conf.d/gps.oestepan.com.br.conf diff --git a/conf.d/internal_networks.conf b/_backup/conf.d/internal_networks.conf similarity index 100% rename from conf.d/internal_networks.conf rename to _backup/conf.d/internal_networks.conf diff --git a/modsec.conf.template b/_backup/modsec.conf.template similarity index 100% rename from modsec.conf.template rename to _backup/modsec.conf.template diff --git a/modsec_includes.conf b/_backup/modsec_includes.conf similarity index 100% rename from modsec_includes.conf rename to _backup/modsec_includes.conf diff --git a/modsec_rules/exchange-rule-exceptions.conf b/_backup/modsec_rules/exchange-rule-exceptions.conf similarity index 100% rename from modsec_rules/exchange-rule-exceptions.conf rename to _backup/modsec_rules/exchange-rule-exceptions.conf diff --git a/modsec_rules/gitea-rule-exceptions.conf b/_backup/modsec_rules/gitea-rule-exceptions.conf similarity index 100% rename from modsec_rules/gitea-rule-exceptions.conf rename to _backup/modsec_rules/gitea-rule-exceptions.conf diff --git a/modsec_rules/global-exceptions.conf b/_backup/modsec_rules/global-exceptions.conf similarity index 100% rename from modsec_rules/global-exceptions.conf rename to _backup/modsec_rules/global-exceptions.conf diff --git a/modsec_rules/grafana-rule-exceptions.conf b/_backup/modsec_rules/grafana-rule-exceptions.conf similarity index 100% rename from modsec_rules/grafana-rule-exceptions.conf rename to _backup/modsec_rules/grafana-rule-exceptions.conf diff --git a/modsec_rules/nextcloud-rule-exceptions.conf b/_backup/modsec_rules/nextcloud-rule-exceptions.conf similarity index 100% rename from modsec_rules/nextcloud-rule-exceptions.conf rename to _backup/modsec_rules/nextcloud-rule-exceptions.conf diff --git a/modsec_rules/zabbix-rule-exceptions.conf b/_backup/modsec_rules/zabbix-rule-exceptions.conf similarity index 100% rename from modsec_rules/zabbix-rule-exceptions.conf rename to _backup/modsec_rules/zabbix-rule-exceptions.conf diff --git a/modsec_rules/zammad-rule-exceptions.conf b/_backup/modsec_rules/zammad-rule-exceptions.conf similarity index 100% rename from modsec_rules/zammad-rule-exceptions.conf rename to _backup/modsec_rules/zammad-rule-exceptions.conf diff --git a/nginx.conf b/_backup/nginx.conf similarity index 100% rename from nginx.conf rename to _backup/nginx.conf diff --git a/scripts/git_sync.sh b/_backup/scripts/git_sync.sh similarity index 100% rename from scripts/git_sync.sh rename to _backup/scripts/git_sync.sh diff --git a/scripts/inject_acme.sh b/_backup/scripts/inject_acme.sh similarity index 100% rename from scripts/inject_acme.sh rename to _backup/scripts/inject_acme.sh diff --git a/scripts/pre-flight.sh b/_backup/scripts/pre-flight.sh similarity index 100% rename from scripts/pre-flight.sh rename to _backup/scripts/pre-flight.sh diff --git a/scripts/reload.ps1 b/_backup/scripts/reload.ps1 similarity index 100% rename from scripts/reload.ps1 rename to _backup/scripts/reload.ps1 diff --git a/scripts/reload.sh b/_backup/scripts/reload.sh similarity index 100% rename from scripts/reload.sh rename to _backup/scripts/reload.sh diff --git a/scripts/renew_ssl.sh b/_backup/scripts/renew_ssl.sh similarity index 100% rename from scripts/renew_ssl.sh rename to _backup/scripts/renew_ssl.sh diff --git a/snippets/acme_challenge.conf b/_backup/snippets/acme_challenge.conf similarity index 100% rename from snippets/acme_challenge.conf rename to _backup/snippets/acme_challenge.conf diff --git a/snippets/cache_zones.conf b/_backup/snippets/cache_zones.conf similarity index 100% rename from snippets/cache_zones.conf rename to _backup/snippets/cache_zones.conf diff --git a/snippets/custom_errors.conf b/_backup/snippets/custom_errors.conf similarity index 100% rename from snippets/custom_errors.conf rename to _backup/snippets/custom_errors.conf diff --git a/snippets/docker_resolver.conf b/_backup/snippets/docker_resolver.conf similarity index 100% rename from snippets/docker_resolver.conf rename to _backup/snippets/docker_resolver.conf diff --git a/snippets/log_formats.conf b/_backup/snippets/log_formats.conf similarity index 100% rename from snippets/log_formats.conf rename to _backup/snippets/log_formats.conf diff --git a/snippets/rate_limit.conf b/_backup/snippets/rate_limit.conf similarity index 100% rename from snippets/rate_limit.conf rename to _backup/snippets/rate_limit.conf diff --git a/snippets/security_maps.conf b/_backup/snippets/security_maps.conf similarity index 100% rename from snippets/security_maps.conf rename to _backup/snippets/security_maps.conf diff --git a/deploy.sh b/deploy.sh index d6f1e68..4d1198b 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,22 +1,15 @@ #!/bin/bash set -e -echo "Detecting Public IP..." -CURRENT_IP=$(curl -s https://ifconfig.me) +echo "Deploying Caddy Proxy..." -if [ -z "$CURRENT_IP" ]; then - echo "Error: Could not detect Public IP." - exit 1 -fi +# Pull latest code +git pull -echo "Public IP detected: $CURRENT_IP" -echo "HOST_PUBLIC_IP=$CURRENT_IP" > .env - -echo "Building and testing..." -docker compose build -docker compose run --rm nginx-proxy nginx -t - -echo "Deploying..." +# Ensure containers are up docker compose up -d -echo "Done! Proxy is running." +# Force a reload just in case +docker compose exec caddy caddy reload + +echo "Deployment Complete." diff --git a/docker-compose.yml b/docker-compose.yml index 702624e..e56bed2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,94 +1,21 @@ services: - # ============================================ - # ModSecurity WAF (Frente do NGINX) - # ============================================ - modsecurity: - build: - context: . - dockerfile: Dockerfile.modsec - container_name: modsecurity-waf + caddy: + image: caddy:latest + container_name: proxy_caddy restart: always ports: - "80:80" - "443:443" - environment: - # - BACKEND=http://nginx-proxy:8080 # Replaced by static config mount - - PARANOIA=1 - - ANOMALY_INBOUND=5 - - ANOMALY_OUTBOUND=4 volumes: - - ssl_data:/etc/nginx/ssl:ro - - modsec_logs:/var/log/modsecurity - depends_on: - - nginx-proxy + - ./Caddyfile:/etc/caddy/Caddyfile + - ./sites:/etc/caddy/sites + - caddy_data:/data + - caddy_config:/config + - caddy_logs:/var/log/caddy extra_hosts: - "host.docker.internal:host-gateway" - - "srvproxy001.itguys.com.br:172.16.254.1" - - "srvproxy001:172.16.254.1" - - - "zammad.itguys.com.br:172.16.254.59" - - "zammad:172.16.254.59" - - "cloud.grupopralog.com.br:172.16.253.12" - - "business.itguys.com.br:172.16.121.13" - - "verbocloud.itguys.com.br:172.16.253.13" - - "srvoffice001.itguys.com.br:172.16.253.101" - - "srvoffice001:172.16.253.101" - - # ============================================ - # NGINX Proxy (Backend do ModSecurity) - # ============================================ - nginx-proxy: - build: . - container_name: nginx-proxy - restart: always - expose: - - "8080" - environment: - - HOST_PUBLIC_IP=${HOST_PUBLIC_IP} - volumes: - - ssl_data:/etc/nginx/ssl - - nginx_cache:/var/cache/nginx - - nginx_logs:/var/log/nginx - - certbot_data_conf:/etc/letsencrypt - - certbot_data_www:/var/www/certbot - - repo_data:/opt/repo - extra_hosts: - - "host.docker.internal:host-gateway" - - "server-254:10.10.253.254" - - - "srvproxy001.itguys.com.br:172.16.254.1" - - "srvproxy001:172.16.254.1" - - - "zammad.itguys.com.br:172.16.254.59" - - "zammad:172.16.254.59" - - "cloud.grupopralog.com.br:172.16.253.12" - - "business.itguys.com.br:172.16.121.13" - - "verbocloud.itguys.com.br:172.16.253.13" - - "srvoffice001.itguys.com.br:172.16.253.101" - - "srvoffice001:172.16.253.101" - - # ============================================ - # Fail2ban (Lê logs e bane IPs) - # ============================================ - fail2ban: - build: - context: . - dockerfile: Dockerfile.fail2ban - container_name: fail2ban - restart: always - network_mode: host - cap_add: - - NET_ADMIN - - NET_RAW - volumes: - - nginx_logs:/var/log/nginx:ro - - modsec_logs:/var/log/modsecurity:ro volumes: - nginx_cache: - nginx_logs: - modsec_logs: - ssl_data: - certbot_data_conf: - certbot_data_www: - repo_data: + caddy_data: + caddy_config: + caddy_logs: diff --git a/fail2ban/db/fail2ban.sqlite3 b/fail2ban/db/fail2ban.sqlite3 index 52ea41041b86020ae3a03e3e4b2d90faab300d21..e9e4334eea14ada01060ae6807d7e6037f4796b3 100644 GIT binary patch delta 27 hcmZoTz|wGlWr7qFEAvDdCm^{oVO~7b8J$H52LN_j2><{9 delta 27 icmZoTz|wGlWr7qF%kPOYPC#;F!n}B<{aT9>4gdg=6AIq|