kubeWAF is under active development — feedback and stars on GitHub are very welcome!
kubeWAFkubeWAF
kubeWAFmodsecurity-proxy-wasmpow-proxy-wasm

Envoy Gateway

Protect traffic with kubeWAF and Envoy Gateway

Protect Gateway API traffic when your cluster runs Envoy Gateway. kubeWAF does not create EnvoyExtensionPolicy anymore. Instead it:

  1. Publishes modsecurity-proxy-wasm config over gRPC ECDS
  2. Injects the filter slot via the Envoy Gateway Extension Server

For the full multi-provider model see Data plane (ECDS).

How it works

Prerequisites

  1. Envoy Gateway installed (e.g. GatewayClass eg)
  2. Gateway API CRDs
  3. kubeWAF operator with dataplane ports (Helm chart defaults)
  4. Wasm binary available to the operator (dataplane.modsecurityWasmSourceURL / modsecurityWasmFile, or mounted under /wasm/)
  5. Envoy Gateway extensionManager pointing at kubeWAF (below)

Configure Envoy Gateway Extension Server

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyGateway
provider:
  type: Kubernetes
gateway:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
extensionManager:
  policyResources:
    - group: waf.kubewaf.io
      version: v1beta1
      kind: WAF
  hooks:
    xdsTranslator:
      post:
        - HTTPListener
        - Translation
  service:
    fqdn:
      # Match your Helm release Service name
      hostname: kubewaf-ecds.kubewaf-system.svc.cluster.local
      port: 5005

Apply to the Envoy Gateway config (often a ConfigMap in envoy-gateway-system) and restart the controller.

Also grant the Envoy Gateway ServiceAccount permission to list/watch wafs (and optionally patch status):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kubewaf-envoy-gateway-waf-reader
rules:
  - apiGroups: [waf.kubewaf.io]
    resources: [wafs, wafs/status]
    verbs: [get, list, watch, update, patch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubewaf-envoy-gateway-waf-reader
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubewaf-envoy-gateway-waf-reader
subjects:
  - kind: ServiceAccount
    name: envoy-gateway
    namespace: envoy-gateway-system

Bootstrap: static kubewaf_ecds cluster (required)

Envoy rejects ECDS filter stubs unless the gRPC cluster named in ApiConfigSource is a bootstrap static (non-EDS) cluster. Adding kubewaf_ecds only via CDS (PostTranslateModify) produces:

Error adding/updating listener(s) ...:
ApiConfigSource must have a statically defined non-EDS cluster: 'kubewaf_ecds'

Patch the EnvoyProxy used by your GatewayClass (JSONPatch appends to the default bootstrap):

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: envoy-proxy-config
  namespace: envoy-gateway-system
spec:
  bootstrap:
    type: JSONPatch
    jsonPatches:
      - op: add
        path: /static_resources/clusters/-
        value:
          name: kubewaf_ecds
          type: STRICT_DNS
          connect_timeout: 2s
          lb_policy: ROUND_ROBIN
          typed_extension_protocol_options:
            envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
              "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
              explicit_http_config:
                http2_protocol_options: {}
          load_assignment:
            cluster_name: kubewaf_ecds
            endpoints:
              - lb_endpoints:
                  - endpoint:
                      address:
                        socket_address:
                          address: kubewaf-ecds.kubewaf-system.svc.cluster.local
                          port_value: 18001

Hostname/port must match the kubeWAF ECDS Service (kubewaf-ecds port 18001). Restart Envoy proxy pods after changing bootstrap.

Basic example

Protect an entire Gateway:

apiVersion: waf.kubewaf.io/v1beta1
kind: WAF
metadata:
  name: shop-waf
  namespace: shop
spec:
  engine: ModSecurity
  provider:
    type: EnvoyGateway   # default if omitted (Auto → EnvoyGateway)
  parentRefs:
    targetRef:
      group: gateway.networking.k8s.io
      kind: Gateway
      name: external-gateway
  ruleRefs:
  - kind: RuleSet
    name: shop-rules
    namespace: shop
  crsEnable: true
  logLevel: 3

You may also target HTTPRoute (same namespace). Prefer Gateway targets so the Extension Server can match listeners reliably.

CRS + custom rules

spec:
  crsEnable: true
  crs:
    paranoiaLevel: 2
    inboundAnomalyThreshold: 10
    removeById: [942100]
  ruleRefs:
  - kind: RuleSet
    name: baseline
    namespace: platform
  - kind: RuleSet
    name: shop-specific
    namespace: shop

Directive ordering is enforced by the operator (setup → CRS include → exclusions → user rules).

Wasm image vs HTTP binary

FieldRole
engine: ModSecurityProduct engine — modsecurity-proxy-wasm
wasmHTTPOverride URL Envoy uses to download the .wasm binary
wasmImageOCI reference (documentation / future); not used alone for ECDS fetch
Operator modsecurityWasmSourceURL / modsecurityWasmFileLoad binary into the operator; default fetch URL points at operator :18002

Observing status

kubectl get waf shop-waf -n shop -o yaml

Expect:

  • status.provider: EnvoyGateway
  • status.slotKind: ExtensionServer
  • status.ecdsResourceName: kubewaf/shop/shop-waf
  • Ready=True, ReferencesResolved=True

Debugging

  1. Rules not applied

    • kubectl describe waf … → conditions
    • Envoy Gateway logs: extension server errors
    • Envoy admin: filter chain contains kubewaf/…
  2. Wasm load failures

    • Curl the operator wasm endpoint from a debug pod: http://kubewaf-ecds.kubewaf-system.svc:18002/wasm/modsecurity-proxy-wasm.wasm
    • Check X-Checksum-Sha256 matches status / ECDS
  3. 403 on every request

    • Missing CRS init / thresholds — enable crsEnable or set thresholds via spec.crs
  4. Extension Server not called

    • Confirm extensionManager hostname/port
    • NetworkPolicy must allow EG → operator:5005

Common patterns

Protect everything on a Gateway

One WAF targeting the Gateway — all attached HTTPRoutes inherit the filter.

Per-route policies

Multiple WAF objects with more specific parentRefs (HTTPRoute). Extension Server injects all matching configs for the listener.

Staging vs production

Different RuleSets (staging-strict vs production-balanced) referenced by different WAF objects.

Limitations

  • Full HTTP support via Gateway API; TCP/TLS depends on EG Wasm capabilities
  • Requires EG Extension Server privilege (platform-admin setup)
  • Old EnvoyExtensionPolicy objects from prior kubeWAF versions are unused — delete them

On this page