IBM Cloud Docs
Working with WAF custom rules

Working with WAF custom rules

WAF custom rules offer power and flexibility by targeting HTTP traffic and applying custom criteria to block, challenge, log, or allow certain requests.

You can create many types of WAF custom rules. However, the number of active rules on your site is limited by your customer plan. For more information on entitlements, see Comparing CIS plans.

The number of active rules per plan is fixed. Currently, you can't purchase more active rules.

Before getting started, it's a good idea to review Using fields, functions, and expressions.

You can create, update, and delete a custom rule by using the UI, CLI, API, or Terraform.

Creating a custom rule in the console

Follow these steps to create a custom rule in the console:

WAF custom rules are configured by using the existing Firewall rules page. Any legacy firewall rules that were previously created on your domain are automatically converted into WAF custom rules.

  1. In the CIS console, navigate to the Security.

  2. Select the Custom rules tab and then click Create.

  3. Enter an optional description.

  4. Optionally, input a priority, if necessary. A priority of zero is a null priority and is evaluated last.

  5. Use the UI builder in the Incoming requests section to add a condition. To build an expression with multiple conditions, click either:

    • And - to evaluate conditions that use and logic
    • Or - to evaluate conditions or groups of previously and'ed conditions that use or logic

    You can see that as you build a condition, the Expression Preview shows the expression in plain text.

    In the Expression Preview, you can click to edit your expression manually instead of using the Visual Expression Builder, or switch between the two. However, depending on the complexity of a manually constructed expression, the Visual Expression Builder might be unable to render it.

  6. Pick an action from the Response list menu.

  7. To save your rule, choose the most appropriate option by clicking either:

    • Save as draft to save your rule, but keep it disabled.
    • Save and deploy to save your rule and activate it.

Updating a custom rule in the console

Follow these steps to update an existing custom rule in the console:

  1. In the CIS console, navigate to the Security.
  2. Select the Custom rules tab.
  3. In the Custom rules table, locate the rule that you want to modify, then click the Actions menu on the right of the row.
  4. Select Edit.
  5. Make your changes to the rule.
  6. To save your rule, choose the most appropriate option by clicking either:
    • Save as draft to save your rule, but keep it disabled.
    • Save and deploy to save your rule and activate it.

To pause or activate any rule in the list of existing rules, click the Enabled toggle.

Deleting a custom rule in the console

Follow these steps to delete an existing custom rule in the console:

  1. In the CIS console, navigate to the Security.
  2. In the Custom rules table, locate the rule to delete and click the Actions menu on the right of the row.
  3. Select Delete from the action menu.
  4. Click Delete WAF custom rule to delete the rule.

Creating a custom rule from the CLI

To create a custom rule from the CLI, follow these steps:

  1. Set up your CLI environment.

  2. Log in to your account with the CLI. After you enter the password, the system prompts for the account and region that you want to use:

    ibmcloud login --sso
    
  3. Run the following command to create a custom rule:

    ibmcloud cis custom-waf rule-create DNS_DOMAIN_ID --match EXPRESSION --action ACTION [--description DESCRIPTION] [--enabled true|false] [-i, --instance INSTANCE] [--output FORMAT]
    

    You can also create a custom rule by providing a JSON file or a JSON string directly:

    ibmcloud cis custom-waf rule-create DNS_DOMAIN_ID (--json @JSON_FILE | JSON_STRING) [-i, --instance INSTANCE] [--output FORMAT]
    

Command options

DNS_DOMAIN_ID

The ID of the DNS domain.

--match

Specifies the conditions that must be matched for the rule to run. For match value, see Using fields, functions, and expressions.

--action

The rule action to perform. Valid values: "block", "challenge", "js_challenge", "managed_challenge", "log", "skip". For "block" and "skip" actions, use JSON file or JSON string instead.

--enabled

Indicates if the rule is active. The default is "false".

--description

A brief description of the rule.

--json

The JSON file or JSON string that is used to describe a custom rule.

  • The required fields in JSON data are expression and action.

    • expression : Specifies the conditions that must be matched for the rule to run.
    • action : The rule action to perform. Valid values are block, challenge, js_challenge, managed_challenge, log, and skip.
  • The optional fields are description, enabled, logging, and action_parameters.

    • action_parameters : The rule action parameters.
    • ruleset : Skip all remaining rules or one or more WAF-managed rulesets. Valid value is current.
    • phases : Skips WAF components for matching requests. Valid values are http_ratelimit, http_request_firewall_managed, and http_request_sbfm.
    • products : Skips specific security products for matching requests. Valid values are waf, rateLimit, securityLevel, hot, bic, uaBlock, and zoneLockdown.
    • response : Define a custom response for block action.
      • status_code : Choose an HTTP status code for the response in the range 400-499.
      • content_type : The content type of a custom response. Valid response types are :text/html, text/plain, application/json, text/xml.
      • content : The response body.
    • description : Briefly describes the rule.
    • enabled : Indicates whether the rule is active or not. When this field is disabled, matched requests don't appear in firewall requests.
    • logging : Log requests matching the skip rule. This field is only available for 'skip' action.

Sample JSON data:


         {
           "description": "test-custom-rule",
           "expression": "(http.cookie contains \"test\")",
           "action": "skip",
           "logging": {
                   "enabled": true
               },
           "action_parameters": {
             "ruleset": "current",
               "phases": [
                       "http_ratelimit",
                       "http_request_firewall_managed",
                       "http_request_sbfm"
                   ],
                   "products": [
                       "waf",
                       "rateLimit",
                       "securityLevel",
                       "hot",
                       "bic",
                       "uaBlock",
                       "zoneLockdown"
                   ]
           },
           "enabled": true
         }
-i, --instance
Instance name or ID. If not set, the context instance that is specified by ibmcloud cis instance-set INSTANCE is used.
--output
Specify output format, only JSON is supported.

Updating a custom rule from the CLI

Run the following command to update a custom rule in the CLI:

ibmcloud cis custom-waf rule-update DNS_DOMAIN_ID [--match EXPRESSION] [--action ACTION] [--description DESCRIPTION] [--enabled true|false] [-i, --instance INSTANCE] [--output FORMAT]

You can also update a custom rule by providing a JSON file or a JSON string directly:

ibmcloud cis custom-waf rule-update DNS_DOMAIN_ID (--json @JSON_FILE | JSON_STRING) [-i, --instance INSTANCE] [--output FORMAT]

Command options

DNS_DOMAIN_ID

The ID of the DNS domain.

RULE_ID

The ID of the rule.

--match

Specifies the conditions that must be matched for the rule to run. For match value, see Using fields, functions, and expressions.

--action

The rule action to perform. Valid values: "block", "challenge", "js_challenge", "managed_challenge", "log", "skip".For "block" and "skip" actions, use JSON file or JSON string instead.

--enabled

Indicates if the rule is active. The default value is "false".

--description

A brief description of the rule.

--json

The JSON file or JSON string that is used to describe a custom rule.

  • The required fields in JSON data are expression and action.

    • expression : Specifies the conditions that must be matched for the rule to run.
    • action : The rule action to perform. Valid values are block, challenge, js_challenge, managed_challenge, log, and skip.
  • The optional fields are description, enabled, logging, and action_parameters.

    • action_parameters : The rule action parameters.
      • ruleset : Skip all remaining rules or one or more WAF-managed rulesets. Valid value is current
      • phases : Skips WAF components for matching requests. Valid values are http_ratelimit, http_request_firewall_managed, and http_request_sbfm.
      • products : Skips specific security products for matching requests. Valid values are waf, rateLimit, securityLevel, hot, bic, uaBlock, and zoneLockdown.
      • response : Define a custom response for 'block' action.
        • status_code : Choose an HTTP status code for the response in the range 400-499.
        • content_type : The content type of a custom response. Valid response types are text/html, text/plain, application/json, and text/xml.
        • content : The response body.
    • description : Briefly describes the rule.
    • enabled : Indicates whether the rule is active or not. When this field is disabled, matched requests don't appear in firewall requests.
    • logging : Log requests matching the skip rule. This field is only available for 'skip' action.

Sample JSON data:

         {
           "description": "test-custom-rule",
           "expression": "(http.cookie contains \"test\")",
           "action": "block",
           "action_parameters": {
             "response": {
             "status_code": 429,
             "content_type": "text/xml",
             "content": "reject"
             }
           },
           "enabled": true
         }
-i, --instance
Instance name or ID. If not set, the context instance that is specified by ibmcloud cis instance-set INSTANCE is used.
--output
Specify output format, only JSON is supported.

Deleting a custom rule from the CLI

Run the following command to delete a custom rule in the CLI:

ibmcloud cis custom-waf rule-delete DNS_DOMAIN_ID RULE_ID [-f, --force] [-i, --instance INSTANCE] [--output FORMAT]

Command options

DNS_DOMAIN_ID
The ID of the DNS domain.
RULE_ID
The ID of the custom rule.
-i, --instance
Instance name or ID. If not set, the context instance that is specified by ibmcloud cis instance-set INSTANCE is used.
-f, --force
Attempt to delete a custom rule without prompting for confirmation.
--output
Specify output format, only JSON is supported.

Command examples

  • To create a custom rule:

    ibmcloud cis custom-waf rule-create 601b728b86e630c744c81740f72570c3 --action challenge --description "rule 1" --enabled true --match "(http.host eq \"www.example.com\")"

  • To update a custom rule:

    ibmcloud cis custom-waf rule-update 601b728b86e630c744c81740f72570c3 4d37cb6f87654e96a18bc531628a4d27 --enabled true

  • To delete a custom rule:

    ibmcloud cis custom-waf rule-delete 601b728b86e630c744c81740f72570c3 4d37cb6f87654e96a18bc531628a4d27

Getting the custom rule entry point for the API

All custom rule API operations require a RULESET_ID of the entry point ruleset for the custom rules phase.

Follow these steps to get the custom rule entry point ruleset:

  1. Set up your API environment with the correct variables.

  2. Store the following values in variables to be used in the API command:

    CRN: The full URL-encoded Cloud Resource Name (CRN) of the service instance.

    ZONE_ID: The domain ID.

  3. When all variables are initiated, get the entry point ruleset:

    curl -X GET "https://api.cis.cloud.ibm.com/v1/$CRN/zones/$ZONE_ID/rulesets/phases/http_request_firewall_custom/entrypoint" \
    --header "X-Auth-User-Token: Bearer <API_TOKEN>" \
    --header "Content-Type: application/json"
    

    The ruleset ID is in the response of the successful request. If the preceding call returns a 404 Not Found response, use the following API to create the entry point ruleset for the custom rule phase:

    curl -x POST https://api.cis.cloud.ibm.com/v1/$CRN/zones/$ZONE_ID/rulesets \
    --header "X-Auth-User-Token: Bearer <API_TOKEN>" \
    --header "Content-Type: application/json" \
    --data '{
       "name": "Zone-level phase entry point",
       "kind": "zone",
       "description": "Custom rule entry point ruleset.",
       "phase": "http_request_firewall_custom"
    }'
    

Creating a custom rule with the API

Follow these steps to create a custom rule with the API:

  1. Set up your API environment with the correct variables.

  2. Store the following values in variables to be used in the API command:

    CRN: The full URL-encoded Cloud Resource Name (CRN) of the service instance.

    ZONE_ID: The domain ID.

    RULESET_ID: The ID of the custom rule entrypoint ruleset.

  3. When all variables are initiated, create the custom rule:

    curl -X POST "https://api.cis.cloud.ibm.com/v1/$CRN/zones/$ZONE_ID/rulesets/$RULESET_ID/rules" \
    --header "X-Auth-User-Token: Bearer <API_TOKEN>" \
    --header "Content-Type: application/json" \
    --data '{
      "description": "My custom rule with plain text response",
      "expression": "(ip.src.country eq \"GB\" or ip.src.country eq \"FR\") and cf.waf.score lt 20",
      "action": "block",
      "action_parameters": {
        "response": {
          "status_code": 403,
          "content": "Your request was blocked.",
          "content_type": "text/plain"
        }
      }
      }'
    

Updating a custom rule with the API

Follow these steps to update an existing custom rule with the API:

  1. Set up your API environment with the correct variables.

  2. Store the following values in variables to be used in the API command:

    CRN: The full URL-encoded Cloud Resource Name (CRN) of the service instance.

    ZONE_ID: The domain ID.

    RULESET_ID: The ID of the custom rule entrypoint ruleset.

    RULE_ID: The ID of the custom rule to be modified.

  3. When all variables are initiated, update the custom rule:

    curl -X PATCH "https://api.cis.cloud.ibm.com/v1/$CRN/zones/$ZONE_ID/rulesets/$RULESET_ID/rules/$RULE_ID" \
    --header "X-Auth-User-Token: Bearer <API_TOKEN>" \
    --header "Content-Type: application/json" \
    --data '{
       "enabled": false,
       "description": "block GB and FR or based on IP Reputation (temporarily disabled)"
    }'
    

Deleting a custom rule with the API

Follow these steps to delete an existing custom rule with the API:

  1. Set up your API environment with the correct variables.

  2. Store the following values in variables to be used in the API command:

    CRN: The full URL-encoded Cloud Resource Name (CRN) of the service instance.

    ZONE_ID: The domain ID.

    RULESET_ID: The ID of the custom rule entrypoint ruleset.

    RULE_ID: The ID of the custom rule to be modified.

  3. When all variables are initiated, delete the custom rule:

    curl -X DELETE "https://api.cis.cloud.ibm.com/v1/$CRN/zones/$ZONE_ID/rulesets/$RULESET_ID/rules/$RULE_ID" \
    --header "X-Auth-User-Token: Bearer <API_TOKEN>" \
    --header "Content-Type: application/json"
    

Creating a custom rule with Terraform

To create a custom rule, you must create an entry point first, then create the custom rule. To do so, follow these steps:

  1. To create an entry point ruleset, run the following command:

    resource "ibm_cis_ruleset_entrypoint_version" "test" {
        cis_id    = "<cis-id>"
        domain_id = "<domain-id>"
        phase = "http_request_firewall_custom"
        rulesets {
          description = "Entry point ruleset for custom ruleset"
        }
        lifecycle {
          ignore_changes = [
            rulesets
          ]
      }
    }
    
  2. To create a custom rule, run the following command:

       resource ibm_cis_ruleset_rule "config" {
          cis_id    = ibm_cis.instance.id
          domain_id = data.ibm_cis_domain.cis_domain.domain_id
          ruleset_id = resource.ibm_cis_ruleset_entrypoint_version.rulesets[0].ruleset_id
          rule {
            action =  "block"
            description = "var.description"
            expression = "true"
            enabled = "false"
            action_parameters {
              response {
                status_code = var.status_code
                content =  var.content
                content_type = "text/plain"
              }
            }
            position {
              index = var.index
              after = <id of any existing rule>
              before = <id of any existing rule>
            }
          }
        }
    

The following example shows how to create an entry point and WAF custom rule:

resource ibm_cis_ruleset_entrypoint_version test {
cis_id    = ibm_cis.instance.id
domain_id = data.ibm_cis_domain.cis_domain.domain_id
phase = "http_request_firewall_custom"
rulesets {
   description = "Entrypoint ruleset for custom ruleset"
   }
   lifecycle {
   ignore_changes = [
      rulesets
   ]
  }
}
data ibm_cis_ruleset_entrypoint_versions custom_rule_ep {
  cis_id    = ibm_cis.instance.id
  domain_id = data.ibm_cis_domain.cis_domain.domain_id
  phase = "http_request_firewall_custom"
  depends_on = [
  ibm_cis_ruleset_entrypoint_version.test
  ]
}

resource ibm_cis_ruleset_rule "custom_rule1" {
    cis_id    = ibm_cis.instance.id
    domain_id = data.ibm_cis_domain.cis_domain.domain_id
    ruleset_id = data.ibm_cis_ruleset_entrypoint_versions.custom_rule_ep.rulesets[0].ruleset_id
      rule {
        action =  "skip"
        description = "adding custom rule via terraform"
        expression = "true"
        enabled = "true"
        action_parameters{
          ruleset = "current"
          phases = ["http_ratelimit", "http_request_sbfm","http_request_firewall_managed"]
          products = ["zoneLockdown","uaBlock", "bic", "hot", "securityLevel", "rateLimit","waf"]
        }
      }
}

resource ibm_cis_ruleset_rule "custom_rule2" {
    cis_id    = ibm_cis.instance.id
    domain_id = data.ibm_cis_domain.cis_domain.domain_id
    ruleset_id = data.ibm_cis_ruleset_entrypoint_versions.custom_rule_ep.rulesets[0].ruleset_id
      rule {
        action =  "block"
        description = "adding custom rule via terraform2"
        expression = "(http.request.uri.path eq \"/path1\")"
        enabled = "false"
      }
}

For more information, see ibm_cis_ruleset_rule in the Terraform registry.

You can update a custom rule with Terraform by modifying the preceding example and running the terraform apply command. To delete the rule, simply remove the configuration and run terraform apply.