Data plane (ECDS)
How kubeWAF pushes config over ECDS to Envoy-based gateways
This guide is the authoritative description of how kubeWAF pushes config into Envoy and how Envoy Gateway, Istio, and Cilium attach the filter.
Overview
Key idea: rule updates are pure ECDS publishes. Platform-specific resources only install stubs. Engines and challenge modules are separate wasm binaries served by the operator (see WAF engine and challenge).
Architecture deep dive
What Envoy sees
ECDS resource naming
| Item | Value |
|---|---|
| WAF filter name | kubewaf/<namespace>/<waf-name> |
| Challenge filter name | kubewaf/<namespace>/<waf-name>/challenge |
| Example | kubewaf/shop/shop-waf |
| Cluster for ECDS | kubewaf_ecds |
| Cluster for wasm fetch | kubewaf_wasm_code |
Rule update vs slot update
Wasm binary delivery (multi-module)
Envoy must HTTP-fetch each .wasm (OCI alone is not used on pure ECDS).
Option A — Operator hosts modules (recommended)
# Helm values — paths default under /wasm
dataplane:
modsecurityWasmFile: /wasm/modsecurity-proxy-wasm.wasm
challengeWasmFile: /wasm/challenge-proxy-wasm.wasm
# Or download once at startup:
# modsecurityWasmSourceURL: https://…/modsecurity-proxy-wasm.wasm
# challengeWasmSourceURL: https://…/challenge-proxy-wasm.wasmBuild monorepo artifacts:
make wasm-build # → dist/wasm/*.wasmDefault Envoy URLs (operator Service):
http://<release>-ecds.<ns>.svc:18002/wasm/modsecurity-proxy-wasm.wasm
http://<release>-ecds.<ns>.svc:18002/wasm/challenge-proxy-wasm.wasmOption B — Per-WAF / external URL
spec:
engine: ModSecurity
wasmHTTP: https://cdn.example.com/modsecurity-proxy-wasm.wasm
wasmSHA256: "<hex>"
challenge:
enabled: true
wasmHTTP: https://cdn.example.com/challenge-proxy-wasm.wasmOperator guides: WAF engine · challenge.
Provider: Envoy Gateway
Enable Extension Server on Envoy Gateway
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:
hostname: kubewaf-ecds.kubewaf-system.svc.cluster.local
port: 5005Restart Envoy Gateway after changing this config.
Example WAF
apiVersion: waf.kubewaf.io/v1beta1
kind: WAF
metadata:
name: shop-waf
namespace: shop
spec:
provider:
type: EnvoyGateway
parentRefs:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: external
crsEnable: true
ruleRefs:
- kind: RuleSet
name: shop-rulesFull guide: Envoy Gateway Integration.
Provider: Istio
No Istio control-plane flag is required. kubeWAF creates the EnvoyFilter.
spec:
provider:
type: Istio
istio:
workloadSelector:
istio: ingressgateway
context: GATEWAYFull guide: Istio Integration.
Provider: Cilium
spec:
provider:
type: Cilium
cilium:
serviceName: shop-frontend
serviceNamespace: shopCilium Envoy build
Creating the CEC is always supported. Whether Wasm/ECDS filters run depends on your Cilium Envoy feature set. See Cilium Integration.
Multi-replica / HA
| Component | Runs on | Leader election |
|---|---|---|
| ECDS gRPC | every pod | no |
| Wasm HTTP | every pod | no |
| EG Extension Server | every pod | no |
| Dataplane sync controller | every pod | no |
| WAF controller (status, slots, finalizers) | leader | yes |
| Inventory metrics | leader | yes |
Helm defaults:
replicaCount: 2
leaderElection:
enabled: true
podDisruptionBudget:
enabled: true
minAvailable: 1Status fields
kubectl get waf shop-waf -o yaml| Field | Meaning |
|---|---|
status.provider | Resolved provider |
status.engine | WAF engine (e.g. ModSecurity) |
status.challengeEnabled | PoW filter installed |
status.ecdsResourceName | Primary WAF ECDS name |
status.ecdsVersion | Snapshot counter |
status.slotKind | ExtensionServer / EnvoyFilter / CiliumEnvoyConfig |
status.slotName | Platform object name (if any) |
status.conditions[Ready] | Overall health |
Operator flags (summary)
| Flag | Default | Purpose |
|---|---|---|
--leader-elect | true | Multi-replica safety for writes |
--ecds-bind-address | :18001 | ECDS listen |
--extension-server-bind-address | :5005 | EG hooks |
--wasm-serve-bind-address | :18002 | Multi-module wasm HTTP |
--modsecurity-wasm-file / --modsecurity-wasm-source-url | /wasm/modsecurity-… | WAF engine |
--challenge-wasm-file / --challenge-wasm-source-url | /wasm/challenge-… | PoW module |
--ecds-service-host | chart FQDN | DNS name for Envoy clusters |
Migration from EnvoyExtensionPolicy
The old path that created an EnvoyExtensionPolicy with inline Wasm config
is removed.
- Expose ECDS / extension / wasm ports (Helm chart does this).
- Provide a wasm binary (
dataplane.wasmSourceURLor volume). - Configure Envoy Gateway
extensionManager(see above). - Delete leftover
EnvoyExtensionPolicyobjects from older kubeWAF versions. - Re-apply
WAFresources.
E2E
Provider tests live under test/e2e/. See test/e2e/README.md.
make test-e2e-envoy-gateway
make test-e2e-istio
make test-e2e-cilium