Extensions
Per-rule rate limiting and Coraza WAF, attached with the ExtensionRef filter and configured from plain ConfigMaps.
krouter extends route rules through the Gateway API
ExtensionRef filter,
without defining any custom resource. An extension is a plain
ConfigMap
or Secret
holding HCL documents, referenced from
an HTTPRoute or GRPCRoute rule. The referenced object lives in the route’s
own namespace (LocalObjectReference semantics, so cross-namespace
references are not expressible) and each key belongs to exactly one kind:
| Key | Kind | Contents |
|---|---|---|
ratelimit.hcl | ConfigMap | Token-bucket rate limiting |
waf.hcl | ConfigMap | Coraza web application firewall ruleset |
auth.hcl | Secret | Authentication and authorization |
A misplaced key (such as auth.hcl in a ConfigMap) invalidates the
reference. This page covers the two ConfigMap keys; authentication has
its own page.
The filter and the ConfigMap it points to always travel together:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api
namespace: shop
spec:
parentRefs:
- name: public
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: ExtensionRef
extensionRef:
group: ""
kind: ConfigMap
name: api-protection
backendRefs:
- name: api
port: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: api-protection
namespace: shop
data:
ratelimit.hcl: |
version = 1
rate_limit {
requests = 100
window = "1m"
key = "client_ip"
}
waf.hcl: |
version = 1
waf {
directives = <<-EOT
Include @coraza.conf-recommended
Include @crs-setup.conf.example
Include @owasp_crs/*.conf
SecRuleEngine On
EOT
}
Rate limiting
ratelimit.hcl configures a token bucket applied per rule and per client
key:
requestsandwindowset the refill rate (tokens added per window);burstis the optional bucket capacity (defaulting torequests).keybuckets requests byclient_ip(the resolved client address: the downstream peer, or what its forwarded chain says when the Gateway trusts that peer) or byheader:<Name>(the first value of that request header, with a shared anonymous bucket for requests lacking it).- A rejected request is answered with
status(default429) and a Retry-After header carrying the whole seconds until the next token.
Enforcement is local to each data-plane pod: there is no cluster-wide
coordination, so the effective limit is per pod and you should size
requests accordingly. Buckets are local to a configuration generation
and reset when the affected route changes, exactly like round-robin
counters on a table swap.
Web application firewall
waf.hcl carries a Coraza (SecLang) ruleset. The
OWASP Core Rule Set and the Coraza recommended
configuration are embedded in the krouter binary and reachable through
the @-includes shown above; any other Include path reads rule files
from the pod filesystem, provided at deployment time by extending the
image or mounting volumes (see
the WAF tutorial for both recipes).
- The request phases (request headers, then request body) are inspected
before any byte reaches a backend. On HTTPRoute rules the body is
buffered and inspected up to the engine’s limits (
SecRequestBodyLimitand related directives), then replayed to the backend unchanged. On GRPCRoute rules the request message is buffered and inspected the same way, which suits unary calls; for streaming calls the buffering adds latency, so scope the WAF to unary services. The stock CRS also rejects theapplication/grpccontent type at the header phase (rule 920420) unless a later fragment allows it. - A matching deny rule interrupts the request: the client receives the
interruption’s status (
403when the ruleset sets none) and the backend never sees the request. - Response phases are not inspected, which preserves response streaming.
WebSocket upgrades are inspected on the handshake request itself, before the connection is hijacked into a tunnel; once the tunnel is established its frames are neither inspected nor counted.
Composition and status
Several ExtensionRef filters on one rule compose in filter list order,
which is the modularity mechanism: a base ConfigMap can be shared by many
routes and refined by later, route-specific ones.
ratelimit.hcldocuments merge attribute by attribute (a later document overrides the attributes it sets and inherits the rest).waf.hcldocuments concatenate their directives into one ruleset, so a base ConfigMap can carry the CRS include and a later one the application-specific tuning.
Extensions follow the same fail-closed contract as the rest of the Gateway API:
- A reference with any group other than core or any kind other than
ConfigMaporSecret, or anExtensionRefunderbackendRefs[].filters, rejects the route in full with reasonUnsupportedValue. - A referenced object that is missing, carries no key valid for its kind,
holds invalid HCL, produces an incomplete merged rate-limit
configuration, or whose concatenated WAF directives fail to build keeps
the route accepted but sets
ResolvedRefstoFalsewith reasonInvalidExtensionRef. Requests matching the affected rule are answered500, never silently skipped, mirroring how unresolvable backends behave.
The control plane validates a WAF ruleset by building the engine once at
compile time, so a broken program surfaces as InvalidExtensionRef before
any request reaches it.
Enforcement order
For a request matched to a rule carrying extensions, enforcement runs rate limiting first (cheapest, so a limited request consumes no WAF CPU), then authentication, then the WAF request phases, then every other filter and gateway-produced response (CORS answers, redirects, mirrors, header modifiers, forwarding). A request rejected by an extension is never mirrored, redirected, answered with CORS headers, or forwarded.
See Observability for the decision metrics and the access-log fields that record extension rejections.