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

Глава 5. Правила (Rules)

5.1. Структура правила: условие → действие

Терминология: В BlueCoat всё, что стоит после условий (triggers) в правиле, называется свойством (property). Отдельного понятия «действие» (action) в спецификации нет — allow, deny, force_deny описываются как «imperative gestures», но формально тоже являются свойствами. Термин «действие» используется в VPM (колонка «Action») и в документации Redcoat как удобное обозначение для подмножества свойств, управляющих решением по транзакции и поведением прокси. Блоки define action и свойство action.<name>(yes/no) — часть стандартного синтаксиса BlueCoat CPL.

Каждое правило занимает одну строку и состоит из нуля или более условий и нуля или более действий:

[условие1] [условие2] ... [действие1] [действие2] ...

Примеры:

; Одно условие, одно действие
url.domain=example.com deny

; Несколько условий, одно действие
url.domain=example.com http.method=POST deny

; Одно условие, несколько действий
category=Malware log(yes) deny

; Только действие (безусловное)
allow

; Только условие (scope opener — см. 5.4)
client.address=10.0.0.0/8

5.2. Множественные условия

AND (неявный): Несколько условий на одной строке объединяются логическим И. Все условия должны совпасть для срабатывания правила:

; Запрос должен быть из корпоративной сети И методом POST И к домену example.com
client.address=10.0.0.0/8 http.method=POST url.domain=example.com deny

OR (внутри значения): Список значений в скобках через запятую или || — логическое ИЛИ для одного условия:

; Любой из перечисленных методов
http.method=(GET, POST, PUT)

; Любой из перечисленных доменов
url.domain=(example.com || blocked.com)

; Любое из расширений
url.extension=(exe, msi, bat, cmd, ps1)

Важно: Явные ключевые слова AND и OR между условиями не поддерживаются — это отличие CPL от языков общего назначения. AND всегда неявный (пробел), OR — только внутри значений одного условия.

5.3. Отрицание условий

Отрицание инвертирует результат условия. Поддерживается два синтаксиса:

; Через !=
url.domain!=example.com deny                    ; все домены КРОМЕ example.com

; Через ! перед значением
url.domain=!example.com deny                    ; эквивалентно

; Отрицание с ссылкой на define
client.address=!corporate_net deny              ; все IP НЕ из define subnet

; Отрицание булевых условий
authenticated=no deny                           ; неаутентифицированные

5.4. Наследование области видимости (scope)

Строка, содержащая только условия без действий, создаёт область видимости (scope). Последующие строки с бо́льшим отступом наследуют эти условия:

<Proxy>
    client.address=10.0.0.0/8              ; scope opener
        url.domain=internal.corp allow     ; наследует client.address + свое условие
        url.path=/admin deny               ; наследует client.address + свое условие

    ; Отступ вернулся — scope завершён
    category=Malware deny                  ; независимое правило
    allow

Это эквивалентно:

<Proxy>
    client.address=10.0.0.0/8 url.domain=internal.corp allow
    client.address=10.0.0.0/8 url.path=/admin deny
    category=Malware deny
    allow

Guard слоя (условие в заголовке): Условия в заголовке слоя применяются ко всем правилам:

<Proxy client.address=10.0.0.0/8>
    url.domain=internal.corp allow     ; = client.address=10.0.0.0/8 url.domain=internal.corp allow
    deny                               ; = client.address=10.0.0.0/8 deny

Действие по умолчанию (в заголовке): Действие в заголовке слоя наследуется правилами без явного действия:

<Proxy deny>
    url.domain=example.com              ; наследует deny
    url.domain=trusted.com allow        ; явный allow переопределяет

5.5. Терминальные и нетерминальные действия

Примечание: Разделение на «терминальные» и «нетерминальные» действия — упрощение, принятое в документации Redcoat для удобства понимания. В спецификации BlueCoat такого деления нет: все элементы после условий являются свойствами (properties), а порядок их применения определяется приоритетами и правилами конфликтов между слоями (см. раздел 5.5.3 «Приоритеты при конфликтах»). Тем не менее, деление на терминальные и нетерминальные полезно для понимания того, какие свойства прекращают дальнейшую обработку запроса.

Действия (свойства) CPL делятся на две группы по влиянию на обработку запроса:

Терминальные — немедленно определяют итоговое решение по транзакции. После срабатывания терминального свойства обработка оставшихся слоёв прекращается (с одним исключением — см. deny ниже).

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

Терминальные действия

Действие Поведение Слой Exception
force_deny Немедленный запрет, обработка останавливается Пропускается
deny Запрет, но обработка передаётся в слой Exception Выполняется
redirect HTTP-перенаправление, обработка останавливается Пропускается
authenticate Запрос аутентификации (407/401), обработка останавливается Пропускается
rate_limit Превышен лимит запросов (429), обработка останавливается Пропускается
exception Показ страницы исключения, обработка останавливается Пропускается
force_exception Безусловная страница исключения Пропускается

Нетерминальные действия

Действие Назначение
allow Разрешение (транзакция продолжается к серверу)
forward, direct, socks_gateway Выбор маршрута upstream
ssl.forward_proxy, ssl.intercept Решение о TLS-перехвате
cache, bypass_cache, force_cache, ttl Управление кэшированием
set, delete, append (заголовки) Модификация заголовков
log, access_log, log.rewrite, log.suppress Управление логированием
label Метка правила
max_bitrate, bandwidth_class, priority Ограничение полосы
dns.server, dns.respond, dns.cache Управление DNS
trace, profile, debug.* Диагностика
virus_check, icap_service Антивирусная проверка
transform, inject_script, inject_css Трансформация контента

Все нетерминальные действия одного правила выполняются совместно. Например, правило может одновременно установить заголовок, включить логирование и выбрать upstream-маршрут:

<Proxy>
    url.domain=example.com set(request.header.X-Via, "proxy") log(yes) allow

Различие между deny и force_deny

Это ключевое различие при построении политик:

  • deny — запрещает транзакцию, но передаёт управление в слой <Exception>, где можно кастомизировать страницу ошибки. Exception-слой получает exception.id=policy_denied и может подставить собственную страницу.

  • force_deny — запрещает транзакцию немедленно, слой <Exception> пропускается. Используется для безусловной блокировки, когда кастомизация страницы ошибки не нужна или нежелательна.

; deny: Exception-слой может показать кастомную страницу
<Proxy>
    category=Gambling deny

<Exception>
    exception.id=policy_denied exception(custom_block_page)

; force_deny: Exception-слой НЕ вызывается
<Proxy>
    category=Malware force_deny

Приоритеты при конфликтах

Когда несколько правил из разных слоёв возвращают противоречащие терминальные действия, применяется следующий приоритет:

Приоритет Действие Описание
Высший force_deny Безусловный запрет, не может быть переопределён
force_exception Безусловная страница ошибки
authenticate.force Принудительная аутентификация (выше deny)
Средний deny Запрет (может быть переопределён allow)
exception Страница ошибки
Низший allow Разрешение
authenticate Запрос аутентификации (ниже deny)

Важное правило: authenticate имеет приоритет ниже deny. Это означает, что если правило содержит и authenticate(realm), и deny, то deny выполнится без запроса аутентификации. Для обратного поведения используйте authenticate.force(realm):

<Proxy>
    ; deny выполнится БЕЗ запроса аутентификации:
    url.domain=blocked.com authenticate(LDAP) deny

    ; authenticate.force выполнится ПЕРЕД deny:
    url.domain=restricted.com authenticate.force(LDAP) deny

Первое совпавшее правило

Внутри каждого слоя правила обрабатываются сверху вниз. Как только найдено первое совпавшее правило, все его действия выполняются, а оставшиеся правила в этом слое пропускаются:

<Proxy>
    url.domain=example.com log(yes) allow     ; Правило 1
    url.domain=example.com deny               ; Правило 2 — НЕ будет оценено,
                                              ; т.к. Правило 1 уже совпало
    deny                                      ; Правило 3

Если в правиле несколько действий, все они выполняются:

<Proxy>
    ; Все три действия выполнятся для одного совпавшего правила:
    url.domain=api.corp set(request.header.X-Internal, "true") log(yes) allow

5.6. Аутентификация и запрет доступа

Одна из наиболее важных взаимосвязей в CPL — это соотношение между аутентификацией (authenticate) и запретом доступа (deny). Запрет может быть выполнен как до, так и после аутентификации, и разные организации имеют разные требования к этому.

Deny имеет приоритет над authenticate

Действие deny имеет приоритет 10, а authenticate(realm) — приоритет 20 (ниже). Это означает, что если правило содержит оба действия, deny выполняется без запроса аутентификации:

define subnet corporate_subnet
    10.10.12.0/24
end

<Proxy>
    client.address=!corporate_subnet deny    ; блокировка без аутентификации
    authenticate(MyRealm)                     ; authenticate ниже по приоритету

В этой политике запросы извне корпоративной подсети будут отклонены, а пользователи внутри подсети — аутентифицированы. Однако имя пользователя не будет доступно в access log для отклонённых запросов, так как аутентификация не выполнялась.

Примечание: Категории контента определяются из URL запроса и могут быть определены до аутентификации. Поэтому deny с проверкой категории отклоняет запрос до запроса учётных данных.

authenticate.force имеет приоритет над deny

Действие authenticate.force(realm) имеет приоритет 5выше deny (10). Это позволяет сначала аутентифицировать пользователя, а затем отклонить запрос. Имя пользователя будет доступно в access log:

define subnet corporate_subnet
    10.10.12.0/24
end

<Proxy>
    client.address=!corporate_subnet deny    ; блокировка
    authenticate.force(MyRealm)               ; но СНАЧАЛА аутентификация (приоритет 5 > deny 10)

<Proxy>
    ; Имена пользователей будут видны в access log для отклонённых запросов
    category=Gambling exception(content_filter_denied)

Полная таблица приоритетов

Действия в одном правиле выполняются в порядке их приоритетов (от наименьшего числового значения к наибольшему):

Приоритет Действие Описание
0 force_deny Безусловный запрет, Exception-слой пропускается
1 force_exception Безусловная страница ошибки
5 authenticate.force(realm) Принудительная аутентификация (выше deny!)
10 deny Запрет, Exception-слой вызывается
11 deny.unauthorized Запрет с кодом 401
12 exception(id) Страница ошибки
19 allow Разрешение
20 authenticate(realm) Запрос аутентификации (ниже deny!)
30-39 SSL/TLS действия ssl.intercept, ssl.forward_proxy и др.
40-49 Forwarding forward, direct, socks_gateway
50-59 Кэширование cache, force_cache, bypass_cache, ttl
60-69 Модификация redirect, rewrite, заголовки
70-79 QoS rate_limit, max_bitrate, bandwidth_class
80-89 DNS dns.respond, dns.server и др.
90-99 Логирование log, access_log, label, trace

Взаимодействие deny и Exception-слоя

Действие deny не прекращает обработку немедленно. Вместо этого оно сохраняется как отложенный запрет (pending deny), и управление передаётся в слой <Exception>. Это позволяет кастомизировать страницу ошибки:

<Proxy>
    category=Gambling deny

<Exception>
    ; Эта секция вызывается ТОЛЬКО если был pending deny
    ; Условие exception.id=policy_denied устанавливается автоматически
    exception.id=policy_denied exception(custom_gambling_block_page)

Действие force_deny прекращает обработку немедленно — слой <Exception> пропускается, показывается стандартная страница ошибки.

SOCKS аутентификация

Для протокола SOCKS аутентификация происходит при установлении соединения (до получения запроса). Поэтому идентификационная информация пользователя доступна до оценки категории контента:

define subnet corporate_subnet
    10.10.12.0/24
end

<Proxy>
    client.address=!corporate_subnet deny
    socks.authenticate(MyRealm)              ; SOCKS auth до категоризации

<Proxy>
    ; Имена пользователей доступны для SOCKS-аутентифицированных
    category=Gambling exception(content_filter_denied)

Примечание: Это поведение корректно только для SOCKS-аутентифицированных пользователей.

authenticate() как нетерминальное действие

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

Если следующее правило содержит deny — deny побеждает, пользователь отклоняется без запроса аутентификации. Если следующие правила содержат только allow или нетерминальные действия — возвращается запрос аутентификации (пользователь видит требование ввести учётные данные).

<Proxy>
    ; Правило 1: authenticate — сохраняется как pending auth
    authenticate(LDAP_Realm)

    ; Правило 2: deny для категории — побеждает pending auth
    ; Результат: deny БЕЗ запроса аутентификации
    category=Malware deny

    ; Правило 3: allow — для остальных возвращается отложенная аутентификация (запрос учётных данных)
    allow

Для пользователя, запрашивающего Malware-сайт: deny (без 407). Для остального: 407 Proxy-Authenticate (запрос учётных данных).

Приоритет deny > authenticate. Это поведение — общее правило: deny всегда приоритетнее authenticate(), независимо от порядка слоёв. Чтобы запрос аутентификации выдавался ПЕРЕД deny (и имя пользователя логировалось для отклонённых запросов), используйте authenticate.force(realm) — его приоритет выше deny.

Тип запроса аутентификации определяется режимом аутентификации, а не политикой: proxy* → 407 (Proxy-Authenticate / ответ Proxy-Authorization); origin* → 401 (WWW-Authenticate / ответ Authorization, как у исходного сервера); form* → перенаправление 302.

Для уже аутентифицированного пользователя правило authenticate(realm) пропускается (если пользователь уже аутентифицирован в этом или совместимом realm).

Конфликтующие действия

Хотя правила в файле политики могут устанавливать одно и то же свойство неоднократно, конкретные действия могут конфликтовать:

  • Если блок define action содержит два конфликтующих действия, это ошибка времени компиляции.
  • Если два разных блока define action выполняются и содержат конфликтующие действия, ошибка фиксируется в event log, и одно действие выбирается произвольно.

Потенциальные конфликты:

Конфликт Пример
Противоречивые значения cache(yes) cache(no)
Взаимоисключающие forward(proxy:3128) direct
Неэффективная комбинация cache(yes) bypass_cache

Построение политики безопасности (Blacklist vs Whitelist)

Whitelist подход (рекомендуемый для безопасности) — по умолчанию всё запрещено, разрешается только явно указанное:

define subnet corporate_subnet
    10.10.12.0/24
end

; 1. Разрешить доступ корпоративным пользователям
<Proxy>
    client.address=corporate_subnet allow

; 2. Требовать аутентификацию
<Proxy>
    authenticate(corp_realm)

; 3. Фильтрация нежелательного контента
<Proxy>
    url.domain=forbidden.com deny
    category=(Gambling, Hacking, Chat) deny
    ; ... дополнительные слои

Blacklist подход — по умолчанию всё разрешено, блокируется только явно указанное:

<Proxy>
    category=(Malware, Phishing) deny
    url.domain=blocked-site.com deny
    allow

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

Исключения из общих правил

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

Через порядок правил внутри слоя — наиболее специфические правила (исключения) располагаются первыми, общее правило — последним:

<Proxy>
    ; Исключение: HR-персонал имеет доступ к payroll
    condition=payroll_location group=HR_staff allow
    ; Общее правило: payroll закрыт для всех
    condition=payroll_location deny

Через порядок слоёв — общие правила в одном слое, исключения в следующем:

; Общее правило
<Proxy>
    condition=payroll_location deny

; Исключение (в следующем слое — имеет приоритет)
<Proxy>
    condition=payroll_location group=HR_staff allow

Гарантированный запрет

Действие deny может быть переопределено действием allow в последующем слое. Для гарантированного запрета, который не может быть переопределён, используйте force_deny или force_exception:

; Этот запрет гарантирован — никакие последующие allow его не отменят
<Proxy>
    client.address=!corporate_subnet force_deny