Перейти к содержанию

Настройка страницы блокировки и кода ошибки (NGINX)

Данная инструкция описывает способ настройки страницы и кода ошибки, которые возвращаются клиенту в ответ на запрос, заблокированный по следующим причинам:

Ограничения настройки

Настройка страницы блокировки и кода ошибки поддерживается во всех формах деплоя WAF-ноды на основе NGINX.

Способы настройки

По умолчанию, в ответ на заблокированный запрос возвращаются код ошибки 403 и стандартная страница блокировки NGINX. Вы можете изменить настройки по умолчанию, используя следующие директивы NGINX:

  • wallarm_block_page

  • wallarm_block_page_add_dynamic_path

Директива NGINX wallarm_block_page

В директиве wallarm_block_page можно передать следующие параметры страницы блокировки и кода ошибки:

  • Путь до HTM- или HTML-файла со страницей блокировки. В качестве страницы блокировки вы можете использовать как собственную страницу, так и страницу, которую предоставляет Вебмониторэкс.

  • Текст сообщения, который необходимо вернуть в ответе на заблокированный запрос.

  • URL, на который необходимо перенаправить клиента.

  • respose_code: код ответа на заблокированный запрос.

  • type: тип заблокированного запроса, в ответе на который необходимо вернуть заданную конфигурацию. Параметр может принимать одно или несколько значений (через запятую) из списка:

    • attack (по умолчанию): для запросов с признаками атак, которые были заблокированы WAF-нодой в режиме блокировки или мягкой блокировки (при условии, что источник запроса добавлен в серый список).
    • acl_ip: для запросов с IP-адресов, которые добавлены в черный список как одиночные адреса или в составе подсети.
    • acl_source: для запросов с IP-адресов, которые зарегистрированы в стране, регионе или дата-центре из черного списка.

Перечисленные параметры можно задать в директиве wallarm_block_page следующими способами:

  • Путь до HTM- или HTML-файла со страницей блокировки, код ошибки (опционально) и тип заблокированного запроса (опционально)

    wallarm_block_page &/<PATH_TO_FILE/HTML_HTM_FILE_NAME> response_code=<CUSTOM_CODE> type=<BLOCKED_REQUEST_TYPE>;
    

    Вы можете использовать переменные NGINX на странице блокировки. Для этого вставьте в код страницы имя переменной в {}, начиная с символа $. Например, ${remote_addr} отобразит на странице блокировки IP‑адрес, с которого отправлен запрос.

    Вебмониторэкс предоставляет пример страницы блокировки. Чтобы ее использовать, необходимо скопировать пример и задать в директиве путь к копии.

    Важная информация для пользователей Debian и CentOS

    Если вы используете NGINX версии ниже 1.11, установленный из репозиториев CentOS/Debian, для корректного отображения страницы блокировки необходимо удалить из кода страницы переменную request_id:

    UUID ${request_id}
    

    Удаление переменной требуется при использовании как собственного шаблона динамической страницы, так и wallarm_blocked.html.

    Пример конфигурации →

  • URL для перенаправления клиента и тип заблокированного запроса (опционально)

    wallarm_block_page /<REDIRECT_URL> type=<BLOCKED_REQUEST_TYPE>;
    

    Пример конфигурации →

  • Именованный location NGINX и тип заблокированного запроса (опционально)

    wallarm_block_page @<NAMED_LOCATION> type=<BLOCKED_REQUEST_TYPE>;
    

    Пример конфигурации →

  • Переменная, в которой будет передан путь до HTM- или HTML-файла со страницей блокировки, код ошибки (опционально) и тип заблокированного запроса (опционально)

    wallarm_block_page &<VARIABLE_NAME> response_code=<CUSTOM_CODE> type=<BLOCKED_REQUEST_TYPE>;
    

    Инициализация страницы блокировки с переменными NGINX

    Если вы задаете страницу блокировки через переменную и внутри страницы блокировки также используются переменные NGINX, необходимо инициализировать страницу блокировки с переменными с помощью директивы wallarm_block_page_add_dynamic_path.

    Пример конфигурации →

Директива wallarm_block_page может быть передана в блоках http, server, location конфигурационного файла NGINX.

Директива NGINX wallarm_block_page_add_dynamic_path

Директива wallarm_block_page_add_dynamic_path используется для инициализации страницы блокировки, если внутри страницы используются переменные NGINX и путь до страницы блокировки также задан с помощью переменной. В остальных случаях директива не используется.

Директива wallarm_block_page_add_dynamic_path может быть передана только в блоке http конфигурационного файла NGINX.

Кастомизация примера страницы блокировки

Вебмониторэкс предоставляет пример страницы блокировки. Пример доступен по пути /usr/share/nginx/html/wallarm_blocked.html и выглядит следующим образом:

Пример страницы блокировки Вебмониторэкс

Вы можете скопировать пример и кастомизировать его следующим образом::

  • Добавить логотип вашей компании – по умолчанию логотипа на странице нет.

  • Преобразовать фразу contact us в ссылку на электронную почту вашей службы поддержки – по умолчанию данная фраза является обычным текстом.

  • Изменить любые представленные элементы HTML или добавить собственные.

Варианты создания собственной страницы

Вместо модификации примера страницы блокировки Вебмониторэкс, вы можете создать собственную страницу с нуля.

Порядок действий

Если вы будете изменять саму страницу-пример, внесенные изменения могут быть потеряны при обновлении компонентов Вебмониторэкс. Поэтому рекомендуется скопировать страницу-пример, дать копии новое имя и кастомизировать ее. Необходимые для кастомизации шаги для разных вариантов установки описаны в разделах ниже.

Страница-пример для копирования

Вы можете скопировать страницу-пример /usr/share/nginx/html/wallarm_blocked.html из исходных файлов ноды или из блока ниже:

Показать код страницы-примера
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>You are blocked</title>
    <link href="https://fonts.googleapis.com/css?family=Poppins:700|Roboto|Roboto+Mono&display=swap" rel="stylesheet">
    <style>
        html {
            font-family: 'Roboto', sans-serif;
        }

        body {
            margin: 0;
            height: 100vh;
        }

        .content {
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            align-items: center;
            min-height: 100%;
        }

        .logo {
            margin-top: 32px;
        }

        .message {
            display: flex;
            margin-bottom: 100px;
        }

        .alert {
            padding-top: 20px;
            width: 246px;
            text-align: center;
        }

        .alert-title {
            font-family: 'Poppins', sans-serif;
            font-weight: bold;
            font-size: 24px;
            line-height: 32px;
        }

        .alert-desc {
            font-size: 14px;
            line-height: 20px;
        }

        .info {
            margin-left: 76px;
            border-left: 1px solid rgba(149, 157, 172, 0.24);
            padding: 20px 0 20px 80px;
            width: 340px;
        }

        .info-title {
            font-weight: bold;
            font-size: 20px;
            line-height: 28px;
        }

        .info-text {
            margin-top: 8px;
            font-size: 14px;
            line-height: 20px;
        }

        .info-divider {
            margin-top: 16px;
        }

        .info-data {
            margin-top: 12px;
            border: 1px solid rgba(149, 157, 172, 0.24);
            border-radius: 4px;
            padding: 9px 12px;
            font-size: 14px;
            line-height: 20px;
            font-family: 'Roboto Mono', monospace;
        }

        .info-copy {
            margin-top: 12px;

            padding: 6px 12px;
            border: none;
            outline: none;
            background: rgba(149, 157, 172, 0.08);
            cursor: pointer;
            transition: 0.24s cubic-bezier(0.24, 0.1, 0.24, 1);
            border-radius: 4px;

            font-size: 14px;
            line-height: 20px;
        }

        .info-copy:hover {
            background-color: rgba(149, 157, 172, 0.24);
        }

        .info-copy:active {
            background-color: rgba(149, 157, 172, 0.08);
        }

        .info-mailto,
        .info-mailto:visited {
            color: #fc7303;
        }
    </style>
    <script>
        // Place your support email here
        const SUPPORT_EMAIL = "";
    </script>
</head>

<body>
    <div class="content">
        <div id="logo" class="logo">
            <!--
                Place you logo here.
                You can use an external image:
                <img src="https://example.com/logo.png" width="160" alt="Company Name" />
                Or put your logo source code (like svg) right here:
                <svg width="160" height="80"> ... </svg>
            -->
        </div>

        <div class="message">
            <div class="alert">
                <svg width="207" height="207" viewBox="0 0 207 207" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path
                        d="M88.7512 33.2924L15.6975 155.25C14.1913 157.858 13.3943 160.816 13.3859 163.828C13.3775 166.84 14.1579 169.801 15.6494 172.418C17.141 175.035 19.2918 177.216 21.8877 178.743C24.4837 180.271 27.4344 181.092 30.4462 181.125H176.554C179.566 181.092 182.516 180.271 185.112 178.743C187.708 177.216 189.859 175.035 191.351 172.418C192.842 169.801 193.623 166.84 193.614 163.828C193.606 160.816 192.809 157.858 191.303 155.25L118.249 33.2924C116.711 30.7576 114.546 28.6618 111.963 27.2074C109.379 25.7529 106.465 24.9888 103.5 24.9888C100.535 24.9888 97.6206 25.7529 95.0372 27.2074C92.4538 28.6618 90.2888 30.7576 88.7512 33.2924V33.2924Z"
                        stroke="#F24444" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" />
                    <path d="M103.5 77.625V120.75" stroke="#F24444" stroke-width="16" stroke-linecap="round"
                        stroke-linejoin="round" />
                    <path d="M103.5 146.625V146.668" stroke="#F24444" stroke-width="16" stroke-linecap="round"
                        stroke-linejoin="round" />
                </svg>
                <div class="alert-title">Malicious activity blocked</div>
                <div class="alert-desc">Your request is blocked since it was identified as a malicious one.</div>
            </div>
            <div class="info">
                <div class="info-title">Why it happened</div>
                <div class="info-text">
                    You might have used symbols similar to a malicious code sequence, or uploaded a specific file.
                </div>

                <div class="info-divider"></div>

                <div class="info-title">What to do</div>
                <div class="info-text">
                    If your request is considered to be legitimate, please <a id="mailto" href="" class="info-mailto">contact us</a> and provide your last action description and the following data:
                </div>

                <div id="data" class="info-data">
                    IP ${remote_addr}<br />
                    Blocked on ${time_iso8601}<br />
                    UUID ${request_id}
                </div>

                <button id="copy-btn" class="info-copy">
                    Copy details
                </button>
            </div>
        </div>
        <div></div>
    </div>
    <script>
        // Warning: ES5 code only

        function writeText(str) {
            const range = document.createRange();

            function listener(e) {
                e.clipboardData.setData('text/plain', str);
                e.preventDefault();
            }

            range.selectNodeContents(document.body);
            document.getSelection().addRange(range);
            document.addEventListener('copy', listener);
            document.execCommand('copy');
            document.removeEventListener('copy', listener);
            document.getSelection().removeAllRanges();
        }

        function copy() {
            const text = document.querySelector('#data').innerText;

            if (navigator.clipboard && navigator.clipboard.writeText) {
                return navigator.clipboard.writeText(text);
            }

            return writeText(text);
        }

        document.querySelector('#copy-btn').addEventListener('click', copy);

        const mailto = document.getElementById('mailto');
        if (SUPPORT_EMAIL) mailto.href = `mailto:${wallarm_dollar}{SUPPORT_EMAIL}`;
        else mailto.replaceWith(mailto.textContent);
    </script>
</body>

Пакеты DEB/RPM

Скопируйте страницу-пример /usr/share/nginx/html/wallarm_blocked.html под новым именем в ту же или любую другую директорию, где NGINX имеет права на чтение.

Docker-контейнер

Для загрузки кастомизированной страницы-примера или собственной страницы в Docker-контейнер с нодой, вы можете использовать опцию Docker bind mount. Технология позволяет скопировать страницу блокировки и конфигурационный файл NGINX с вашей машины в контейнер. Созданные копии связываются ссылками с оригинальными файлами, так что при изменении оригинальных файлов копии в контейнере также изменяются и наоборот.

Для кастомизации страницы блокировки:

  1. До первого запуска контейнера подготовьте кастомизированную страницу wallarm_blocked_renamed.html.

  2. Подготовьте конфигурационный файл NGINX с путем к вашей странице. Смотрите пример конфигурации.

  3. Примонтируйте подготовленную страницу блокировки и конфигурационный файл NGINX в контейнер при его запуске.

  4. Если позднее вам понадобится обновить страницу блокировки в запущенном контейнере, на вашей машине обновите wallarm_blocked_renamed.html, затем перезапустите NGINX в контейнере.

Ingress-контроллер

Для кастомизации страницы блокировки:

  1. Подготовьте кастомизированную страницу wallarm_blocked_renamed.html.

  2. Создайте ConfigMap из файла wallarm_blocked_renamed.html.

  3. Примонтируйте созданный ConfigMap в pod с Ingress‑контроллером Вебмониторэкс. Для этого необходимо изменить объект Deployment Ingress‑контроллера Вебмониторэкс по инструкции.

    Директория для монтирования ConfigMap

    Рекомендуется использовать отдельную директорию для файлов, примонтированных с помощью ConfigMap. При монтировании файлов в существующую директорию, предыдущие файлы в этой директории могут быть удалены.

  4. Настройте pod для использования страницы блокировки путем добавления аннотации к Ingress:

    kubectl annotate ingress <INGRESS_NAME> nginx.ingress.kubernetes.io/wallarm-block-page="<PAGE_ADDRESS>"
    

Стандартные элементы для кастомизации

Чтобы добавить логотип вашей компании, снимите комментарии и задайте путь до логотипа в следующей секции файла wallarm_blocked_renamed.html:

<div class="content">
    <div id="logo" class="logo">
        <!--
            Place you logo here.
            You can use an external image:
            <img src="https://example.com/logo.png" width="160" alt="Company Name" />
            Or put your logo source code (like svg) right here:
            <svg width="160" height="80"> ... </svg>
        -->
    </div>

Чтобы преобразовать фразу contact us в ссылку на электронную почту, задайте в файле wallarm_blocked_renamed.html значение переменной SUPPORT_EMAIL:

<script>
    // Place your support email here
    const SUPPORT_EMAIL = "support@company.com";
</script>

Примеры настройки

Ниже приведены примеры настройки страницы блокировки и кода ошибки через директивы wallarm_block_page и wallarm_block_page_add_dynamic_path.

В каждом примере в явном виде передается параметр type с типом запроса, в ответе на который возвращается заданная конфигурация. Если требуется, вы можете удалить параметр type. Тогда заданная конфигурация вернется только в ответе на запросы с признаками атак, заблокированные в режиме блокировки или мягкой блокировки (при условии, что источник запроса добавлен в серый список).

Путь до HTM- или HTML-файла со страницей блокировки и код ошибки

В примере приведены настройки для возвращения клиенту:

  • Кастомизированной страницы-примера блокировки /usr/share/nginx/html/wallarm_blocked_renamed.html и кода ошибки 445 в ответ на запрос, заблокированный WAF-нодой в режиме блокировки или мягкой блокировки (при условии, что источник запроса добавлен в серый список).

  • Собственной страницы блокировки /usr/share/nginx/html/block.html и кода ошибки 445 в ответ на запрос, отправленный с любого IP-адреса из черного списка.

Конфигурационный файл NGINX

wallarm_block_page &/usr/share/nginx/html/wallarm_blocked_renamed.html response_code=445 type=attack;
wallarm_block_page &/usr/share/nginx/html/block.html response_code=445 type=acl_ip,acl_source;

Аннотация Ingress

Перед добавлением аннотации Ingress, необходимо:

  1. Создать ConfigMap из файлов wallarm_blocked_renamed.html и block.html.

  2. Примонтировать созданный ConfigMap в pod с Ingress‑контроллером Вебмониторэкс. Для этого необходимо изменить объект Deployment Ingress‑контроллера Вебмониторэкс по инструкции.

    Директория для монтирования ConfigMap

    Рекомендуется использовать отдельную директорию для файлов, примонтированных с помощью ConfigMap. При монтировании файлов в существующую директорию, предыдущие файлы в этой директории могут быть удалены.

Аннотация Ingress:

kubectl annotate ingress <INGRESS_NAME> nginx.ingress.kubernetes.io/wallarm-block-page="&/usr/share/nginx/html/wallarm_blocked_renamed.html response_code=445 type=attack;&/usr/share/nginx/html/block.html response_code=445 type=acl_ip,acl_source"

URL для перенаправления клиента

В примере приведены настройки для перенаправления клиента на страницу host/err445, если запрос отправлен с IP-адреса, который зарегистрирован в стране, регионе или дата-центре из черного списка.

Конфигурационный файл NGINX

wallarm_block_page /err445 type=acl_source;

Аннотация Ingress

kubectl annotate ingress <INGRESS_NAME> nginx.ingress.kubernetes.io/wallarm-block-page="/err445 type=acl_source"

Именованный location NGINX

В примере приведены настройки для возвращения клиенту сообщения The page is blocked и кода ошибки 445 вне зависимости от причины блокировки запроса (режим блокировки, режим мягкой блокировки и источник из серого списка или источник из черного списка).

Конфигурационный файл NGINX

wallarm_block_page @block type=attack,acl_ip,acl_source;
location @block {
    return 445 'The page is blocked';
}

Аннотация Ingress

kubectl annotate ingress <INGRESS_NAME> nginx.ingress.kubernetes.io/server-snippet="location @block {return 445 'The page is blocked';}"
kubectl annotate ingress <INGRESS_NAME> nginx.ingress.kubernetes.io/wallarm-block-page="@block type=attack,acl_ip,acl_source"

Переменная и код ошибки

В зависимости от значения заголовка User-Agent, клиенту, который отправил запрос с одиночного IP-адреса или подсети из черого списка, возвращаются разные страницы блокировки:

  • По умолчанию — кастомизированная страницы-примера блокировки /usr/share/nginx/html/wallarm_blocked_renamed.html. Страница содержит переменные NGINX, поэтому ее необходимо инициализировать в директиве wallarm_block_page_add_dynamic_path.

  • Для пользователей Firefox — /usr/share/nginx/html/block_page_firefox.html (при установке Ingress‑контроллера рекомендуется создать отдельную директорию для примонтированных файлов, например, /usr/custom-block-pages/block_page_firefox.html):

    You are blocked!
    
    IP ${remote_addr}
    Blocked on ${time_iso8601}
    UUID ${request_id}
    

    Страница содержит переменные NGINX, поэтому ее необходимо инициализировать в директиве wallarm_block_page_add_dynamic_path.

  • Для пользователей Chrome — /usr/share/nginx/html/block_page_chrome.html (при установке Ingress‑контроллера рекомендуется создать отдельную директорию для примонтированных файлов, например, /usr/custom-block-pages/block_page_chrome.html):

    You are blocked!
    

    Страница не содержит переменные NGINX, поэтому ее не нужно инициализировать.

Также, вне зависимости от значения заголовка User-Agent, возвращается код 445.

Конфигурационный файл NGINX

wallarm_block_page_add_dynamic_path /usr/share/nginx/html/block_page_firefox.html /usr/share/nginx/html/wallarm_blocked_renamed.html;

map $http_user_agent $block_page {
  "~Firefox"  &/usr/share/nginx/html/block_page_firefox.html;
  "~Chrome"   &/usr/share/nginx/html/block_page_chrome.html;
  default     &/usr/share/nginx/html/wallarm_blocked_renamed.html;
}

wallarm_block_page $block_page response_code=445 type=acl_ip;
  • Чтобы применить настройку к Docker-контейнеру, необходимо примонтировать в контейнер конфигурационный файл NGINX с необходимыми настройками, а также страницы wallarm_blocked_renamed.html, block_page_firefox.html и block_page_chrome.html. Запуск контейнера с примонтированным конфигурационным файлом →

  • Чтобы применить настройку к sidecar‑контейнеру, необходимо передать директивы в ConfigMap Вебмониторэкс (инструкция для приложения, опубликованного с использованием Helm Charts или Kubernetes‑манифестов).

Ingress‑контроллер

  1. Передать в развернутый Helm‑чарт параметр controller.config.http-snippet, используя команду helm upgrade:

    helm upgrade --reuse-values --set controller.config.http-snippet='wallarm_block_page_add_dynamic_path /usr/custom-block-pages/block_page_firefox.html /usr/share/nginx/html/wallarm_blocked_renamed.html; map $http_user_agent $block_page { "~Firefox" &/usr/custom-block-pages/block_page_firefox.html; "~Chrome" &/usr/custom-block-pages/block_page_chrome.html; default &/usr/share/nginx/html/wallarm_blocked_renamed.html;}' <INGRESS_CONTROLLER_NAME> wallarm/wallarm-ingress -n <KUBERNETES_NAMESPACE>
    
  2. Создать ConfigMap из файлов wallarm_blocked_renamed.html, block_page_firefox.html и block_page_chrome.html.

  3. Примонтировать созданный ConfigMap в pod с Ingress‑контроллером Вебмониторэкс. Для этого необходимо изменить объект Deployment Ingress‑контроллера Вебмониторэкс по инструкции.

    Директория для монтирования ConfigMap

    Рекомендуется использовать отдельную директорию для файлов, примонтированных с помощью ConfigMap. При монтировании файлов в существующую директорию, предыдущие файлы в этой директории могут быть удалены.

  4. Добавить аннотацию к Ingress:

    kubectl annotate ingress <INGRESS_NAME> nginx.ingress.kubernetes.io/wallarm-block-page='$block_page response_code=445 type=acl_ip'