WAF

Filter, monitor, and block potentially harmful HTTP traffic with a Web Application Firewall (WAF) policy.

About web application firewalls

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.

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.

You have several options for using ModSecurity to write WAF policies:

For more information, see the Gloo Gateway API docs.

Before you begin

This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started, and that your Kubernetes context is set to the cluster you store your Gloo config in (typically the management cluster). If you have different names, make sure to update the sample configuration files in this guide.

Follow the getting started instructions to:

  1. Set up Gloo Gateway in a single cluster.
  2. Deploy sample apps.
  3. Configure an HTTPS listener on your gateway and set up basic routing for the sample apps.

Configure WAF policies

You can apply a WAF policy at the route level. For more information, see Applying policies.

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:
  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 previously applied. If omitted, 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.

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.

  1. Enable source IP preservation in the Istio 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.
  2. 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'"
    

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.

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

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.

The following ModSecurity directives that configure the audit engine are ignored, even if set:

Although the way that audit logging works is different, you still can use the features that ModSecurity audit logging provides:

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:
  • NEVER: Never generate audit logs, which is the default behavior if you omit audit logging from your WAF policy configuration.
  • RELEVANT: Log transactions that have a warning, an error, or a status code that is considered to be relevant as determined by the SecAuditLogRelevantStatus directive.
  • ALWAYS: Always generate an audit log for each transaction, which is set in this example.
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:
  • DYNAMIC_METADATA: Process the audit log “eagerly,” or for every ModSecurity transaction. Dynamic processing can increase CPU, because the Envoy gateway still runs the computation even if the result has no access log message.
  • FILTER_STATE: Process the audit log “lazily,” or only if the Envoy gateway access log detects an audit log message after processing the ModSecurity transaction. Filter state processing uses less CPU because not as many messages are logged. However, this method can use more memory, because the ModSecurity transaction stays in memory longer.
Note that Data Loss Prevention (DLP) is not supported with filter state access logging.

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.

  1. Download the example WAF policy for the ratings route that you reviewed in the previous section.

  2. Apply the example WAF policy.

    
    kubectl apply --context ${MGMT_CONTEXT} -f waf-policy_bookinfo_basic-auth.yml
    
    
    kubectl apply --context ${REMOTE_CONTEXT1} -f waf-policy_bookinfo_basic-auth.yml
    

  3. Include the user-agent: test1 header in a request to the ratings app.

    curl -vik -H "User-Agent: test1" --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1
    

    Example output: Notice that your request is now denied with a 403 error and the custom-intervention-message.

    HTTP/2 403
    ...
    custom-intervention-message* Closing connection 0
    
  4. Optional: Clean up the resources that you created.

    kubectl -n bookinfo delete wafpolicy basic-auth