Web application firewall (WAF)
Filter and block potentially harmful traffic with a Web Application Firewall (WAF) policy.
If you import or export resources across workspaces, your policies might not apply. For more information, see Import and export policies.
About
WAFs protect your web apps by monitoring, filtering, and blocking potentially harmful HTTP traffic. You write a WAF policy by following a framework and ruleset. Then, you apply the WAF policy to the route for the apps that you want to protect. When Gloo Gateway receives an incoming request for that route (ingress traffic), the WAF intercepts and inspects the network packets and uses the rules that you set in the policy to determine access to the web app. The WAF policy also applies to any outgoing responses (egress traffic) along the route. This setup provides an additional layer of protection between your apps and end users.
ModSecurity ruleset
Gloo supports the popular Web Application Firewall framework and ruleset ModSecurity version 3.0.4. ModSecurity uses a simple rules language to interpret and process incoming HTTP traffic. Because it is open source, ModSecurity is a flexible, cross-platform solution that incorporates transparent security practices to protect apps against a range web attacks.
Gloo applies the OWASP Core Rule Set to your routes by default, but you must supply the settings to use with the coreRuleSetSettingsString
field. To disable the OWASP Core Rule Set, setdisableCoreRuleSet
to true
in your WAF policy.
You have several options for using ModSecurity to write WAF policies:
- Use publicly available rule sets that provide a generic set of detection rules to protect against the most common security threats. For example, the OWASP Core Rule Set is an open source project that protects apps against a wide range of attacks, including the “OWASP Top Ten.”
- Write your own custom rules by following the ModSecurity rules language. For examples, see Configure WAF policies.
For more information, see the Gloo Mesh Gateway API docs.
Multiple WAF policies
For multiple WAF policies on the same route, Gloo Gateway applies the Core Rule Set settings from each policy. If the policies have conflicting values, Gloo Gateway uses the priority of each policy to determine whether to enable CRS. When the priorities are the same or unset, Gloo Gateway enables CRS.
The following diagram shows an example of multiple WAF policies:
- The Platform team sets up default ingress behavior by creating several Gloo resources.
RouteTableA
provides the main routes for ingress traffic. This route table delegatesRouteA1
toRouteTableB
andRouteA2
toRouteTableC
.- The default WAF policy applies to both
RouteA1
andRouteA2
, and sets up default ModSecurity and Iress rules.
- Team B applies their own WAF policy to all the routes in
RouteTableB
. With priority settings, Team B makes sure that all ingress traffic fromRouteA1
that is forwarded to their routes applies their own team-specific rules and overrides. - Team C does not have their own WAF policy. As such, the default WAF policy for
RouteA2
applies to ingress traffic that is served by the backing services ofRouteTableC
.
Before you begin
This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started. If you have different names, make sure to update the sample configuration files in this guide.
- Set up Gloo Mesh Gateway in a single cluster.
- Install Bookinfo and other sample apps.
- Configure an HTTP listener on your gateway and set up basic routing for the sample apps.
Get the external address of your ingress gateway. The steps vary depending on the type of load balancer that backs the ingress gateway.
```shell export INGRESS_GW_ADDRESS=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS ```
Note: Depending on your environment, you might see
<pending>
instead of an external IP address. For example, if you are testing locally in kind or minikube, or if you have insufficient permissions in your cloud platform, you can instead port-forward the service port of the ingress gateway:kubectl -n gloo-mesh-gateways port-forward deploy/istio-ingressgateway-1-22 8081
Configure WAF policies
You can apply a WAF policy at the route level. For more information, see Applying policies.
OWASP Core Rule Set
By default, Gloo Gateway applies the OWASP Core Rule Set to your routes. You can configure the settings with the coreRuleSetSettingsString
field. In the following example, the settings are configured to block all connections except http2
.
apiVersion: security.policy.gloo.solo.io/v2
kind: WAFPolicy
metadata:
name: basic-auth
namespace: bookinfo
spec:
applyToRoutes:
- route:
labels:
route: ratings
config:
coreRuleSetSettingsString: |
# default rules section
SecRuleEngine On
SecRequestBodyAccess On
# CRS section
# Will block by default
SecDefaultAction "phase:1,log,auditlog,deny,status:403"
SecDefaultAction "phase:2,log,auditlog,deny,status:403"
# only allow http2 connections
SecAction \
"id:900230,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:'tx.allowed_http_versions=HTTP/2 HTTP/2.0'"
SecAction \
"id:900990,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_setup_version=310"
Header example
The following YAML configuration is for a basic WAF policy that denies requests if the User-Agent
header is set to the value scammer
. You can use this example to learn the basic parts of a WAF policy, as well as get ideas for how you might allow or deny traffic based on request headers.
apiVersion: security.policy.gloo.solo.io/v2
kind: WAFPolicy
metadata:
annotations:
cluster.solo.io/cluster: ""
name: basic-auth
namespace: bookinfo
spec:
applyToRoutes:
- route:
labels:
route: ratings
config:
customInterventionMessage: custom-intervention-message
customRuleSets:
- ruleStr: |2
SecRuleEngine On
SecRule REQUEST_HEADERS:User-Agent "test([1-5])$" "deny,status:403,id:1,phase:1,msg:'blocked scammer'"
disableCoreRuleSet: true
priority: 0
Review the following table to understand this configuration. For more information, see the API docs.
Setting | Description |
---|---|
applyToRoutes | Use labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations , the policy applies to all routes in the workspace. |
customInterventionMessage | Return a custom message when the policy causes the gateway to intervene in the request. This example returns the message custom-intervention-message . |
customRuleSets | Create a set of rules for the WAF policy to apply, based on the ModSecurity rules language. You can have multiple rule sets in a policy. Rule sets are applied in the order that they are configured, with later rule sets overwriting earlier ones in the case of a conflict. This example has only one rule set. |
- ruleStr | You can write the custom rule set inline to the policy (ruleStr ), refer to .conf files of rule sets (files ), or refer to a directory of .conf files (directory ). This example writes the rule set inline. |
2 | The number of rules in the inline rule set, such as 2 in this example. |
SecRuleEngine On | This rule enables the rules engine (On ), which is Off by default. To run the rules engine to detect and log requests, but not to intervene such as by denying the request, set to DetectionOnly . |
SecRule REQUEST_HEADERS:User-Agent "test" "deny,status:403,id:107,phase:1,msg:'blocked scammer'" | This rule inspects the request header "user-agent" . If the value of that header equals "test" , then the gateway denies the request and returns a 403 status. The WAF logs the rule message blocked scammer , which you might use as input in other custom rules. |
disableCoreRuleSet: true | Include this setting to disable the OWASP core rule set from also being applied to requests along this route, such as for testing purposes or if you customize the core rule set yourself. |
IP allowlist example
You might want to allow access to a specific IP address or subnet range, such as an access control list (ACL) for internal services or to enforce a network boundary between different environments like staging and production. You can use a WAF filter with custom ModSecurity rules to set up an allowlist, such as with the following example to allow access from a local workstation.
- Enable source IP preservation in the ingress gateway that your Gloo virtual gateway selects. For more information, see the Istio docs. If applicable, make sure that the load balancer in front of your cluster environment permits source IP preservation. In this example, the source IP address of your local workstation must be preserved when the traffic from your workstation is forwarded by the load balancer to the gateway. This way, the gateway can see the original client IP as the remote address, and use this address in the WAF rules to allow the IP address.
- Create a custom rule set to allow the IP address of your local workstation in the
config
section of your WAF policy.apiVersion: security.policy.gloo.solo.io/v2 kind: WAFPolicy metadata: name: basic-auth namespace: bookinfo spec: applyToRoutes: - route: labels: route: ratings config: customRuleSets: - ruleStr: |2 SecRuleEngine On SecRule REMOTE_ADDR "!@ipMatch 173.175.0.0/16" "phase:1,deny,status:403,id:1,msg:'block ip'" disableCoreRuleSet: true
Review the following table to understand this configuration. For details on the other settings, see the Header example
tab or the API docs.
Setting | Description |
---|---|
customRuleSets | Create a set of rules for the WAF policy to apply, based on the ModSecurity rules language. You can have multiple rule sets in a policy. Rule sets are applied in the order that they are configured, with later rule sets overwriting earlier ones in the case of a conflict. This example has only one rule set. |
- ruleStr | You can write the custom rule set inline to the policy (ruleStr ), refer to .conf files of rule sets (files ), or refer to a directory of .conf files (directory ). This example writes the rule set inline. |
2 | The number of rules in the inline rule set, such as 2 in this example. |
SecRuleEngine On | This rule enables the rules engine (On ), which is Off by default. To run the rules engine to detect and log requests, but not to intervene such as by denying the request, set to DetectionOnly . |
SecRule REMOTE_ADDR "!@ipMatch 173.175.0.0/16" "phase:1,deny,status:403,id:1,msg:'block ip'" | This rule inspects the remote address of the request. If the IP address is not within the 173.175.0.0/16 network range, then the gateway denies the request and returns a 403 status. The WAF logs the rule message block ip , which you might use as input in other custom rules. |
Performance for gRPC services
You can improve the performance of certain services by not sending the body of requests to the ModSecurity WAF ruleset. For example, calls from gRPC services can timeout if the gRPC call is a stream, because the WAF filter requires an end stream to be specified to finish evaluating intervention. To avoid this timeout, add the requestHeadersOnly: true
and responseHeadersOnly: true
entries to the WAF config, such as in the following example.
apiVersion: security.policy.gloo.solo.io/v2
kind: WAFPolicy
metadata:
name: basic-auth
namespace: bookinfo
spec:
applyToRoutes:
- route:
labels:
route: ratings
config:
requestHeadersOnly: true
responseHeadersOnly: true
Audit logging
Gloo audit logging for WAF works differently than native ModSecurity, because Gloo is optimized for performance in Envoy and Kubernetes cloud environments. Instead of using the ModSecurity audit engine, Gloo exposes the audit logs as part of the Envoy gateway’s access logging.
Note that Data Loss Prevention (DLP) is not supported with filter state access logging.
The following ModSecurity directives that configure the audit engine are ignored, even if set:
SecAuditEngine
. Instead, use theaction
property of the audit logging configuration to choose when to log.SecAuditLog
SecAuditLogStorageDir
SecAuditLogType
Although the way that audit logging works is different, you still can use the features that ModSecurity audit logging provides:
SecAuditLogFormat
SecAuditLogParts
SecAuditLogRelevantStatus
noauditlog
, when the action isRELEVANT_ONLY
Review the following configuration file and table description to understand audit logging-specific settings. For details on the other settings, see the Header example
tab.
apiVersion: security.policy.gloo.solo.io/v2
kind: WAFPolicy
metadata:
name: basic-auth
namespace: bookinfo
spec:
applyToRoutes:
- route:
labels:
route: ratings
config:
customInterventionMessage: custom-intervention-message
customRuleSets:
- ruleStr: |2
SecRuleEngine On
SecAuditLogFormat JSON
SecRule REQUEST_HEADERS:User-Agent "test" "deny,status:403,id:107,phase:1,msg:'blocked scammer'"
disableCoreRuleSet: true
auditLogging:
action: ALWAYS
location: FILTER_STATE
Review the following table to understand this configuration. For more information, see the API docs.
Setting | Description |
---|---|
SecAuditLogFormat JSON | Set audit log format to JSON. To use regular string format, omit this directive. |
auditLogging | Use this audit logging stanza to tell the WAF filter how to log transactions. |
action: ALWAYS | Describe the action to take. Possible values are:
|
location: FILTER_STATE | Determine how to expose audit logs in the access logs. You can expose audit logs via access logs in two ways, depending on your performance requirements:
|
Verify WAF policies
The verification steps in this topic are specific to the header example in Configure WAF policies. Note that the steps for verifying other WAF policy examples might vary.
Apply the header example WAF policy that you reviewed in the previous section.
kubectl apply -f- <<EOF apiVersion: security.policy.gloo.solo.io/v2 kind: WAFPolicy metadata: annotations: cluster.solo.io/cluster: "" name: basic-auth namespace: bookinfo spec: applyToRoutes: - route: labels: route: ratings config: customInterventionMessage: custom-intervention-message customRuleSets: - ruleStr: |2 SecRuleEngine On SecRule REQUEST_HEADERS:User-Agent "test([1-5])$" "deny,status:403,id:1,phase:1,msg:'blocked scammer'" disableCoreRuleSet: true priority: 0 EOF
Check the status of the WAF policy.
kubectl -n bookinfo get wafpolicies basic-auth -o jsonpath --template '{.status}' | jq
Example of successful output:
"state": "ACCEPTED"
Example of a warning: You might get a warning if your resource is misconfigured or conflicts with another policy. For more troubleshooting information, try launching the UI and checking the resource in the Resources > Solo page.
"global": { "message": "Resource failed to apply in 2 namespaces; Resource applied with warnings in 1 namespace", "state": "WARNING" }
Example of error metrics: You can also check or set alerts for translation errors related to WAF policies. or more information, see Translation alerts.
curl -s gloo-mesh-mgmt-server:9091/metrics | grep WAF | grep translation_error translation_error{cluster="cluster1",gvk="security.policy.gloo.solo.io/v2/WAFPolicy",namespace="istio-gateway-ns",workspace="anything"} 1
Include the
user-agent: test1
header in a request to theratings
app.Example output: Notice that your request is now denied with a
403
error and thecustom-intervention-message
.HTTP/2 403 ... custom-intervention-message* Closing connection 0
Audit logging only: If you used an audit logging configuration, enable access logging for WAF in the gateway as in the following examples. Then you can repeat the previous command and check the access logs for the transaction message, such as
blocked scammer
from the example configuration.
Cleanup
You can optionally remove the resources that you set up as part of this guide.
kubectl -n bookinfo delete wafpolicy basic-auth