About WAF
Learn more about how WAF is implemented in Solo Enterprise for kgateway.
Solo Enterprise for kgateway includes a built-in Web Application Firewall (WAF) that is powered by the open source Coraza rule engine. The WAF inspects HTTP traffic in real time at the gateway and blocks attacks, such as SQL injection, cross-site scripting (XSS), and remote code execution before they reach your apps. You define security policies in a Kubernetes-native WAFPolicy resource and attach them to routes or gateways.
Enablement
WAF is configured as a shared extension in the EnterpriseKgatewayParameters resource and must be attached to a GatewayClass. You cannot attach a shared extension that is defined in an EnterpriseKgatewayParameters resource to a Gateway.
When you deploy a gateway proxy with that GatewayClass, Solo Enterprise for kgateway automatically spins up a WAF server for you. The WAF server is shared by all gateways with the same GatewayClass.
For more information, see Enable the WAF server.
Common use cases
- OWASP CRS: Enable the OWASP Core Rule Set (CRS) v4 to get broad protection against common web attacks without writing rules from scratch.
- IP filtering: Block or allow traffic by client IP address or CIDR range.
- Custom rules: Write Coraza SecLang rules inline or load them from ConfigMaps to enforce application-specific security policies.
WAF server architecture
The built-in WAF server implements Envoy’s External Processing (ExtProc) API and uses the open source Coraza rule engine to inspect and filter HTTP traffic.
The Coraza rule engine processes HTTP traffic in four sequential phases. Each phase covers a distinct stage of the request-response lifecycle.
| Phase | Name |
|---|---|
| 1 | Request headers |
| 2 | Request body |
| 3 | Response headers |
| 4 | Response body |
The following diagram shows how a request flows through the four WAF inspection phases. The waf-server can block the request or response at any phase by returning an immediate response to Envoy.
sequenceDiagram
participant Client
participant GW as Gateway
participant WAF as waf-server (ExtProc)
participant UP as Upstream
Client->>GW: HTTP request
rect rgba(100, 150, 255, 0.15)
Note over Client,UP: Request Processing
Note over GW,WAF: Phase 1 — Request Headers
GW->>WAF: Phase 1 — request headers
alt Rule triggered
WAF-->>GW: Block (immediate response)
GW-->>Client: 403 Forbidden
else Pass
WAF-->>GW: Continue
end
Note over GW,WAF: Phase 2 — Request Body
GW->>WAF: Phase 2 — request body
alt Rule triggered
WAF-->>GW: Block (immediate response)
GW-->>Client: 403 Forbidden
else Pass
WAF-->>GW: Continue
end
end
GW->>UP: Forwarded request
UP-->>GW: HTTP response
rect rgba(100, 200, 130, 0.15)
Note over Client,UP: Response Processing
Note over GW,WAF: Phase 3 — Response Headers
GW->>WAF: Phase 3 — response headers
alt Rule triggered
WAF-->>GW: Block (immediate response)
GW-->>Client: Error response
else Pass
WAF-->>GW: Continue
end
Note over GW,WAF: Phase 4 — Response Body
GW->>WAF: Phase 4 — response body
alt Rule triggered
WAF-->>GW: Block (immediate response)
GW-->>Client: Error response
else Pass
WAF-->>GW: Continue
end
end
GW-->>Client: HTTP response
Coraza variables
Each Coraza phase exposes a different set of Coraza variables that you can use to access data from a request or response. When you specify WAF rules, always specify the earliest phase in which the required data is available. For example, to access request headers, use phase:1.
The following table shows the different phases and the Coraza variables that are available in each phase:
| Phase | Name | Trigger | Key Coraza variables available |
|---|---|---|---|
| 1 | Request headers | After request headers are received, before the body is buffered | REQUEST_HEADERS, REQUEST_METHOD, REQUEST_URI, REQUEST_PROTOCOL, REMOTE_ADDR, REMOTE_PORT, SERVER_ADDR, SERVER_PORT |
| 2 | Request body | After the full request body is buffered | Everything from Phase 1, plus REQUEST_BODY, ARGS, ARGS_GET, ARGS_POST, FILES |
| 3 | Response headers | After response headers are received from upstream | RESPONSE_HEADERS, RESPONSE_STATUS, RESPONSE_PROTOCOL |
| 4 | Response body | After the full response body is buffered | Everything from Phase 3, plus RESPONSE_BODY |
All request headers and the full request body are buffered before the WAF finishes evaluating them and before the request is forwarded upstream. Expect added latency proportional to body size when body inspection (Phase 2) is enabled.
During Phase 1, the WAF server receives both the request headers and the connection metadata (client IP, client port, server IP, server port) from Envoy. Rules that use REMOTE_ADDR, REMOTE_PORT, SERVER_ADDR, or SERVER_PORT must use phase:1.
Header and body processing modes
By default, the WAF server inspects request headers (Phase 1) and response headers (Phase 3). You can use the processingConfig setting in your WAFPolicy resource to change which phases are active during WAF rule processing.
Note that body inspection adds latency and memory overhead to the WAF server because the full body must be buffered before it can be inspected.
The following table shows the different WAF processing configuration modes and how they map to the Coraza rule phases.
| Request/response | WAF processing mode | Coraza phase | Notes |
|---|---|---|---|
| Request | Headers (default) | Phase 1 | Inspects request headers only. This mode is turned on by default. |
| Request | HeadersAndBody | Phases 1 and 2 | Inspects request headers and body. The full request body is buffered before the upstream receives the request. |
| Response | Headers (default) | Phase 3 | Inspects response headers only. This mode is turned on by default. |
| Response | HeadersAndBody | Phases 3 and 4 | Inspects response headers and body. The full response body is buffered before the client receives the response. |
| Response | None | — | Skips all response inspection. Phases 3 and 4 are not run. |
Headers. Request headers carry connection metadata, such as the client IP address and port that the WAF processing depends on.For an example, see Request and response body inspection.
Body buffering
The WAF server does not support streaming. Envoy buffers the full request or response body before forwarding it to the WAF server for inspection. Body buffering is triggered when you configured the respective body processing mode in the processingConfig field of your WAFPolicy.
The ListenerPolicy provides the following settings to set the body buffer size:
perConnectionBufferLimitBytes: The total buffer limit per connection. The default is 1MiB (1048576 bytes). For HTTP/1.1 traffic, this is effectively a per-request limit. For HTTP/2, a single connection can carry multiple concurrent request streams. Because of that, the limit is shared across all multiplexed streams. When the limit is exceeded, requests are rejected with a 413 response.maxRequestSize: The buffer limit for an individual request or response, regardless of protocol.
To increase both the perConnectionBufferLimitBytes and maxRequestSize settings, create a ListenerPolicy that targets your Gateway:
apiVersion: gateway.kgateway.dev/v1alpha1
kind: ListenerPolicy
metadata:
name: waf-buffer-limit
namespace: kgateway-system
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: http
default:
perConnectionBufferLimitBytes: 10485760
buffer:
maxRequestSize: '1024'Both settings can be used together. If you increase maxRequestSize above the default 1MiB perConnectionBufferLimitBytes, you must also raise perConnectionBufferLimitBytes; otherwise the connection limit is reached first and requests are rejected with a 413 response.
For more information, see Buffering.
WAF rule definition
The Coraza rule engine implements the OWASP Coraza Seclang directive language, which is compatible with ModSecurity v3 directives. You use this language to write your WAF rules. The WAF server evaluates each request and response against those rules.
To provide your WAF rules to the WAF server, Solo Enterprise for kgateway uses a dedicated WAFPolicy resource. You can define your rules as follows:
- Inline: Write directives directly in the
WAFPolicyresource as strings. - ConfigMap: Store directives in a Kubernetes ConfigMap and reference it from the
WAFPolicy. This approach makes it easier to manage and update rules without modifying the policy itself, because the WAF server watches for ConfigMap changes and reloads rules automatically.
Custom Coraza rules
Write custom WAF rules by using the Coraza Seclang directive language, which is compatible with ModSecurity v3 directives.
For more information, see Custom Coraza rules.
IP-based filtering
You can use Coraza’s REMOTE_ADDR variable to block or allow traffic based on the client IP address. By default, REMOTE_ADDR is set to the socket-level peer IP. You can configure the gateway to derive the REMOTE_ADDR variable value from the X-Forwarded-For header instead. For more information, see IP-based filtering.
OWASP Core Rule Set (CRS)
You can optionally enable the OWASP Core Rule Set (CRS) in your WAFPolicy. CRS is an open source set of generic detection rules that protect against a wide range of attacks, including the OWASP Top Ten. Solo Enterprise for kgateway bundles CRS version 4. For the full explanation and a setup example, see OWASP Core Rule Set.
Disable WAF for a route
If a WAF policy is applied at the Gateway level, you can disable it for specific routes by setting entWAF.disable in the route-level EnterpriseKgatewayTrafficPolicy.
apiVersion: enterprisekgateway.solo.io/v1alpha1
kind: EnterpriseKgatewayTrafficPolicy
metadata:
name: httpbin-no-waf
namespace: httpbin
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: httpbin
entWAF:
disable: {}Fail-closed behavior
The WAF server fails closed by default. If a WAFPolicy contains invalid directives that fail the Coraza WAF engine compilation, the server denies all requests that match the policy with a 500 response until the policy is corrected. To detect invalid policies before they affect traffic, check the WAFPolicy status. For more information, see Debug WAF.