diff --git a/docs/.vuepress/public/images/uam_admin_add_rule.png b/docs/.vuepress/public/images/uam_admin_add_rule.png
new file mode 100644
index 00000000..13d4424c
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_add_rule.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_cookie_ttl.png b/docs/.vuepress/public/images/uam_admin_cookie_ttl.png
new file mode 100644
index 00000000..db65c42d
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_cookie_ttl.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_overview.png b/docs/.vuepress/public/images/uam_admin_overview.png
new file mode 100644
index 00000000..ac3c74e5
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_overview.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_path_conditions.png b/docs/.vuepress/public/images/uam_admin_path_conditions.png
new file mode 100644
index 00000000..e2f0daf7
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_path_conditions.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_paths.png b/docs/.vuepress/public/images/uam_admin_paths.png
new file mode 100644
index 00000000..0cc85543
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_paths.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_rule_details.png b/docs/.vuepress/public/images/uam_admin_rule_details.png
new file mode 100644
index 00000000..24b22b77
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_rule_details.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_service_disabled.png b/docs/.vuepress/public/images/uam_admin_service_disabled.png
new file mode 100644
index 00000000..97227d6a
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_service_disabled.png differ
diff --git a/docs/.vuepress/public/images/uam_admin_test_url.png b/docs/.vuepress/public/images/uam_admin_test_url.png
new file mode 100644
index 00000000..f0695193
Binary files /dev/null and b/docs/.vuepress/public/images/uam_admin_test_url.png differ
diff --git a/docs/.vuepress/public/images/uam_user_add_rule.png b/docs/.vuepress/public/images/uam_user_add_rule.png
new file mode 100644
index 00000000..dc213b9a
Binary files /dev/null and b/docs/.vuepress/public/images/uam_user_add_rule.png differ
diff --git a/docs/.vuepress/public/images/uam_user_overview.png b/docs/.vuepress/public/images/uam_user_overview.png
new file mode 100644
index 00000000..347978b0
Binary files /dev/null and b/docs/.vuepress/public/images/uam_user_overview.png differ
diff --git a/docs/dashboard/README.md b/docs/dashboard/README.md
index 9ee52165..b57b45e2 100644
--- a/docs/dashboard/README.md
+++ b/docs/dashboard/README.md
@@ -25,6 +25,8 @@ It allows to access:
* [Firewall](/dashboard/#firewall) – a dashboard of Black List, White List and Gray List, and Blocked Ports with the ability to manage them.
+* [WebShield](/dashboard/#webshield) – manages Under Attack Mode: puts a domain behind a JavaScript splash challenge while it is under attack.
+
* [Malware Scanner](/dashboard/#malware-scanner) – real-time file scanner.
* [Proactive Defense](/dashboard/#proactive-defense) – a unique Imunify360 feature that can prevent malicious activity through PHP scripts
@@ -510,6 +512,156 @@ Exact ports and port-ranges to be allowed can be configured by the following fie
* FIREWALL.UDP_IN_IPv6
* FIREWALL.UDP_OUT_IPv6
+## WebShield
+
+The WebShield tab holds the WebShield protection features that the server administrator configures per domain rather than per IP address. Currently it contains one such feature, Under Attack Mode.
+
+:::warning The tab is shown only where the feature is available
+WebShield inspects requests inside the web server, so the features on this tab need the WebShield module loaded there. On environments where that module is not loaded — LiteSpeed-based cPanel setups, for example — the WebShield tab is not displayed at all, and opening its URL directly redirects back to the dashboard. The same applies to the end user interface.
+
+If the tab is missing on a server where you expect it, check which WebShield features are available:
+
+
+
+```
+imunify360-wsctl filters
+FILTER AVAILABLE DESCRIPTION
+verdict yes IPSET lookup
+uam no Under Attack Mode
+l7prot no L7 rate limiter
+```
+
+
+
+Where `uam` is not available, only the GreyList / Anti-bot Challenge applies. See [WebShield feature availability](/command_line_interface/#webshield-feature-availability) for the full list.
+:::
+
+:::tip Note
+The WebShield tab is available in the cPanel integration only.
+
+On servers whose Imunify360 agent is older than the interface, availability cannot be probed and the tab is displayed anyway. If you see it but `imunify360-wsctl filters` reports `uam no`, rules can still be created but visitors are never challenged — update the agent.
+:::
+
+### Under Attack Mode
+
+**Under Attack Mode (UAM)** puts a domain behind a mandatory JavaScript splash challenge while it is being attacked. Regular browsers solve the challenge transparently and receive a clearance cookie, so they are challenged only once per cookie lifetime; simple bots that cannot run the challenge never reach the site.
+
+Unlike the [GreyList / Anti-bot Challenge](/features/#greylist-and-anti-bot-challenge), which reacts to the reputation of an individual IP address, UAM is configured explicitly and applies to **every** visitor of the domain (and, optionally, only of some URL paths) it covers.
+
+The concept, the enforcement details and the equivalent `imunify360-wsctl uam` commands are described in [Under Attack Mode (UAM)](/features/under_attack_mode/). This section only covers the user interface.
+
+
+
+#### Service settings
+
+Two switches at the top of the tab control the service as a whole:
+
+* **Enable the service** – turns UAM on or off for the whole server. UAM is off by default. Turning it off stops all challenges immediately; the rules are preserved and take effect again when the service is enabled.
+* **Show the service to end users** – lets unprivileged users manage their own rules for their own domains from the [end user interface](/user_interface/#under-attack-mode). Off by default.
+
+While the service is off, the rules table is replaced with a notice:
+
+
+
+The same switches are available from the command line:
+
+
+
+```
+imunify360-wsctl uam settings service enable
+imunify360-wsctl uam settings visibility enable
+```
+
+
+
+#### Testing a URL against the rules
+
+**Test URL against the rules** checks whether a given URL would be challenged, using the same matching as live traffic. It is the quickest way to verify path scoping without generating real requests.
+
+Enter a host with an optional path and query string (the scheme may be omitted) and click **TEST**. When a rule matches, its ID and domain are reported and the matching row is highlighted in the table below; otherwise the verdict is _No rule matches — visitors are not challenged_.
+
+
+
+:::tip Note
+The query string is significant — see [Path scoping](/features/under_attack_mode/#path-scoping).
+:::
+
+#### The rules table
+
+| Column | Description |
+|-|-|
+|ID|The rule identifier assigned by WebShield. It is the ID used by the `imunify360-wsctl uam` commands.|
+|Active|Pauses or resumes the rule without deleting it. Inactive rules are skipped when matching.|
+|Owner|`admin` for rules created by the server administrator, or the user name for a rule created by an end user.|
+|Domain|The domain the rule covers. A wildcard is shown as it was entered, for example `*.example.com`.|
+|Cookie TTL|How long a visitor's clearance cookie stays valid after they solve the challenge, before they are challenged again.|
+|Hits for|The number of challenges served for the rule within the selected period: Today (default), Last hour, Last 4 hours, Last 24 hours, Last 4 days or Last 7 days. Counts are retained for 7 days.|
+|Label|The optional free-text note stored with the rule.|
+|Actions|Edit and Remove for own rules, View for the rules of end users.|
+
+Rules are matched from top to bottom and the first matching rule is applied, in the same way as iptables/nftables rules.
+
+Use **Filter by** to narrow the list down by Domain or by Owner.
+
+#### Adding a rule
+
+Click **ADD** and fill in the form.
+
+
+
+| Field | Description |
+|-|-|
+|Domain|The domain to put under attack. Pick one of the server's domains from the list, or enter its wildcard — `*.example.com` covers the subdomains only, `.example.com` covers the domain and its subdomains. The domain of an existing rule cannot be changed.|
+|Clearance cookie lifetime|How long a visitor is trusted after solving the challenge. Choose one of the presets, or Custom… and enter a number followed by a unit — `s`, `m`, `h` or `d` — such as `90m` or `6h`, between 10 seconds and 3 days. The default is `1h`.|
+|Label|An optional note, up to 128 characters, shown in the rules table.|
+|Paths|Which URLs of the domain the rule covers — see [Scoping a rule to paths](#scoping-a-rule-to-paths) below.|
+
+
+
+#### Scoping a rule to paths
+
+By default a rule covers the whole domain. Choose one of the other two modes to challenge only some URLs:
+
+* **Entire domain** – every request to the domain is challenged.
+* **Only the listed paths** – a request is challenged only if it matches one of the paths below.
+* **All paths except the listed ones** – the listed paths are let through, everything else on the domain is challenged.
+
+Add up to 32 paths with **ADD PATH**. Each path has a value and a condition, and the rule matches if **any** of them matches.
+
+
+
+
+
+| Condition | Matches when the request URI |
+|-|-|
+|equals|is exactly the value|
+|starts with|begins with the value|
+|ends with|ends with the value|
+|contains|contains the value anywhere|
+|matches wildcard|matches the value as a wildcard pattern, where `*` stands for any number of characters, including `/`|
+|query contains|has a query string containing the value|
+|query matches regex|has a query string matching the value as a regular expression|
+
+:::warning The query string is part of what is matched
+The path conditions are applied to the whole request URI, query string included. An `equals` path for `/wp-login.php` therefore does not match `/wp-login.php?redirect_to=/wp-admin/`. Use `starts with` when the URL may carry a query string, and check the result with **Test URL against the rules**.
+:::
+
+#### Editing, pausing and removing rules
+
+* **Edit** (the pencil) opens the same form for an existing rule. Everything except the domain can be changed.
+* The **Active** switch pauses a rule and resumes it later, which is preferable to deleting a rule you are going to need again.
+* **Remove** (the bin) deletes the rule after a confirmation.
+
+#### Rules created by end users
+
+When **Show the service to end users** is on, end users can create rules for their own domains, and those rules appear in the administrator's table with the user name in the Owner column.
+
+A rule that belongs to an end user can only be inspected, not changed: its Active switch is disabled and the only action available is **View**, which opens the rule read-only.
+
+
+
+To manage such a rule anyway, use the command line as `root` — see [Managing rules](/features/under_attack_mode/#managing-rules).
+
## Malware Scanner
::: tip Note
@@ -1080,7 +1232,7 @@ Go to _Imunify360 → Settings → General_. Th
* [3-rd Party Integration](/dashboard/#_3-rd-party-integration)
* [Auto White List](/dashboard/#auto-white-list)
* [Incidents Logging](/dashboard/#incidents-logging)
-* [WebShield](/dashboard/#webshield)
+* [WebShield settings](/dashboard/#webshield-settings)
* [Anti-bot protection](/dashboard/#anti-bot-protection)
* [OSSEC](/dashboard/#ossec)
* [PAM](/dashboard/#pam)
@@ -1446,10 +1598,14 @@ Autocleanup configuration allows to keep the Incidents
Click _Save changes_ button on the bottom of the section to save changes.
-#### WebShield
+#### WebShield settings

+:::tip Note
+The options below configure the IP-based side of WebShield — the GreyList and the Anti-bot Challenge. They do not control Under Attack Mode, which has its own switch on the [WebShield](/dashboard/#under-attack-mode) tab and keeps working when _Enable WebShield_ is off.
+:::
+
* _Enable WebShield_. When the option is off, disable WebShield, GreyList, and Anti-bot Challenge. A disabled state is recommended for servers with a small amount of RAM. A disabled option along with enabled "Minimized WAF Ruleset" will switch Imunify360 to the "Low Resource Usage" mode.
* _Detect IPs behind CDN_ feature allows to recognize and block IPs with suspicious activity behind supported CDN providers.
diff --git a/docs/features/under_attack_mode/README.md b/docs/features/under_attack_mode/README.md
index ee431ae6..64c19287 100644
--- a/docs/features/under_attack_mode/README.md
+++ b/docs/features/under_attack_mode/README.md
@@ -9,7 +9,9 @@
- Regular browsers solve the challenge transparently and receive a clearance cookie (its lifetime is configurable per rule); subsequent requests carrying a valid cookie flow through normally.
- Simple bots that cannot run the challenge never reach the application.
-UAM is a **server-wide administrator feature**. It is distinct from the per-IP GreyList / Anti-bot Challenge: UAM decisions are keyed on the request's `(domain, path)` and are configured explicitly by the administrator, rather than being driven by the state of an IP list.
+UAM is distinct from the per-IP GreyList / Anti-bot Challenge: UAM decisions are keyed on the request's `(domain, path)` and are configured explicitly, rather than being driven by the state of an IP list.
+
+The server administrator turns the feature on and manages rules for any domain. If the administrator also allows it, site owners can manage rules for their own domains. Rules are managed either from the control panel — see [WebShield](/dashboard/#under-attack-mode) for the administrator interface and [WebShield](/user_interface/#under-attack-mode) for the end user one — or with the `imunify360-wsctl uam` commands described below.
:::tip When to use it
Turn UAM on for a specific domain when it is the target of an automated flood (for example scripted checkout or login abuse during a sale) and you want to gate **all** visitors of that domain behind a challenge, regardless of their IP reputation.
@@ -28,12 +30,13 @@ cPanel + Apache or Nginx (module loaded) only.
- A domain is placed under attack by creating a **UAM rule**. A rule pairs a `domain` (optionally narrowed to a path-set) with the clearance-cookie lifetime to grant once a visitor solves the challenge.
- When a request matches an **active** rule, WebShield returns the JS splash challenge and does not forward the request to the backend until the visitor passes.
- UAM is **fail-open**: if the feature is disabled, the request has no `Host`, the rule store cannot be read, or no active rule matches, the request simply proceeds through the normal WebShield flow. UAM never blocks a request outright — it only inserts a challenge.
+- Whitelisted IP addresses are **never** challenged: the whitelist is consulted before the application-level checks, so an IP on the [White List](/command_line_interface/#whitelist) reaches the site directly. Whitelist any legitimate automation that cannot solve a JavaScript challenge — search-engine crawlers, monitoring, server-side integrations, or `wp-cron` invoked over HTTP.
- The feature is gated behind a single on/off toggle that is **off by default**, and all UAM state is stored on the server itself.
## Prerequisites
- WebShield version >= 1.45.0 installed and running in an environment where UAM is available (see the note above). The core rule commands and the service toggle have been available since 1.44.2; the per-rule challenge-cookie lifetime and the `uam test` command described below were added in 1.45.0.
-- Root access to the server. UAM is managed with the `imunify360-wsctl uam` command, which must be run as `root`.
+- Root access to the server. The `imunify360-wsctl uam` command described below must be run as `root`. Site owners manage their own rules from the control panel instead.
## Enabling and disabling UAM
@@ -51,13 +54,29 @@ imunify360-wsctl uam settings service disable # turn UAM off
Disabling the feature stops all challenges immediately; your rules are preserved and take effect again when you re-enable it.
+A second, independent switch controls whether site owners may manage rules for their own domains in the control panel. It is off by default, so UAM starts out administrator-only:
+
+
+
+```
+imunify360-wsctl uam settings visibility # show the current {allowed_for_users} state
+imunify360-wsctl uam settings visibility enable # let site owners manage their own rules
+imunify360-wsctl uam settings visibility disable # administrator-only again
+```
+
+
+
+:::tip Note
+`Enable WebShield` in Settings does not control UAM. It turns off the GreyList and the Anti-bot Challenge; UAM keeps its own on/off state.
+:::
+
## Managing rules
A rule is created from a small JSON payload. The fields are:
| Field | Required | Description |
|-|-|-|
-|`domain`|yes|Exact hostname to put under attack (up to 253 characters).|
+|`domain`|yes|Hostname to put under attack (up to 253 characters). A leading wildcard is accepted: `*.example.com` matches the subdomains of `example.com` but not `example.com` itself, while `.example.com` matches both. Hostnames are compared verbatim, so write the domain in lower case.|
|`cookie_ttl`|yes|How long a visitor's clearance cookie stays valid after they solve the challenge, before they are challenged again. A Go-style duration string using the units `s`, `m`, `h` (compound values such as `1h30m` are allowed), between **10 seconds and 3 days**.|
|`label`|no|Free-text note (up to 128 characters).|
|`paths`|no|Path-scoping block (see [Path scoping](#path-scoping) below). Omit it to cover the whole domain.|
@@ -76,25 +95,27 @@ imunify360-wsctl uam add '{"domain":"shop.example.com","cookie_ttl":"30m","paths
-List the current rules (a table by default, `--json` for raw output, `--domain` to filter by exact domain):
+List the current rules (a table by default, `--json` for raw output, `--domain` and `--owner` to filter by exact domain or by rule owner):
```
imunify360-wsctl uam list
imunify360-wsctl uam list --domain shop.example.com --json
+imunify360-wsctl uam list --owner alice
```
-The table shows `ID ACTIVE DOMAIN COOKIE_TTL LABEL`. The `ID` is a positive integer assigned by WebShield when the rule is created; you use it to edit or delete the rule.
+The table shows `ID OWNER ACTIVE DOMAIN COOKIE_TTL LABEL`. The `ID` is a positive integer assigned by WebShield when the rule is created; you use it to edit or delete the rule. `OWNER` is `admin` for rules created by the server administrator, or the user name for a rule a site owner created for their own domain.
```
imunify360-wsctl uam list
-ID ACTIVE DOMAIN COOKIE_TTL LABEL
-7 true shop.example.com 1h Black Friday
+ID OWNER ACTIVE DOMAIN COOKIE_TTL LABEL
+7 admin true shop.example.com 1h Black Friday
+8 alice true blog.example.com 30m Comment spam
```
@@ -129,7 +150,21 @@ By default a rule covers the whole domain. Add a `paths` block to challenge only
- `mode: "include"` — the matchers list the paths that **are** under attack. A request is challenged only if it matches one of them.
- `mode: "exclude"` — the matchers list **exceptions**. A matching request is let through; everything else on the domain is challenged.
-Each matcher is `{"value": "...", "condition": "..."}`, where `condition` is one of `equals`, `prefix`, `suffix`, or `contains`. A rule may hold between 1 and 32 matchers, and a request matches the block if it satisfies **any** of them.
+Each matcher is `{"value": "...", "condition": "..."}`. A rule may hold between 1 and 32 matchers, and a request matches the block if it satisfies **any** of them. The available conditions are:
+
+| Condition | Matches when the request URI |
+|-|-|
+|`equals`|is exactly the value|
+|`prefix`|begins with the value|
+|`suffix`|ends with the value|
+|`contains`|contains the value anywhere|
+|`wildcard`|matches the value as a wildcard pattern, where `*` stands for any number of characters, including `/`|
+|`query_contains`|has a query string containing the value|
+|`query_regex`|has a query string matching the value as a regular expression|
+
+:::warning The query string is part of what is matched
+Matchers are applied to the whole request URI, query string included. An `equals` matcher for `/wp-login.php` therefore does **not** match `/wp-login.php?redirect_to=/wp-admin/` — use `prefix` when the URL may carry a query string. Matching is case-sensitive. Verify the result with [`uam test`](#testing-which-rule-matches-a-url).
+:::
@@ -200,6 +235,7 @@ imunify360-wsctl uam counters # all rules with hits today,
imunify360-wsctl uam counters --since 24h # a rolling window instead of "today"
imunify360-wsctl uam counters shop.example.com # filter by exact domain
imunify360-wsctl uam counters 7 # filter by rule id
+imunify360-wsctl uam counters --owner alice # filter by rule owner
```
diff --git a/docs/user_interface/README.md b/docs/user_interface/README.md
index f5163662..299719bd 100644
--- a/docs/user_interface/README.md
+++ b/docs/user_interface/README.md
@@ -114,3 +114,50 @@ imunify360-agent config update '{"PERMISSIONS": {"user_ignore_list": false}}'
:::
+
+## WebShield
+
+The WebShield tab holds the WebShield protection features you can manage for your own domains. Currently it contains one such feature, Under Attack Mode.
+
+### Under Attack Mode
+
+**Under Attack Mode (UAM)** lets you put your own domains "under attack": while a domain is under attack, every visitor first gets a JavaScript splash page and only reaches the site after their browser solves it. Regular browsers solve it transparently and are then trusted for the lifetime of the clearance cookie; simple bots never get through.
+
+Turn it on for a domain when it is the target of an automated flood — a scripted login or checkout abuse, for example — and turn it off once the flood is over.
+
+
+
+:::tip Note
+This tab appears only if Under Attack Mode is supported by the server, the server administrator has enabled it, and they have allowed end users to manage their own rules. If you do not see the tab, or you see a notice saying that the service is disabled, ask your hosting provider.
+:::
+
+You only see and manage the rules you created, for the domains of your own account. Rules created by the server administrator are not shown here, but they still apply to your domains.
+
+#### Adding a rule
+
+Click **ADD** and fill in the form.
+
+
+
+| Field | Description |
+|-|-|
+|Domain|The domain to put under attack. Pick one of your domains from the list, or enter its wildcard — `*.example.com` covers the subdomains only, `.example.com` covers the domain and its subdomains.|
+|Clearance cookie lifetime|How long a visitor is trusted after solving the splash page, before being asked again. The default is `1h`; a shorter lifetime is stricter, a longer one is more comfortable for real visitors.|
+|Label|An optional note to remind you why the rule exists.|
+|Paths|**Entire domain** challenges every request. **Only the listed paths** challenges just the URLs you list — useful when only the login or the checkout page is being hit. **All paths except the listed ones** challenges everything else, which is a way to keep an API or a webhook endpoint reachable.|
+
+The path conditions and the way they treat query strings are described in [Scoping a rule to paths](/dashboard/#scoping-a-rule-to-paths).
+
+#### Checking and managing rules
+
+* **Test URL against the rules** tells you whether a given URL would be challenged, without waiting for real traffic.
+* Hits for shows how many challenges the rule has served over the selected period, so you can see whether it is doing any work.
+* The **Active** switch pauses a rule and resumes it later — better than deleting a rule you will need again during the next flood.
+* **Edit** changes the cookie lifetime, the label and the paths of an existing rule. The domain cannot be changed; create a new rule instead.
+* **Remove** deletes the rule.
+
+:::warning Legitimate automation needs to be whitelisted
+Anything that cannot run JavaScript is stopped while the rule is active — including your own integrations, monitoring and cron jobs that call the site over HTTP. Ask your hosting provider to whitelist the IP addresses they come from; whitelisted IP addresses are not challenged.
+:::
+
+For the technical details of how the challenge is enforced, see [Under Attack Mode (UAM)](/features/under_attack_mode/).