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.

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 delegates RouteA1 to RouteTableB and RouteA2 to RouteTableC.
    • The default WAF policy applies to both RouteA1 and RouteA2, 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 from RouteA1 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 of RouteTableC.

Figure: Multiple WAF policies
Figure: Multiple WAF policies
Figure: Multiple WAF policies
Figure: Multiple WAF policies

Before you begin

  1. Set up Gloo Mesh Gateway in a single cluster.
  2. Install Bookinfo and other sample apps.
  3. Configure an HTTP listener on your gateway and set up basic routing for the sample apps.
  4. Get the external address of your ingress gateway. The steps vary depending on the type of load balancer that backs the ingress gateway.

      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-20 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.

SettingDescription
applyToRoutesUse 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.
customInterventionMessageReturn a custom message when the policy causes the gateway to intervene in the request. This example returns the message custom-intervention-message.
customRuleSetsCreate 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.
- ruleStrYou 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.
2The number of rules in the inline rule set, such as 2 in this example.
SecRuleEngine OnThis 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: trueInclude 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.

  1. 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.
  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'"
        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.

SettingDescription
customRuleSetsCreate 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.
- ruleStrYou 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.
2The number of rules in the inline rule set, such as 2 in this example.
SecRuleEngine OnThis 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.

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.

SettingDescription
SecAuditLogFormat JSONSet audit log format to JSON. To use regular string format, omit this directive.
auditLoggingUse this audit logging stanza to tell the WAF filter how to log transactions.
action: ALWAYSDescribe 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_STATEDetermine 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.

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. 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
      
  2. 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
        
  3. Include the user-agent: test1 header in a request to the ratings app.

    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. 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