For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
OWASP Core Rule Set
Enable the bundled OWASP Core Rule Set (CRS) to protect against a broad range of common web attacks.
About the OWASP Core Rule Set
Web applications are constantly exposed to a broad range of attacks, including SQL injection, cross-site scripting (XSS), remote code execution (RCE), local file inclusion, and more. These attacks are well-understood and cataloged by the OWASP Top Ten. Building and maintaining rules that can detect each attack is a significant effort. Without a managed rule set, teams must write and update individual rules for every known attack vector, and keep up with new threats as they emerge.
The OWASP Core Rule Set (CRS) is an open source set of generic detection rules that protect against a wide range of attacks, including the OWASP Top Ten. Solo Enterprise for kgateway bundles CRS version 4.
CRS rules are assigned a unique ID and are organized into the same four Coraza processing phases that are described in the WAF server architecture section. Each phase targets a different part of the HTTP transaction.
The following table shows which groups of CRS rules run in each phase.
Each CRS rule is assigned a specific ID.
| Phase | Name | When it runs | Example CRS rules |
|---|---|---|---|
| 1 | Request headers | After request headers are received, before the body | 913100 (scanner User-Agent detection), 920430 (HTTP protocol version), 930130 (restricted file paths) |
| 2 | Request body | After the full request body is received | 942xxx (SQL injection), 941xxx (XSS), 932xxx (RCE) |
| 3 | Response headers | After response headers are received from the upstream | 950xxx (data leakage detection) |
| 4 | Response body | After the full response body is received | 951xxx–956xxx (data leakage in response body) |
To use the CRS, add the coreRuleSet.settings field to your WAFPolicy. The following rules apply:
- You must enable the Coraza rule engine by setting
SecRuleEngine OninruleEngineSettings. - At a minimum, you must set the
crs_setup_versiontransaction variable inphase:1by using an ID in the 900xxx range. The CRS rule 901001 checks that this variable is set. If the rule is missing, CRS blocks all requests with a critical error. - You can specify which CRS rules you want to apply in each phase by using the
SecDefaultActiondirective in thecoreRuleSet.settingsfield. - You can use
customDirectivesto exclude or modify individual CRS rules. - You can use CRS rules together with custom rules to layer app-specific rules on top of generic CRS protections.
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 the OWASP Core Rule Set
In this example, you enable CRS Phase 1 (request header) and Phase 2 (request body) rules for the httpbin app. If a request matches any of these rules, the request is denied with a 403 HTTP response. You also use the customDirectives setting to exclude two CRS rules.
Create the
WAFPolicywith your CRS rules.kubectl apply -f- <<EOF apiVersion: waf.solo.io/v1alpha1 kind: WAFPolicy metadata: name: httpbin-waf namespace: httpbin spec: coreRuleSet: settings: inline: | SecDefaultAction "phase:1,log,auditlog,deny,status:403" SecDefaultAction "phase:2,log,auditlog,deny,status:403" SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230" ruleEngineSettings: inline: | SecRuleEngine On customDirectives: - inline: | SecRuleRemoveById 913100 SecRuleRemoveById 920430 EOFSetting Description coreRuleSet.settingsConfigures the OWASP CRS. SecDefaultAction "phase:1,..."andSecDefaultAction "phase:2,..."set the default action for all Phase 1 (request header) and Phase 2 (request body) CRS rules to log and deny with a 403 status.SecAction id:900990is the required CRS initialization action that sets thetx.crs_setup_versionvariable. Without this variable, CRS rule 901001 blocks all requests. For more information, see About the OWASP Core Rule Set.ruleEngineSettingsRequired. Enables the Coraza rule engine with SecRuleEngine On.customDirectivesRemoves two Phase 1 rules that cause false positives in this environment. Rule 913100 (REQUEST-913 Scanner Detection) blocks User-Agents that are associated with security scanners such as sqlmap. Rule 920430 (REQUEST-920 Protocol Enforcement) validates the HTTP protocol version. You remove this rule, because the ExtProc integration in Solo Enterprise for kgateway does not forward the original HTTP protocol version, which can cause all requests to get denied.Create the
EnterpriseKgatewayTrafficPolicyto apply the policy.kubectl apply -f- <<EOF apiVersion: enterprisekgateway.solo.io/v1alpha1 kind: EnterpriseKgatewayTrafficPolicy metadata: name: httpbin-waf namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin entWAF: wafPolicyRef: name: httpbin-waf EOFSend a request to the httpbin app. Verify that you get a 200 response.
curl -i http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.example.com:8080"curl -i localhost:8080/status/200 -H "host: www.example.com"Example output:
HTTP/1.1 200 OK ...Send a request that triggers a Phase 1 CRS rule. The
.htaccesspath matches rule 930130 from the REQUEST-930 Local File Inclusion (LFI) rule group, which blocks requests that attempt to access restricted server configuration files. Verify that the WAF server blocks the request with a 403 response.curl -vik "http://$INGRESS_GW_ADDRESS:8080/.htaccess" \ -H "host: www.example.com:8080"curl -vik "localhost:8080/.htaccess" \ -H "host: www.example.com"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-wafOther configurations
Review other common configurations.
Detection-only mode
Evaluate CRS rules against production traffic without blocking requests by setting the rules engine to DetectionOnly. You also change the default actions to pass so that violations are logged, but traffic is not denied. This setup lets you identify false positives before switching to enforcement.
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: crs-detection-only
namespace: httpbin
spec:
coreRuleSet:
settings:
inline: |
SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"
SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
ruleEngineSettings:
inline: |
SecRuleEngine DetectionOnlyCRS with IP filtering
Layer CRS on top of IP filtering. This way, traffic is inspected for the OWASP Top Ten attacks and allowed through only if sent from specific IP addresses. You define IP filtering rules in the customDirectives.
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: crs-with-ip-allowlist
namespace: httpbin
spec:
coreRuleSet:
settings:
inline: |
SecDefaultAction "phase:1,log,auditlog,deny,status:403"
SecDefaultAction "phase:2,log,auditlog,deny,status:403"
SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
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: |
SecRuleRemoveById 913100
SecRuleRemoveById 920430CRS with custom rules
Add application-specific rules alongside CRS. CRS covers generic attack patterns, but you can add custom directives for business logic that CRS does not address, such as blocking a malicious User-Agent.
apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
name: crs-with-custom
namespace: httpbin
spec:
coreRuleSet:
settings:
inline: |
SecDefaultAction "phase:1,log,auditlog,deny,status:403"
SecDefaultAction "phase:2,log,auditlog,deny,status:403"
SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
ruleEngineSettings:
inline: |
SecRuleEngine On
customDirectives:
- inline: |
SecRule REQUEST_HEADERS:User-Agent "@streq bad-bot" \
"deny,status:403,id:5001,phase:1,msg:'blocked bad-bot User-Agent'"
- inline: |
SecRuleRemoveById 913100
SecRuleRemoveById 920430