Seguridad

Cómo configurar un WAF open source (Coraza) frente a tu app en 2026

TL;DR — Lo que vas a tener al final

  • Tiempo de setup: 30 minutos a 2 horas dependiendo de la complejidad del entorno.
  • Requisitos: Acceso root, Linux moderno, conexión a internet para descargar dependencias.
  • Resultado: Servicio funcional y estable, documentado paso a paso.
  • Troubleshooting: Logs estándar en /var/log/, comandos de verificación al final de cada sección.

Por qué necesitas un WAF en 2026

Un firewall de red tradicional filtra por IP, puerto y protocolo. Frente a una aplicación HTTP, eso deja sin filtrar casi todo el tráfico malicioso real: inyecciones SQL, cross-site scripting, path traversal, User-Agent exóticos, payloads binarios en cuerpos JSON y un arsenal de ataques que cambian cada semana. El Web Application Firewall (WAF) se coloca delante del reverse proxy o directamente sobre él e inspecciona cada request a nivel de aplicación, bloqueando lo que rompe el contrato HTTP esperado antes de que toque tu código.

El problema histórico era que la referencia del sector, ModSecurity, quedó en una encrucijada: Trustwave anunció su End-of-Life comercial el 1 de julio de 2024, y aunque el proyecto open source sigue vivo en forks comunitarios, el motor en C se quedó atrás en soporte de OWASP CRS 4.x y en despliegues modernos. Aquí es donde entra OWASP Coraza: una reimplementación 100% compatible con la sintaxis SecLang de ModSecurity, escrita en Go, mantenido por la comunidad OWASP y con primer release estable en septiembre de 2021. La versión actual al momento de escribir este artículo es v3.7.0 (publicada el 6 de abril de 2026), con ~3.700 estrellas en GitHub y licencia Apache-2.0.

Qué es Coraza y cómo se compara con ModSecurity

Coraza es un motor WAF en Go que parsea y evalúa directivas SecLang idénticas a las de ModSecurity: SecRuleEngine, SecRule, SecAction, Include, SecRuleUpdateTargetById, etc. Si vienes de ModSecurity, tus reglas existentes funcionan sin reescritura. La diferencia está en el runtime: al estar escrito en Go, Coraza embebe en cualquier binario Go sin servidor HTTP propio, se compila a WebAssembly para plugins de proxy, y consume entre un 40% y 60% menos de RAM que libmodsecurity en benchmarks de Traefik Labs y de la comunidad de Coraza.

Característica Coraza v3.7.0 ModSecurity v2.9
Lenguaje Go (compilado, memory-safe) C (Apache module / standalone)
Soporte OWASP CRS 4.x completo (incluye PL1–PL4) 3.x estable, 4.x parcial vía forks
Compatibilidad SecLang 100% (pasa el test suite oficial de CRS) 100% (es la referencia)
Estado del proyecto OWASP Production, releases cada 2-3 meses Comercial EOL 07/2024, comunidad mantiene forks
Modo embebido Sí, como librería Go No, requiere Apache/nginx connector
Plugins proxy Caddy oficial, Traefik WASM, Istio sidecar, Otoroshi Apache, nginx, IIS
Licencia Apache-2.0 Apache-2.0 (libmodsecurity 3.x cambió a AGPL/Apache mixto)

Para una API moderna o un reverse proxy en Go, Coraza es estrictamente mejor opción: actual, mantenido, y con un pipeline CI/CD donde los tests de regresión corren contra el sandbox oficial de OWASP CRS (sandbox.coreruleset.org).

OWASP CRS 4.x: el ruleset que detecta el 95% de los ataques

Coraza sin reglas es un motor en vacío. El valor real viene de cargarle el OWASP Core Rule Set (CRS), mantenido por el proyecto homónimo y versionado en paralelo. La línea actual es CRS 4.29.0, con una variante 4.25 LTS para quienes prefieren estabilidad de largo plazo. CRS 4.x introdujo una arquitectura de plugins que mantiene el core magro y mueve features opcionales a paquetes separados: scoring, detección de anomalías HTTP, sanitización de UTF-8, etc.

Lo crítico al desplegar CRS son los Paranoia Levels (PL). No son niveles de severidad: son niveles de agresividad progresiva del ruleset.

Paranoia Level Descripción Cuándo usarlo
PL1 Baseline. Reglas que rara vez generan falsos positivos. Recomendado para empezar. Sitios públicos, APIs REST estándar, primera activación
PL2 Añade ~30 reglas extra. Casi seguro introduce nuevos falsos positivos en apps con inputs libres. Apps internas, B2B, después de tunear PL1
PL3 Detección más estricta, ~80 reglas extra. Falsos positivos probables sin whitelist. Backoffice, endpoints administrativos
PL4 Máxima cobertura. Solo para tráfico donde la seguridad supera la disponibilidad. POC, pentesting, sistemas aislados

El patrón recomendado por OWASP es arrancar en DetectionOnly durante una o dos semanas, ver los logs, crear exclusiones por regla y path, y solo entonces pasar a SecRuleEngine On (bloqueo activo). Saltar directo a PL3 con bloqueo es la forma más rápida de tirar tu propio sitio.

Instalación: tres modos de despliegue

Coraza no es un binario monolítico. Es una librería que se integra según dónde corre tu tráfico:

  1. Como librería Go embebida: go get github.com/corazawaf/coraza/v3 en tu app o reverse proxy custom.
  2. Como módulo de Caddy v2: github.com/corazawaf/coraza-caddy/v2, primer soporte oficial y el camino más simple para HTTPS automático + WAF.
  3. Como plugin WASM de Traefik 3: github.com/jcchavezs/coraza-http-wasm-traefik, corre dentro del proceso de Traefik sin contenedor aparte.
  4. Como sidecar en Istio/Envoy: OCI image ghcr.io/corazawaf/coraza-proxy-wasm:0.5.0, montado como WasmPlugin en fase AUTHN.

El modo más común en homelab y deployments pequeños es Caddy + módulo Coraza. El más común en Kubernetes productivo es Istio + WasmPlugin. Los dos comparten la misma sintaxis SecLang, así que migrar reglas entre uno y otro es cuestión de copy-paste.

Configuración con Caddy v2 (caso real)

El módulo oficial coraza-caddy/v2 expone una directiva coraza_waf dentro del bloque del sitio. El directives recibe un string multilínea con la configuración SecLang completa. La carga de CRS se hace con load_owasp_crs, que importa el ruleset preempaquetado.

:443 {
    coraza_waf {
        load_owasp_crs
        directives `
            Include @coraza.conf-recommended
            Include @crs-setup.conf.example
            Include @owasp_crs/*.conf
            SecRuleEngine DetectionOnly
            SecDebugLog /var/log/coraza/debug.log
            SecDebugLogLevel 3
        `
        tx_id_req_header "X-Transaction-ID"
    }
    reverse_proxy backend:8080
}

Las dos primeras semanas quieres SecRuleEngine DetectionOnly. Eso le dice a Coraza que registre en debug.log cada request que matchea una regla, sin bloquear nada. Es la única forma sensata de ver qué tráfico legítimo está siendo marcado antes de activar bloqueo y empezar a devolver 403 a tus propios usuarios.

El tx_id_req_header propaga un ID de transacción al backend, útil para correlacionar logs entre Coraza y tu app. El formato del debug.log es el estándar ModSecurity audit log, así que cualquier parser existente (goaccess, go-modsecurity-parser) lo lee sin configuración adicional.

Probando que el WAF detecta

OWASP CRS mantiene un sandbox público (sandbox.coreruleset.org) y un set de pruebas go-ftw (Functional Testing for WAFs) con payloads conocidos. Antes de bloquear producción, lanza una batería local:

# XSS básico (debe aparecer en el audit log como regla 941100-xss)
curl -k "https://tu-dominio/?q=<script>alert(1)</script>"
# SQL injection (regla 942100-sqli)
curl -k "https://tu-dominio/api/items?id=1' OR '1'='1"
# Path traversal (regla 930130-lfi)
curl -k "https://tu-dominio/static/../../../../etc/passwd"
# Comando OS injection en header (regla 932100-rce)
curl -k -H "X-Forwarded-For: ; cat /etc/passwd" "https://tu-dominio/admin"

Cada uno de esos requests debe aparecer en debug.log con un Message que indique la regla que matcheó y el Severity de la amenaza. Si SecRuleEngine está en On, todos devuelven 403. Si está en DetectionOnly, devuelven 200 pero el request queda registrado. La idea es exactamente la misma que con cualquier IDS/IPS: ver antes que bloquear.

Activar bloqueo y manejar falsos positivos

Después de dos semanas en DetectionOnly, identificas los falsos positivos recurrentes y los excluyes por regla + path. El mecanismo es SecRuleUpdateTargetById, que cambia el target de una regla sin desactivarla globalmente.

# El editor WYSIWYG envía HTML legítimo que CRS marca como XSS (regla 941100)
SecRuleUpdateTargetById 941100 "!ARGS:body"
# El endpoint de webhooks recibe JSON con strings que parecen SQLi
SecRuleUpdateTargetById 942100 "!ARGS:payload"
# Desactivar regla completa solo si estás seguro (último recurso)
SecRuleRemoveById 941110

El ! (negación) antes del target es la sintaxis correcta para excluir; SecRuleRemoveById desactiva la regla entera y es lo que más cuesta defender ante una auditoría de seguridad. Empieza siempre por SecRuleUpdateTargetById con la variable específica. CRS 4.x añade además SecRuleUpdateActionById para cambiar solo la acción de una regla (de block a pass en un path concreto), que es aún más granular.

Cuándo NO usar Coraza

Con todo, Coraza no resuelve todos los problemas y hay escenarios donde encaja peor que otras alternativas:

  • Si ya tienes Cloudflare: Cloudflare WAF Managed Rules incluye reglas equivalentes a CRS PL1/PL2 en el edge, sin que despliegues nada. Tiene falso positivo menor en sus reglas pagadas y un dashboard más amigable. Coraza tiene sentido cuando quieres el WAF on-prem o en tu propia VPC.
  • Si necesitas protección DDoS de capa 7: Coraza no fue diseñado para rate limiting masivo ni para mitigación de slowloris. Para eso, combínalo con CrowdSec (blocklists compartidas) y fail2ban (rate limit por IP).
  • Si tu tráfico es < 100 req/s: la sobrecarga de CRS PL2+ empieza a ser notoria en CPU. Para sitios chicos, un buen reverse proxy con headers de seguridad (Content-Security-Policy, X-Frame-Options) cubre el 80% del riesgo sin WAF.
  • Si tu app es serverless puro: Coraza corre como proceso; en Lambda/Functions no aplica. En ese caso, AWS WAF o Cloudflare Workers con reglas equivalentes.

La regla de oro: WAF + hardening de headers + rate limiting son capas complementarias, no excluyentes. Coraza hace lo suyo, pero no sustituye al reverse proxy bien configurado.

Resumen

OWASP Coraza es la evolución natural de ModSecurity para stacks modernos: misma sintaxis SecLang, misma cobertura OWASP CRS 4.x, pero en Go, con licencia Apache-2.0, releases cada pocos meses y un módulo oficial para Caddy v2 más plugin WASM para Traefik 3 e Istio. La versión 3.7.0 (abril 2026) trae mejoras de performance en caché de transformaciones y alineación de severidades con ModSecurity.

El camino de adopción es: instalar el módulo en Caddy (o el plugin en Traefik), cargar OWASP CRS 4.29 vía load_owasp_crs, correr dos semanas en SecRuleEngine DetectionOnly, tunear exclusiones con SecRuleUpdateTargetById, y solo entonces activar SecRuleEngine On en PL1. Saltarse ese ciclo es la receta para llenar Slack de tickets “el login no anda” en la primera hora.

Para leer más

Preguntas frecuentes

¿Por qué Cómo configurar un WAF open source (Coraza) frente a tu app en 2026 es relevante para sysadmins Linux?

Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.

¿Qué necesito para empezar con Cómo configurar un WAF open?

Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.

¿Cuáles son los errores más comunes con Cómo configurar un WAF open?

Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.

¿Cuándo usar Cómo configurar un WAF open y cuándo una alternativa?

Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.

¿Cómo migro desde una configuración previa a Cómo configurar un WAF open?

Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

One thought on “Cómo configurar un WAF open source (Coraza) frente a tu app en 2026”

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *