For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
IP-based filtering
Block or allow traffic based on the client IP address or CIDR range.
About IP-based filtering
Not all traffic to your apps is legitimate. Malicious actors, botnets, and malicious IP address ranges can target your services with brute-force attacks, credential stuffing, or reconnaissance. Without IP-level filtering at the gateway proxy, these requests reach your app and can consume its resources.
Solo Enterprise for kgateway lets you block or allow traffic based on the client IP address by using the Coraza REMOTE_ADDR variable in a WAFPolicy. IP filtering rules run in the earliest phase (phase:1), so blocked requests are rejected before the request body is buffered or forwarded to the upstream.
In production, requests typically pass through one or more proxies, such as cloud load balancers, CDNs, or reverse proxies before they reach the gateway. Each hop appends an IP address to the X-Forwarded-For header. By default, the gateway proxy uses the socket-level peer IP address of the client to determine the source IP address. IP addresses that are sent in the X-Forwarded-For request header are ignored by default.
You can change the default behavior and instead configure the gateway proxy to derive the REMOTE_ADDR from the X-Forwarded-For header instead. To do that, you create a ListenerPolicy with the following settings:
useRemoteAddress: true(default setting): Append the socket-level peer IP to theX-Forwarded-Forheader.xffNumTrustedHops: In cases where the request passes through multiple trusted proxy hops, each hop appends an IP address to theX-Forwarded-Forheader. ThexffNumTrustedHopssetting defines the number of addresses that Envoy ignores from the right to find the true client IP. For example,xffNumTrustedHops: 1causes Envoy to ignore the rightmost entry and use the entry immediately to its left asREMOTE_ADDR.
The following table shows how the gateway proxy determines the value of REMOTE_ADDR:
| ListenerPolicy field | REMOTE_ADDR value |
|---|---|
default.httpSettings.useRemoteAddress: true (default) | Use the immediate downstream socket peer IP address. The X-Forwarded-For header is ignored. |
default.httpSettings.useRemoteAddress: true, xffNumTrustedHops: N | Use the Nth address from the right of the X-Forwarded-For header. |
Note that you can use IP filtering rules alongside CRS and custom rules in the same WAFPolicy to create a multi-layer defense-in-depth system.
Before you begin
Follow the Get started guide to install Solo Enterprise for kgateway.
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n kgateway-system 8080:8080
- Enable the WAF server for your GatewayClass.
Configure IP-based filtering
Use Coraza’s REMOTE_ADDR variable to block specific client IP addresses or CIDR ranges. For testing purposes, this guide configures a ListenerPolicy so that the client IP is derived from the X-Forwarded-For request header instead of the socket-level peer IP.
Create a
ListenerPolicythat configures Envoy to use theX-Forwarded-Forheader to determine the client IP.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: ListenerPolicy metadata: name: xff-trust namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http default: httpSettings: useRemoteAddress: true xffNumTrustedHops: 1 EOFSetting Description default.httpSettings.useRemoteAddress: trueEnvoy appends the socket-level peer IP to the X-Forwarded-Forheader and uses the downstream address for connection metadata. Combined withxffNumTrustedHops: 1, Envoy selects the address one hop back from the right in theX-Forwarded-Forheader as the trusted client IP, which is what the WAF server receives asREMOTE_ADDR.default.httpSettings.xffNumTrustedHops: 1Each proxy hop in the request chain appends an IP address to the X-Forwarded-Forrequest header. Because of that, the header can hold multiple IP addresses in a comma-separated list. The gateway proxy (Envoy) uses this header to determine the number of trusted proxies that the request passed through and the value to use for theREMOTE_ADDRvariable. ThexffNumTrustedHopssetting defines the number of IP addresses that the gateway needs to ignore to find the value for theREMOTE_ADDRvariable, starting from the right. For example, setting this field to1means that the gateway proxy ignores the right-most entry and reads the entry immediately to its left asREMOTE_ADDR. The example in this guide assumes that theX-Forwarded-Forrequest header contains two IP addresses: the IP address of the client that sent the request, such as your local machine, and the gateway proxy. A value of 1 means that the gateway proxy ignores the gateway proxy IP address and instead uses the client IP address for theREMOTE_ADDRvariable.Create a
WAFPolicythat blocks requests from the198.51.100.0/24CIDR range.kubectl apply -f- <<EOF apiVersion: waf.solo.io/v1alpha1 kind: WAFPolicy metadata: name: httpbin-waf namespace: httpbin spec: ruleEngineSettings: inline: | SecRuleEngine On customDirectives: - inline: | SecRule REMOTE_ADDR "@ipMatch 198.51.100.0/24" "id:1001,phase:1,deny,status:403,msg:'blocked IP'" EOFSetting Description @ipMatchCoraza operator that matches IP addresses or CIDR ranges. Accepts a comma-separated list, for example 10.0.0.0/8,172.16.0.0/12.phase:1Evaluates the rule during request headers processing, before the body is buffered. Create an
EnterpriseKgatewayTrafficPolicyto attach theWAFPolicyto the httpbin HTTPRoute.kubectl apply -f- <<EOF apiVersion: enterprisekgateway.solo.io/v1alpha1 kind: EnterpriseKgatewayTrafficPolicy metadata: name: httpbin-waf-ip namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin entWAF: wafPolicyRef: name: httpbin-waf EOFSend a request with an allowed IP address in the
X-Forwarded-For. Verify that you get a 200 response.curl -i http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.example.com:8080" \ -H "X-Forwarded-For: 10.0.0.1"curl -i localhost:8080/status/200 -H "host: www.example.com" \ -H "X-Forwarded-For: 10.0.0.1"Example output:
HTTP/1.1 200 OK ...Send a request with a blocked IP address in the
X-Forwarded-Forrequest header. Verify that the WAF blocks the request with a 403 response.curl -vik http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.example.com:8080" \ -H "X-Forwarded-For: 198.51.100.10"curl -vik localhost:8080/status/200 -H "host: www.example.com" \ -H "X-Forwarded-For: 198.51.100.10"Example output:
HTTP/1.1 403 Forbidden ...
Next
Cleanup
You can optionally remove the resources that you set up as part of this guide.kubectl delete wafpolicy -n httpbin httpbin-waf
kubectl delete enterprisekgatewaytrafficpolicy -n httpbin httpbin-waf-ip
kubectl delete listenerpolicy -n kgateway-system xff-trustOther configurations
Review other common configurations.
IP allowlisting
Instead of blocking malicious IP addresses, you can invert the logic to allow only trusted IP addresses. The !@ipMatch negation denies any request with a source IP address that is not in the allowlist.
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: ip-allowlist
namespace: httpbin
spec:
ruleEngineSettings:
inline: |
SecRuleEngine On
customDirectives:
- inline: |
SecRule REMOTE_ADDR "!@ipMatch 203.0.113.0/24,198.51.100.50" \
"phase:1,deny,status:403,id:1,msg:'IP not in allowlist'"Per-route IP allowlists with sectionName
In multi-team environments, different routes might need independent IP address restrictions. Use the sectionName in the EnterpriseKgatewayTrafficPolicy to add WAF rules to specific HTTPRoute routes. This setup allows you to make updates to your WAF rules without impacting other teams.
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: ip-allowlist-team-a
namespace: httpbin
spec:
ruleEngineSettings:
inline: |
SecRuleEngine On
customDirectives:
- inline: |
SecRule REMOTE_ADDR "!@ipMatch 203.0.113.0/24" \
"phase:1,deny,status:403,id:1,msg:'IP not in team-a allowlist'"
---
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: ip-allowlist-team-b
namespace: httpbin
spec:
ruleEngineSettings:
inline: |
SecRuleEngine On
customDirectives:
- inline: |
SecRule REMOTE_ADDR "!@ipMatch 198.51.100.0/24" \
"phase:1,deny,status:403,id:1,msg:'IP not in team-b allowlist'"Then attach each policy to its route rule:
apiVersion: enterprisekgateway.solo.io/v1alpha1
kind: EnterpriseKgatewayTrafficPolicy
metadata:
name: waf-team-a
namespace: httpbin
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: httpbin-route
sectionName: team-a
entWAF:
wafPolicyRef:
name: ip-allowlist-team-a
---
apiVersion: enterprisekgateway.solo.io/v1alpha1
kind: EnterpriseKgatewayTrafficPolicy
metadata:
name: waf-team-b
namespace: httpbin
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: httpbin-route
sectionName: team-b
entWAF:
wafPolicyRef:
name: ip-allowlist-team-bCombining IP filtering with attack detection
You can layer IP filtering with additional rules in the same WAFPolicy for defense in depth. For example, combine an IP allowlist with SQL injection and XSS detection so that even allowed IPs are still inspected for malicious payloads.
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: ip-allowlist-with-attack-detection
namespace: httpbin
spec:
ruleEngineSettings:
inline: |
SecRuleEngine On
customDirectives:
- inline: |
SecRule REMOTE_ADDR "!@ipMatch 203.0.113.0/24,198.51.100.50" \
"phase:1,deny,status:403,id:1,msg:'IP not in allowlist'"
- inline: |
SecRule QUERY_STRING "@rx (?i:union.*select|select.*from|drop\s+table|insert\s+into)" \
"phase:1,deny,status:403,id:2,msg:'SQL injection detected'"
- inline: |
SecRule QUERY_STRING "@rx (?i:%3Cscript|<script|javascript:|onerror=|onload=)" \
"phase:1,deny,status:403,id:3,msg:'XSS detected'"