Rules Engine for Firewall
Look up every criterion variable, operator, and behavior a firewall rule can use, and the fields a rule carries in Azion Console and the API.
A rule in Rules Engine for Firewall pairs criteria, which decide whether a request matches, with behaviors, which act on each request that matches. A rule whose criteria do not match applies nothing. To follow a request through the rules of a firewall, refer to How Firewall works.
A firewall rule decides whether a request reaches the application at all, because the firewall takes each request before the application does. A rule that chooses how the application serves an allowed request, such as its connector or cache setting, belongs to Rules Engine for Applications.
Rule fields
Each firewall holds its own list of rules. Azion Console shows them in the firewall’s Rules Engine tab, where Rule opens the Create Rule drawer. The API serves them under /v4/workspace/firewalls/<firewall-id>/request_rules. A rule carries ten fields: five that a request sets, and five that the platform sets and returns.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | integer | Read-only | — | The identifier every later call on the rule uses |
name | string, 1 to 250 characters | Yes | — | The rule name. The Console renders it as the Name field and asks for a unique, descriptive name |
description | string, up to 1,000 characters | No | "" | A comment on the rule, shown in the Description column of the rule list. Past 1,000 characters, the Console shows Description should not exceed 1000 characters |
active | boolean | No | true | Whether the rule is active. The Console renders it as the Active switch in the Status section, and the rule list shows Active or Inactive |
criteria | array of 1 to 5 blocks, each an array of 1 to 10 criteria | Yes | — | The conditions a request must meet. Each value is listed in Criteria |
behaviors | array of behavior objects, at least 1 | Yes | — | What the rule does to a request that matches. Each value is listed in Behaviors |
order | integer | Read-only | Assigned at create | The rule’s position in the firewall’s list of rules |
last_editor | string | Read-only | — | The account that last changed the rule, shown in the Last Editor column |
last_modified | date-time | Read-only | — | When the rule last changed, shown in the Last Modified column |
created_at | date-time | Read-only | — | When the rule was created |
The platform assigns order in creation order: the first rule on a firewall carries 0, and the eleventh carries 10. The Console rule list lets you reorder rules.
Criteria
Criteria decide which requests a rule acts on. Each criterion names a variable, a comparison operator, and, for most operators, an argument to compare the variable with. The Create Rule drawer opens with one criterion already set to Request Uri and starts with, and ${request_uri} starts_with / matches every request the workload receives.
The table lists the variables in Console order. Nine of them require Web Application Firewall (WAF) enabled on the firewall, Network requires Network Shield, and the other five need no Product.
| Variable | API variable | Requires | What it holds | Example |
|---|---|---|---|---|
| Header Accept | ${header_accept} | WAF | The media types the client accepts in the response | application/json |
| Header Accept Encoding | ${header_accept_encoding} | WAF | The content encodings, usually compression algorithms, the client accepts in the response | gzip |
| Header Accept Language | ${header_accept_language} | WAF | The language the client expects | en-US |
| Header Cookie | ${header_cookie} | WAF | The cookies the client sends with the request | session_id=abc123 |
| Header Origin | ${header_origin} | WAF | The origin of a cross-site request or a preflight request: a URI naming the server, with no path | https://example.com |
| Header Referer | ${header_referer} | WAF | The address of the document, or of the element in it, from which the request URI was obtained | https://example.com/landing-page |
| Header User Agent | ${header_user_agent} | WAF | The string that identifies the client application, operating system, vendor, or version | Mozilla/5.0 |
| Host | ${host} | None | In order of precedence: the hostname in the request line, the value of the Host header, or the name of the server that serves the request | api.example.com |
| Network | ${network} | Network Shield | The client IP address, compared with the IP addresses and CIDR ranges, ASNs, or countries of a Network List | 2 |
| Request Args | ${request_args} | WAF | The arguments in the query string of the request | page=1 |
| Request Method | ${request_method} | WAF | The HTTP method of the request | POST |
| Request Uri | ${request_uri} | None | The URI of the request | /api/v1/ |
| Scheme | ${scheme} | None | The scheme of the request, HTTP or HTTPS | https |
| Ssl Verification Status | ${ssl_verification_status} | None | The result of the client certificate validation | CERTIFICATE_VERIFICATION_ERROR |
| Client Certificate Validation | ${client_certificate_validation} | None | The server process that authenticates the client’s digital certificate | true |
When the firewall lacks the Product a variable requires, the Console still lists the variable but does not let you select it. The label then carries a suffix, such as Header Accept - required WAF or Network - required Network Shield. The API refuses a rule that uses ${network} on a firewall without Network Shield, with 400 and code 25047.
Network compares the client IP address with a Network List. In the Console, the argument becomes the Select a Network list, which shows every Network List in the account and closes with Create Network List. In the API, the argument is the list ID sent as a JSON integer. For example, 2 is the Tor exit node list Azion provides, and any rule can reference it. The same ID sent as a string is refused with 400 and code 25042.
Once a rule on the firewall uses ${network}, Network Shield cannot be turned off on that firewall. The API answers 400 with code 24005 and names the rules that hold it.
Request Args holds nothing when the request has no query string, and an empty variable does not match. For example, a rule on ${request_args} matches .* skips every POST that carries its payload in the body and none in the query string.
Ssl Verification Status takes one of three values, which the Console offers in the Select an SSL Status list:
| Value | API argument | Meaning |
|---|---|---|
| Success | SUCCESS | The client certificate passed validation |
| Certificate Verification Error | CERTIFICATE_VERIFICATION_ERROR | The client certificate was not valid |
| Missing Client Certificate | MISSING_CLIENT_CERTIFICATE | The request carried no client certificate |
Operators
A criterion compares its variable with its argument through an operator. The Console labels each operator in lowercase, and the API sends the value in the second column as operator.
| Operator | API value | The criterion matches when | Argument |
|---|---|---|---|
| is equal | is_equal | The value equals the argument, compared character by character | String |
| is not equal | is_not_equal | The value is not exactly the argument | String |
| starts with | starts_with | The value starts with the argument | String |
| does not start with | does_not_start_with | The value does not start with the argument | String |
| matches | matches | The value matches the regular expression in the argument | Regular expression |
| does not match | does_not_match | The value does not match the regular expression in the argument | Regular expression |
| exists | exists | The variable has a value. Request Args exists when the query string carries an argument | None |
| does not exist | does_not_exist | The variable has no value. Request Args does not exist when the query string carries no argument | None |
The Console hides the argument field for exists and does not exist, and replaces it with a list for Network and Ssl Verification Status.
Network is the exception to the table. Its matches and does not match carry the API values is_in_list and is_not_in_list, and both compare the client IP address with the Network List the argument names. The API refuses matches on ${network} with 400 and code 25039.
Each variable offers only some of the operators:
| Variable | Operators |
|---|---|
| Header Accept, Header Accept Encoding, Header Accept Language, Header Cookie, Header Origin, Header Referer, Header User Agent | matches, does not match |
| Host | is equal, is not equal, matches, does not match |
| Network | matches (is_in_list), does not match (is_not_in_list) |
| Request Args | is equal, is not equal, matches, does not match, exists, does not exist |
| Request Method | is equal, is not equal |
| Request Uri | is equal, is not equal, starts with, does not start with, matches, does not match |
| Scheme, Ssl Verification Status, Client Certificate Validation | is equal, is not equal |
Conditionals
Criteria combine inside blocks, and a rule holds from 1 to 5 blocks of 1 to 10 criteria each. Inside a block, the first criterion carries the conditional if, and each later criterion carries and or or. Blocks are joined by And, which the Console prints between them, so a request matches the rule only when it matches every block.
In the Console, And and Or add a criterion to a block, and Add Criteria adds a block. The Console disables And and Or at 10 criteria in a block, and Add Criteria at 5 blocks. The dividers between criteria show the conditional as If, And, or Or.
In the API, criteria is a list of blocks, and each block is a list of criterion objects. The rule in this example holds one block of two criteria. It denies a request only when the client IP address is in the Network List and the URI starts with /ip-deny:
The <network-list-id> placeholder stands for an integer with no quotes around it.
Behaviors
Behaviors are what a rule does to a request that matches its criteria. The Console labels the first behavior row Then and each later row And, and Add Behavior adds a row. In the API, each behavior is an object with a type, and the behaviors that take settings carry them in attributes.
The Requires column names the Product the firewall must have enabled for the Console to offer the behavior. When it is off, the Console shows Set WAF - required WAF or Run Function - required Functions and does not let you select it. None of the six behaviors requires Network Shield.
| Behavior | API type | Requires | Attributes | What the client receives |
|---|---|---|---|---|
| Deny (403 Forbidden) | deny | None | None | 403, with Azion’s default error page titled Forbidden |
| Drop (Close Without Response) | drop | None | None | No response: no status line and no body |
| Set Rate Limit | set_rate_limit | None | type, limit_by, average_rate_limit, maximum_burst_size | 429 for a request the limit does not admit, with the default error page titled Too Many Requests |
| Set WAF | set_waf | WAF | waf_id, mode | 400 for a request the rule set blocks in blocking mode, with the default error page titled Bad Request |
| Run Function | run_function | Functions | value | Whatever the code of the function instance does with the request |
| Set Custom Response | set_custom_response | None | status_code, content_type, content_body | The status code, Content-Type, and body the attributes set |
The Console also limits how behaviors combine. Only Run Function and Set WAF can be followed by another behavior, and each appears at most once in a rule. Choosing Deny (403 Forbidden), Drop (Close Without Response), Set Rate Limit, or Set Custom Response removes every behavior row after it.
Deny (403 Forbidden)
Deny (403 Forbidden) refuses the request with 403 and takes no attributes. The body is Azion’s default error page titled Forbidden, about 10.6 KB of HTML, which shows the client IP address and the request ID. No response header names the firewall or the rule. In the API, the behavior is { "type": "deny" }.
Drop (Close Without Response)
Drop (Close Without Response) closes the request without answering it and takes no attributes. The client receives no status line and no body, immediately. A script that reads the status code sees 000, and a browser shows a connection error. In the API, the behavior is { "type": "drop" }.
Over HTTP/2, curl reports the reset stream and exits with code 92:
Over HTTP/1.1 and plain HTTP, curl exits with code 52:
Set Rate Limit
Set Rate Limit caps the rate of the requests the rule matches. A request the rate and the burst do not admit receives 429, with the default error page titled Too Many Requests. No response carries a rate-limit header, such as retry-after or an x-ratelimit- header.
| Attribute | Console field | Values | Description |
|---|---|---|---|
type | Rate Limit Type | second (Req/s) or minute (Req/min) | The period the rate counts over. Defaults to second |
limit_by | Limit By | client_ip (Client IP address) or global (Global) | Whether the rate counts per client IP address or across all requests. Required. The Console preselects Client IP address |
average_rate_limit | Average Rate Limit | Integer, at least 1 | The requests allowed per second or per minute, as type sets. Required |
maximum_burst_size | Maximum Burst Size | Integer, at least 1 | The extra requests tolerated in a short peak. Applies to second only: the Console hides the field for Req/min and does not send it |
A rate limit of 10 requests per second per client IP address, with a burst of 10:
For how the rate and the burst admit requests, refer to How Firewall works.
Set WAF
Set WAF applies a WAF rule set to the requests the rule matches, and requires WAF enabled on the firewall. A rule set runs only when a rule carries this behavior, and a rule carries at most one. Rule sets live in Azion Console under Edge Libraries > WAF Rules.
| Attribute | Console control | Values | Description |
|---|---|---|---|
waf_id | Select a WAF | Integer | The ID of the rule set to apply. Required |
mode | Select a WAF mode | logging (Logging) or blocking (Blocking) | Whether WAF refuses the requests the rule set flags. Required |
In blocking mode, a request the rule set blocks receives 400 with the default error page titled Bad Request, and no response header names WAF. In logging mode, WAF does not refuse the request. The API refuses a set_waf behavior with no mode, and any mode other than logging or blocking, including learning.
A behavior that applies a rule set in blocking mode:
Run Function
Run Function runs a function instance of the firewall on the requests the rule matches, and requires Functions enabled on the firewall. A firewall created through the API, the CLI, or the Console create page has Functions on, and the Console create drawer starts it off.
The attribute value holds the ID of the function instance, not of the function. Instances live in the firewall’s Functions Instances tab. In the Console, Select a Function lists the active instances of this firewall, and Create Function Instance creates one from that list. A rule carries at most one Run Function behavior. To create an instance, refer to Instantiate a function on a firewall.
A behavior that runs one function instance:
For the code a firewall function runs, refer to Functions for Firewall.
Set Custom Response
Set Custom Response answers the request with the status code, content type, and body it sets, and requires no Product. The three settings go in attributes, as with every behavior that takes settings.
| Attribute | Console field | Values | Description |
|---|---|---|---|
status_code | Status code | Integer, 200 to 499 | The status code of the response. Required |
content_type | Content Type | String, up to 255 characters | The value of the Content-Type header. Defaults to an empty string |
content_body | Content Body | String, up to 500 characters | The body of the response. Defaults to an empty string |
API
Every rule operation is authenticated and sits under https://api.azion.com/v4/workspace/firewalls/<firewall-id>/request_rules. A request carries a personal token in the Authorization header under the Token scheme, and a request with a body also carries Content-Type: application/json.
| Operation | Method and path |
|---|---|
| Create a rule | POST /request_rules |
| List the rules of a firewall | GET /request_rules |
| Retrieve a rule | GET /request_rules/{request_rule_id} |
| Update part of a rule | PATCH /request_rules/{request_rule_id} |
| Delete a rule | DELETE /request_rules/{request_rule_id} |
A create and a delete answer 202, and a read answers 200.
This call creates a rule that denies every request whose URI starts with /deny-test:
The response carries 202 and a state of pending. The response excerpt keeps the fields the platform added:
The platform adds description, an empty string when the create sends none, and order, the rule’s position on the firewall. The id in data is the handle every later call on the rule uses.
CLI
Azion CLI manages rules under the firewall-rule noun. azion create firewall-rule takes only --firewall-id and --file, so the criteria and behaviors of a rule live in the same JSON body the API takes.
| Command | What it does |
|---|---|
azion create firewall-rule | Creates a rule from a JSON file, with --firewall-id and --file |
azion list firewall-rule | Lists the rules of a firewall, with --firewall-id. --details adds the LAST EDITOR and LAST MODIFIED columns |
azion describe firewall-rule | Returns one rule, with --firewall-id and --rule-id. --format json prints the full record |
This file describes a rule that denies requests from the countries in a Network List on /cli-rule:
Save it as rule.json, replace <network-list-id> with the list ID as an integer, and create the rule:
Errors
A rejected rule returns an errors array. Each entry carries a code, a title, a detail, the status, and a source pointer naming the field, and some entries add a meta object:
| Code | Title | Status | What causes it | What to do |
|---|---|---|---|---|
10039 | Invalid Choice | 400 | A value outside an enum: a variable such as $(network), a spelling the v4 API specification lists, a conditional other than if, and, or or, or a mode such as learning. The detail reads "<value>" is not a valid choice. | Send a listed value. The source pointer names the field, such as /data/criteria/0/0/conditional |
10059 | Required Field | 400 | A required field is absent, such as a set_waf behavior with no mode, at /data/behaviors/0/attributes/mode | Add the field the source pointer names |
24005 | Cannot Disable Firewall Network Protection Module | 400 | Turning Network Shield off on a firewall whose rules use ${network} | Change or delete the rules the detail lists, then turn Network Shield off |
25030 | Entity Not Found | 400 | A ${network} argument naming a Network List that does not exist. The detail reads Network List '<network-list-id>' not found. | Send the ID of an existing list |
25031 | Entity Not Active | 400 | A ${network} argument naming an inactive Network List. The detail reads Network List '<network-list-id>' is not active. | Activate the list, then create the rule |
25036 | Invalid Informed WAF | 400 | A waf_id that names no rule set. The detail reads The informed WAF (<waf-id>) is not valid., and meta.waf_id repeats the value | Send the ID of an existing rule set |
25039 | Invalid Operator | 400 | ${network} with the operator matches. The detail reads The operator 'matches' is not valid for the '${network}' variable. | Send is_in_list or is_not_in_list |
25042 | Invalid Operator Argument Type | 400 | A ${network} argument sent as a string, such as "57102", the type the v4 API specification gives it. The detail reads The argument type '<network-list-id>' is not valid for the operator 'is_in_list'. | Send the Network List ID as a JSON integer |
25047 | Missing Required Modules | 400 | A rule using ${network} on a firewall without Network Shield | Enable Network Shield on the firewall, modules.network_protection.enabled set to true, then create the rule |
Two CLI rejections carry no code. A rule whose run_function behavior names a function instance that does not exist fails with Error: failed to create the Firewall Rule: ["Function Instance '99999999' not found."], with the ID the file sent. A behavior written with name instead of type fails with Error: Failed to decode the given 'json' file. Verify if the file format is JSON or fix its content according to the JSON format specification at https://www.json.org/json-en.html, although the file is valid JSON: the key the platform reads is type.