Why a WAF at the Ingress Layer?

Kubernetes Ingress controllers handle routing, but they don't inspect HTTP payloads for attacks like SQL injection, XSS, or path traversal. Adding a Web Application Firewall (WAF) at the ingress layer — before traffic reaches your services — gives you a centralized chokepoint for threat detection and blocking.

Traefik v3 supports Coraza as a middleware plugin. Coraza is an open-source WAF engine that runs the OWASP Core Rule Set (CRS). The combination is entirely self-hosted, zero-licensing-cost, and runs natively inside your cluster.

Installation

We use the Traefik Helm chart with the Coraza plugin enabled at install time:

helm repo add traefik https://traefik.github.io/charts
helm install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace \
  --set "experimental.plugins.coraza.moduleName=github.com/jcchavezs/coraza-middleware-traefik" \
  --set "experimental.plugins.coraza.version=v0.2.1"

cert-manager for TLS

cert-manager handles all TLS certificate provisioning and renewal via Let's Encrypt. A single ClusterIssuer covers all namespaces:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: snaveenkumar0601@gmail.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: traefik

Coraza WAF Middleware

The Coraza middleware is defined as a Traefik Middleware resource. It loads the OWASP CRS rules and runs in detection mode first — you can switch to blocking mode once you've tuned the false positive rate.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: coraza-waf
  namespace: traefik
spec:
  plugin:
    coraza:
      directives: |
        SecRuleEngine DetectionOnly
        SecRequestBodyAccess On
        SecResponseBodyAccess On
        Include @owasp_crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
        Include @owasp_crs/REQUEST-901-INITIALIZATION.conf
        Include @owasp_crs/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
        Include @owasp_crs/REQUEST-941-APPLICATION-ATTACK-XSS.conf
        Include @owasp_crs/REQUEST-932-APPLICATION-ATTACK-RCE.conf
        SecAuditLog /dev/stdout
        SecAuditLogFormat JSON

Attaching WAF to Ingress

Apply the middleware to any Ingress via annotation:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    traefik.ingress.kubernetes.io/router.middlewares: traefik-coraza-waf@kubernetescrd
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
  tls:
    - hosts: [app.example.com]
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-service
                port:
                  number: 80

Tuning and False Positives

Running CRS out of the box in blocking mode will almost certainly cause false positives — especially for APIs that send JSON bodies. The recommended workflow:

  1. Start with SecRuleEngine DetectionOnly and monitor audit logs
  2. Identify false positive rule IDs from Kibana or log queries
  3. Add targeted exclusions for known-safe patterns
  4. Switch to SecRuleEngine On once false positive rate is acceptable

Results

After deploying this stack on the SIEMply Secure platform, we had full TLS automation across all services and WAF coverage on all externally-facing ingress routes. The Coraza audit logs (JSON format, shipped to Elasticsearch via Filebeat) gave us complete visibility into blocked and detected requests.

Running a WAF in detection mode for two weeks before enabling blocking is not optional — it's how you avoid breaking your own platform on day one.

Key Takeaways