This is the "latest" release of Envoy Gateway, which contains the most recent commits from the main branch.
This release might not be stable.
Please refer to the /docs documentation for the most current information.
Dynamic Modules
4 minute read
This task provides instructions for configuring Dynamic Modules.
Dynamic Modules are a critical extension mechanism that allows functionality to be loaded directly into the Envoy proxy process at runtime, typically as shared object files (.so). This approach enables customized filtering and request processing without requiring a full recompile of the core Envoy binary, streamlining deployments and upgrades.
Envoy Gateway is able to load dynamic modules from the local filesystem using EnvoyExtensionPolicy. This example demonstrates it’s working by loading the Coraza Web Application Firewall (WAF) using Built On Envoy development toolkit. The module’s full documentation can be found on the Coraza WAF extension page.
Prerequisites
Follow the steps below to install Envoy Gateway and the example manifest. Before proceeding, you should be able to query the example backend using HTTP.
Expand for instructions
Install the Gateway API CRDs and Envoy Gateway using Helm:
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespaceInstall the GatewayClass, Gateway, HTTPRoute and example app:
kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n defaultVerify Connectivity:
Get the External IP of the Gateway:
export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}')Curl the example app through Envoy proxy:
curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/getThe above command should succeed with status code 200.
Get the name of the Envoy service created the by the example Gateway:
export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')Get the deployment of the Envoy service created the by the example Gateway:
export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')Port forward to the Envoy service:
kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 &Curl the example app through Envoy proxy:
curl --verbose --header "Host: www.example.com" http://localhost:8888/getThe above command should succeed with status code 200.
Coraza WAF Extension
Installation
Add the dynamic module to the Envoy proxy container’s filesystem and configure the DynamicModules spec to load it into Envoy.
cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: my-proxy
spec:
provider:
type: Kubernetes
kubernetes:
envoyDeployment:
pod:
volumes:
- name: dynamic-modules
image:
reference: ghcr.io/tetratelabs/built-on-envoy/composer:0.6.0-dev
pullPolicy: IfNotPresent
container:
env:
- name: GODEBUG
value: "cgocheck=0"
volumeMounts:
- name: dynamic-modules
mountPath: /etc/envoy/dynamic-modules
readOnly: true
dynamicModules:
- name: composer
source:
type: Local
local:
path: /etc/envoy/dynamic-modules/libcomposer.so
doNotClose: true
loadGlobally: false
EOF
Save and apply the following resource to your cluster:
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: my-proxy
spec:
provider:
type: Kubernetes
kubernetes:
envoyDeployment:
pod:
volumes:
- name: dynamic-modules
image:
reference: ghcr.io/tetratelabs/built-on-envoy/composer:0.6.0-dev
pullPolicy: IfNotPresent
container:
env:
- name: GODEBUG
value: "cgocheck=0"
volumeMounts:
- name: dynamic-modules
mountPath: /etc/envoy/dynamic-modules
readOnly: true
dynamicModules:
- name: composer
source:
type: Local
local:
path: /etc/envoy/dynamic-modules/libcomposer.so
doNotClose: true
loadGlobally: false
Note: verify version compatibility of the extension because of dynamic module forward compatibility.
Image Volumes are relatively new and only supported from Kubernetes 1.35 onwards.
Alternative ways of loading the dynamic module are:
- Building a custom docker image
- Copying from
InitContainerto a shared volume
Verify the EnvoyProxy status:
kubectl get envoyproxy/my-proxy -o yaml
Attach to Gateway via a GatewayClass with spec.parametersRef
kubectl patch gatewayclass eg --type=merge -p '{
"spec": {
"parametersRef": {
"group": "gateway.envoyproxy.io",
"kind": "EnvoyProxy",
"name": "my-proxy",
"namespace": "envoy-gateway-system"
}
}
}'
The entire configuration can also be specified directly on the Gateway instead by using spec.envoyProxy.
Configuration
Create a new EnvoyExtensionPolicy resource to configure the dynamic module for an entire Gateway or per HTTPRoute.
This EnvoyExtensionPolicy targets the Gateway “eg” created with the quickstart. It loads the Coraza WAF extension with its configuration.
cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: waf-extension
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: eg
dynamicModule:
- name: composer
filterName: coraza-waf
config:
directives:
- Include @coraza.conf
- SecRuleEngine On
- SecResponseBodyAccess Off
- Include @crs-setup.conf
- Include @owasp_crs/*.conf
EOF
Save and apply the following resource to your cluster:
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: waf-extension
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway # or e.g. HTTPRoute
name: eg
dynamicModule:
- name: composer
filterName: coraza-waf
config:
directives:
- Include @coraza.conf
- SecRuleEngine On
- SecResponseBodyAccess Off
- Include @crs-setup.conf
- Include @owasp_crs/*.conf
Verify the Envoy Extension Policy configuration:
kubectl get envoyextensionpolicy/waf-extension -o yaml
Testing
Ensure the GATEWAY_HOST environment variable from the Quickstart is set. If not, follow the
Quickstart instructions to set the variable.
echo $GATEWAY_HOST
Send a normal request to the backend service:
curl -v -H "Host: www.example.com" "http://${GATEWAY_HOST}/"
You should get a 200 OK response from the backend.
Now send a request with a SQL injection payload to trigger the WAF:
curl -v -H "Host: www.example.com" "http://${GATEWAY_HOST}/?id=1'+OR+'1'%3D'1"
The Coraza WAF should block the request and return a 403 Forbidden response:
> GET /?id=1'+OR+'1'='1 HTTP/1.1
> Host: www.example.com
[...]
< HTTP/1.1 403 Forbidden
< date: Sat, 03 May 2026 12:00:00 GMT
< content-length: 0
<
Clean-Up
Follow the steps from the Quickstart to uninstall Envoy Gateway and the example manifest.
Delete the EnvoyProxy and EnvoyExtensionPolicy:
kubectl delete envoyproxy/my-proxy
kubectl delete envoyextensionpolicy/waf-extension
Next Steps
Checkout the Developer Guide to get involved in the project.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.