Skip to content

Coraza WAF per-rule ErrorLogCallback duplicates SecAuditLog content (Envoy Gateway) #412

Description

@illrill

Description

I'm trying to use SecAuditEngine + SecAuditLog to emit one log event per transaction (which includes contextual TCP/HTTP information as well as all triggered WAF rules in the messages array). My goal is for each transaction I just get one "WAF log" + one "Envoy access log". This is the SecLang I need:

- SecDefaultAction "phase:1,nolog,auditlog,pass"
- SecDefaultAction "phase:2,nolog,auditlog,pass"
- SecDefaultAction "phase:3,nolog,auditlog,pass"
- SecDefaultAction "phase:4,nolog,auditlog,pass"
- SecDefaultAction "phase:5,nolog,auditlog,pass"

The problem is that Include @crs-setup.conf already sets below:

SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"

If I try to override this in my own directives, it fails with:

[2026-05-07 09:25:24.791][1][warning][dynamic_modules] [source/extensions/dynamic_modules/abi_impl.cc:57] Failed to load configuration: failed to create WAF from directives: invalid WAF config from string: failed to parse string: failed to compile the directive "secrule": SecDefaultAction already defined for this phase: phase:1,nolog,auditlog,pass
[2026-05-07 09:25:24.791][1][warning][config] [source/extensions/config_subscription/grpc/delta_subscription_state.cc:282] delta config for type.googleapis.com/envoy.config.listener.v3.Listener rejected: Error adding/updating listener(s) gateway/envoy-gateway-proxy/https-local: Failed to create filter config: Failed to initialize dynamic module
[2026-05-07 09:25:24.791][1][warning][config] [source/extensions/config_subscription/grpc/grpc_subscription_impl.cc:138] gRPC config for type.googleapis.com/envoy.config.listener.v3.Listener rejected: Error adding/updating listener(s) gateway/envoy-gateway-proxy/https-local: Failed to create filter config: Failed to initialize dynamic module

So the only option I've found is to not use Include @crs-setup.conf at all, and instead replicate the same directives myself. This is okay-ish since there's not much in there, but not ideal because risks CRS versioning mismatches + risks missing out on any potential additions/changes that maintainers make to crs-setup.conf going forward.

- SecDefaultAction "phase:1,nolog,auditlog,pass"
- SecDefaultAction "phase:2,nolog,auditlog,pass"
- SecDefaultAction "phase:3,nolog,auditlog,pass"
- SecDefaultAction "phase:4,nolog,auditlog,pass"
- SecDefaultAction "phase:5,nolog,auditlog,pass"
- SecAction "id:900120,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.25.0',setvar:tx.early_blocking=1"
- SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.25.0',setvar:tx.crs_setup_version=4250"

Can we somehow expose configuration options for logging? For example in EEP in EG:

  dynamicModule:
    - name: composer
      filterName: coraza-waf
      config:
        mode: REQUEST_ONLY
        telemetry:
          auditLog: true
          ruleLog: false
        directives: ...

Or can we split up crs-setup.conf into two maintained files crs-setup.conf + default-actions.conf?

Full reproduction

Configuration

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: envoy-gateway-proxy
  namespace: {{ .Release.Namespace }}
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        container:
          env:
            - name: GODEBUG
              value: "cgocheck=0"
          volumeMounts:
            - name: dynamic-modules
              mountPath: /etc/envoy/dynamic-modules
              readOnly: true
        pod:
          volumes:
            - name: dynamic-modules
              image:
                reference: ghcr.io/tetratelabs/built-on-envoy/composer:0.6.0-dev
                pullPolicy: IfNotPresent
  dynamicModules:
    - name: composer
      source: 
        type: Local
        local:
          path: /etc/envoy/dynamic-modules/libcomposer.so
      doNotClose: true
      loadGlobally: false
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
  name: envoy-gateway-waf
  namespace: {{ .Release.Namespace }}
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: envoy-gateway-proxy
  dynamicModule:
    - name: composer
      filterName: coraza-waf
      config:
        mode: REQUEST_ONLY
        directives:
          - Include @coraza.conf
          - SecRequestBodyLimitAction ProcessPartial
          - SecAuditEngine RelevantOnly
          - SecAuditLog /dev/stdout
          - SecRuleEngine On
          - Include @crs-setup.conf
          - Include @owasp_crs/*.conf

I get both audit log + logs from individual rules

{
  "level": "error",
  "ts": 1778142301.534977,
  "caller": "coraza/config.go:87",
  "msg": "[client \"00.000.000.00\"] Coraza: Warning. SQL Injection Attack Detected via libinjection [file \"@owasp_crs/REQUEST-942-APPLICATION-ATTACK-SQLI.conf\"] [line \"5922\"] [id \"942100\"] [rev \"\"] [msg \"SQL Injection Attack Detected via libinjection\"] [data \"Matched Data: 1&1 found within ARGS:id: 1 OR 1=1\"] [severity \"critical\"] [ver \"OWASP_CRS/4.25.0\"] [maturity \"0\"] [accuracy \"0\"] [tag \"application-multi\"] [tag \"language-multi\"] [tag \"platform-multi\"] [tag \"attack-sqli\"] [tag \"paranoia-level/1\"] [tag \"OWASP_CRS\"] [tag \"OWASP_CRS/ATTACK-SQLI\"] [tag \"capec/1000/152/248/66\"] [hostname \"127.0.0.1\"] [uri \"https://bogus-lab.mcs-work-lab6849-aks.lab.svc.hmgroup.io/anything?id=1+OR+1%3D1\"] [unique_id \"41cc52e8-f8b7-912b-9444-76f3a6441658\"]",
  "severity": "critical",
  "stacktrace": "github.com/tetratelabs/built-on-envoy/extensions/composer/waf/coraza.NewWAFFromDirectives.newSlogError.func1\n\t/workspace/waf/coraza/config.go:87\ngithub.com/corazawaf/coraza/v3/internal/corazawaf.(*Transaction).MatchRule\n\t/go/pkg/mod/github.com/corazawaf/coraza/v3@v3.7.0/internal/corazawaf/transaction.go:605\ngithub.com/corazawaf/coraza/v3/internal/corazawaf.(*Rule).doEvaluate\n\t/go/pkg/mod/github.com/corazawaf/coraza/v3@v3.7.0/internal/corazawaf/rule.go:401\ngithub.com/corazawaf/coraza/v3/internal/corazawaf.(*Rule).Evaluate\n\t/go/pkg/mod/github.com/corazawaf/coraza/v3@v3.7.0/internal/corazawaf/rule.go:185\ngithub.com/corazawaf/coraza/v3/internal/corazawaf.(*RuleGroup).Eval\n\t/go/pkg/mod/github.com/corazawaf/coraza/v3@v3.7.0/internal/corazawaf/rulegroup.go:261\ngithub.com/corazawaf/coraza/v3/internal/corazawaf.(*Transaction).ProcessRequestBody\n\t/go/pkg/mod/github.com/corazawaf/coraza/v3@v3.7.0/internal/corazawaf/transaction.go:1087\ngithub.com/tetratelabs/built-on-envoy/extensions/composer/waf.(*wafPlugin).handleRequestBody\n\t/workspace/waf/waf.go:435\ngithub.com/tetratelabs/built-on-envoy/extensions/composer/waf.(*wafPlugin).OnRequestHeaders\n\t/workspace/waf/waf.go:231\ngithub.com/envoyproxy/envoy/source/extensions/dynamic_modules/sdk/go/abi.envoy_dynamic_module_on_http_filter_request_headers\n\t/go/pkg/mod/github.com/envoyproxy/envoy/source/extensions/dynamic_modules@v0.0.0-20260423231439-f1dd21b16c24/sdk/go/abi/internal.go:1603\n_cgoexp_0239f516b2d1_envoy_dynamic_module_on_http_filter_request_headers\n\t/go/pkg/mod/github.com/envoyproxy/envoy/source/extensions/dynamic_modules@v0.0.0-20260423231439-f1dd21b16c24/sdk/go/abi/internal.go:1577\nruntime.cgocallbackg1\n\t/usr/local/go/src/runtime/cgocall.go:466\nruntime.cgocallbackg\n\t/usr/local/go/src/runtime/cgocall.go:362\nruntime.cgocallback\n\t/usr/local/go/src/runtime/asm_amd64.s:1160"
}
{
  "level": "info",
  "ts": 1778142301.535425,
  "caller": "coraza/config.go:93",
  "msg": "[client \"00.000.000.00\"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 5) [file \"@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf\"] [line \"7701\"] [id \"949110\"] [rev \"\"] [msg \"Inbound Anomaly Score Exceeded (Total Score: 5)\"] [data \"\"] [severity \"unknown\"] [ver \"OWASP_CRS/4.25.0\"] [maturity \"0\"] [accuracy \"0\"] [tag \"anomaly-evaluation\"] [tag \"OWASP_CRS\"] [hostname \"127.0.0.1\"] [uri \"https://example.com/anything?id=1+OR+1%3D1\"] [unique_id \"41cc52e8-f8b7-912b-9444-76f3a6441658\"]",
  "severity": "unknown"
}
{
  "transaction": {
    "timestamp": "2026/05/07 08:25:01",
    "unix_timestamp": 1778142301520084241,
    "id": "41cc52e8-f8b7-912b-9444-76f3a6441658",
    "client_ip": "00.000.000.00",
    "client_port": 18389,
    "host_ip": "127.0.0.1",
    "host_port": 80,
    "server_id": "example.com",
    "request": {
      "method": "GET",
      "protocol": "HTTP/2",
      "uri": "https://example.com/anything?id=1+OR+1%3D1",
      "http_version": "",
      "headers": {
        ":authority": [
          "example.com"
        ],
        ":method": [
          "GET"
        ],
        ":path": [
          "/anything?id=1+OR+1%3D1"
        ],
        ":scheme": [
          "https"
        ],
        "accept": [
          "*/*"
        ],
        "host": [
          "example.com"
        ],
        "user-agent": [
          "curl/8.7.1"
        ],
        "x-envoy-external-address": [
          "00.000.000.00"
        ],
        "x-forwarded-for": [
          "00.000.000.00"
        ],
        "x-forwarded-proto": [
          "https"
        ],
        "x-request-id": [
          "41cc52e8-f8b7-912b-9444-76f3a6441658"
        ]
      },
      "body": "",
      "files": null,
      "args": {},
      "length": 0
    },
    "response": {
      "protocol": "",
      "status": 0,
      "headers": {},
      "body": ""
    },
    "producer": {
      "connector": "",
      "version": "",
      "server": "",
      "rule_engine": "On",
      "stopwatch": "1778142301520084241 15634996; combined=1845216, p1=511911, p2=1276041, p3=0, p4=0, p5=57264",
      "rulesets": [
        "OWASP_CRS/4.25.0"
      ]
    },
    "highest_severity": "",
    "is_interrupted": true
  },
  "messages": [
    {
      "actionset": "",
      "message": "",
      "error_message": "[client \"00.000.000.00\"] Coraza: Warning. SQL Injection Attack Detected via libinjection [file \"@owasp_crs/REQUEST-942-APPLICATION-ATTACK-SQLI.conf\"] [line \"5922\"] [id \"942100\"] [rev \"\"] [msg \"SQL Injection Attack Detected via libinjection\"] [data \"Matched Data: 1\u00261 found within ARGS:id: 1 OR 1=1\"] [severity \"critical\"] [ver \"OWASP_CRS/4.25.0\"] [maturity \"0\"] [accuracy \"0\"] [tag \"application-multi\"] [tag \"language-multi\"] [tag \"platform-multi\"] [tag \"attack-sqli\"] [tag \"paranoia-level/1\"] [tag \"OWASP_CRS\"] [tag \"OWASP_CRS/ATTACK-SQLI\"] [tag \"capec/1000/152/248/66\"] [hostname \"127.0.0.1\"] [uri \"https://example.com/anything?id=1+OR+1%3D1\"] [unique_id \"41cc52e8-f8b7-912b-9444-76f3a6441658\"]",
      "data": null
    },
    {
      "actionset": "",
      "message": "",
      "error_message": "[client \"00.000.000.00\"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 5) [file \"@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf\"] [line \"7701\"] [id \"949110\"] [rev \"\"] [msg \"Inbound Anomaly Score Exceeded (Total Score: 5)\"] [data \"\"] [severity \"unknown\"] [ver \"OWASP_CRS/4.25.0\"] [maturity \"0\"] [accuracy \"0\"] [tag \"anomaly-evaluation\"] [tag \"OWASP_CRS\"] [hostname \"127.0.0.1\"] [uri \"https://example.com/anything?id=1+OR+1%3D1\"] [unique_id \"41cc52e8-f8b7-912b-9444-76f3a6441658\"]",
      "data": null
    }
  ]
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    extensionsIssues related to extensionswafIssues related to the WAF extension

    Fields

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions