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

Глава 28. SSL/TLS действия

Действия SSL/TLS управляют перехватом TLS-трафика (MITM), параметрами соединения, проверкой сертификатов и контролем протоколов поверх TLS. Доступны в слоях <SSL-Intercept> и <SSL>.

Аналоги BlueCoat: ssl.forward_proxy(), detect_protocol() , server.certificate.validate() .

28.1. ssl.forward_proxy(yes/no) — SSL forward proxy

Включает или отключает TLS-инспекцию (MITM) для совпавшего запроса. При yes прокси расшифровывает TLS-трафик, инспектирует содержимое и перешифровывает с использованием динамически сгенерированного сертификата (forged certificate). При no запрос туннелируется без расшифровки (TCP-level forwarding).

Синтаксис:

ssl.forward_proxy(yes|no)

where: yes — перехватить (MITM); no — туннелировать без инспекции.

Тип: Нетерминальное. Обработка продолжается к следующим слоям.

Слои и транзакции: SSL-Intercept, SSL. Применяется к CONNECT-транзакциям и transparent TLS-соединениям.

Пример:

<SSL-Intercept>
    ; По умолчанию — перехватывать
    ssl.forward_proxy(yes)

    ; Исключения: финансы и pinned-сертификаты
    url.domain=*.bank.com ssl.forward_proxy(no)
    category="Financial Services" ssl.forward_proxy(no)

См. также: ssl.intercept, Глава 15. Условия SSL/TLS


28.2. ssl.intercept(yes/no) — перехват TLS

Синоним ssl.forward_proxy. Управляет MITM-перехватом. Предпочтительная форма для слоя <SSL-Intercept>.

Синтаксис:

ssl.intercept(yes|no)

where: yes — перехватить; no — пропустить без инспекции.

Тип: Нетерминальное.

Слои и транзакции: SSL-Intercept. Применяется к CONNECT-транзакциям и transparent TLS-соединениям.

Примечание: Для работы TLS-инспекции необходима настройка inspection_enabled = true в proxy.toml.

Пример:

<SSL-Intercept>
    ssl.intercept(yes)
    category="Health and Medicine" ssl.intercept(no)

См. также: ssl.forward_proxy


28.3. server.certificate.validate(yes|no) — верификация сертификата сервера

Управляет проверкой сертификата upstream-сервера при TLS-соединении. При no прокси не проверяет валидность, срок действия и цепочку доверия сертификата сервера. Используется для внутренних серверов с self-signed сертификатами. Имя как в BlueCoat.

Синтаксис:

server.certificate.validate(yes|no)

where: yes — проверять сертификат (поведение по умолчанию); no — не проверять.

Тип: Нетерминальное.

Слои и транзакции: <SSL>. Применяется ко всем HTTPS-транзакциям.

Пример:

<SSL>
    server.certificate.validate(yes)
    url.domain=*.internal.corp server.certificate.validate(no)

См. также: client.certificate.validate


28.4. client.certificate.validate(yes|no|require) — верификация клиента

Запрашивает или требует клиентский сертификат для mTLS-аутентификации. Имя и значения yes|no как в BlueCoat; require — расширение Redcoat (TLS handshake завершается ошибкой, если клиент не предоставил сертификат).

Синтаксис:

client.certificate.validate(yes|no|require)

where: yes — запросить сертификат (необязательно); no — не запрашивать; require — требовать (отказ без сертификата, расширение Redcoat).

Тип: Нетерминальное.

Слои и транзакции: <SSL>, <SSL-Intercept>. Применяется к входящим TLS-соединениям.

Пример:

<SSL>
    ; Требовать клиентский сертификат для админки
    url.path=/admin client.certificate.validate(require)

    ; Запросить, но не требовать для остальных
    client.certificate.validate(yes)

Полный сценарий mTLS через SSL-перехват (каноническая форма)

Это эталонная политика «требовать валидный клиентский сертификат для всего HTTPS-трафика». Поведение: валидный сертификат → 200, без сертификата → 403.

<SSL-Intercept>
    ssl.intercept(yes)
    client.certificate.validate(require)

<Proxy>
    client.certificate.presented=no deny
    allow

Вариант с проверкой Common Name (авторизация по атрибуту сертификата):

<SSL-Intercept>
    ssl.intercept(yes)
    client.certificate.validate(yes)

<Proxy>
    client.certificate.common_name="trusted-client" allow
    deny

Имена условий: CN проверяется через client.certificate.common_name= (каноническое имя как в BlueCoat); client.certificate.subject.cn= принимается как алиас на то же условие. OU — через client.certificate.subject.ou=.

Почему именно в этих слоях (важные нюансы):

Слой Фаза Что доступно Семантика правил Назначение
<SSL-Intercept> до TLS handshake только SNI + IP клиента/сервера; клиентского сертификата ещё нет (TLS не расшифрован) аккумуляция — применяются ВСЕ совпавшие правила решение о перехвате (ssl.intercept) + требование сертификата (client.certificate.validate)
<Proxy> / <SSL> после MITM-handshake, на расшифрованном запросе полный URL, заголовки, клиентский сертификат предъявлен выигрывает первое совпадение — побеждает первое совпавшее правило проверка наличия/атрибутов сертификата (client.certificate.presented, ...subject.cn и т.п.)
  1. <SSL-Intercept> — независимые настройки, поэтому их можно (и нужно) задавать отдельными строками. ssl.intercept(yes) и client.certificate.validate(require) — две разные настройки конфигурации TLS-перехвата, и обе обязаны примениться. Этот слой аккумулирует решения всех совпавших правил (в отличие от <Proxy>).

  2. Проверку сертификата размещают в <Proxy> или <SSL>, НЕ в <SSL-Intercept>. В <SSL-Intercept> сертификата физически ещё нет (handshake не выполнен) — условие client.certificate.presented там всегда ложно. Сертификат становится доступен только после MITM-handshake, на расшифрованном внутреннем запросе. Для CONNECT-трафика движок специально откладывает решение <Proxy> до этого момента.

  3. В <Proxy> порядок правил важен (выигрывает первое совпадение): защитное правило (client.certificate.presented=no deny) всегда ставится ПЕРЕД завершающим allow. Если поменять местами — allow сработает первым и сертификат проверяться не будет.

  4. Глубокая защита (несколько уровней): client.certificate.validate(require) принуждает на уровне TLS (handshake завершается ошибкой без сертификата), а client.certificate.presented=no deny — на уровне доступа (отдаёт корректный 403 с блок-страницей). Достаточно одного механизма, но вместе они надёжнее; форма с явным deny даёт более понятный для клиента ответ, чем сырой обрыв TLS.

Типичная ошибка. Если записать обе настройки <SSL-Intercept> так, чтобы перехват «терялся», соединение туннелируется без MITM и требование сертификата молча не применяется. В канонической форме выше ssl.intercept(yes) указан явно и применяется всегда.

Отзыв (revocation) клиентского сертификата — check_revocation()

Проверка отзыва клиентского сертификата (CRL по CDP-URL из сертификата + локальные CRL-файлы; OCSP) реализована и принудительно применяется при проверке клиентских сертификатов (config: crl_check + crl_files). Поведение: отозванный сертификат → отказ; при включённой проверке без доступного CRL → безопасный отказ (блокировка).

CPL-свойство client.certificate.validate.check_revocation(auto|ocsp|local|no) поддерживается и переопределяет режим отзыва per-rule поверх дефолта cert-realm:

<SSL>
    client.certificate.validate(yes) client.certificate.validate.check_revocation(ocsp)
    ; для доверенного домена — без проверки отзыва
    url.domain=internal.lan client.certificate.validate.check_revocation(no)
Режим Эффект
no пропустить проверку отзыва для совпавшего трафика
ocsp проверять через OCSP
local локальные CRL-файлы + CDP-URL из сертификата
auto проверять обоими способами (отзыв любым → отказ)

Применение: проверка отзыва выполняется только при перехвате (MITM) — на расшифрованном клиентском сертификате, по всей цепочке сертификатов.

CCL (выбор списка CA) — ccl()

CPL-свойство client.certificate.validate.ccl(ccl_id) (выбор именованного CA-списка для отдельного правила) пока не поддерживается (политика с ним отклоняется — безопасный отказ). Требуется реестр именованных CCL (ccl_id → набор CA), которого ещё нет. Сейчас проверка клиентского сертификата использует единственный набор CA realm'а. Это сознательный безопасный отказ (а не молчаливое бездействие), чтобы политика не «делала вид».

См. также: ssl.intercept, client.certificate.presented=, server.certificate.validate, Глава 12. Условия аутентификации


28.5. ssl.client_certificate(cert) — клиентский сертификат

Статус: планируется. Использование в CPL файле приведёт к ошибке парсинга.

Указывает именованный сертификат, используемый прокси для mTLS-соединения с upstream-сервером (client certificate presentation).

Синтаксис:

ssl.client_certificate(<certificate_name>)

where: certificate_name — имя сертификата из keyring прокси.

Тип: Нетерминальное.

Слои и транзакции: SSL. Применяется к исходящим HTTPS-соединениям.

Пример:

<SSL>
    url.domain=api.partner.com ssl.client_certificate(partner_cert)

См. также: ssl.verify_client


28.6. ssl.cipher_suites(suite1, suite2, ...) — наборы шифров

Ограничивает допустимые наборы шифров для TLS-соединения. Поддерживаются имена наборов, группы (gost, gost_modern) и hex-идентификаторы (0xC100).

Синтаксис:

ssl.cipher_suites(<suite>[, <suite>]*)

where: suite — имя набора шифров (например, TLS_AES_256_GCM_SHA384), группа (gost) или hex ID (0xC100).

Тип: Нетерминальное.

Слои и транзакции: SSL. Применяется ко всем TLS-соединениям.

Пример:

<SSL>
    ssl.cipher_suites(TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256)

См. также: ssl.min_version / ssl.max_version


28.7. ssl.version(version1, version2, ...) — допустимые версии TLS

Указывает список допустимых версий протокола TLS. Запросы с другими версиями будут отклонены.

Синтаксис:

ssl.version(<version>[, <version>]*)

where: version — версия TLS (например, TLSv1.2, TLSv1.3).

Тип: Нетерминальное.

Слои и транзакции: SSL. Применяется ко всем TLS-соединениям.

Пример:

<SSL>
    ssl.version(TLSv1.2, TLSv1.3)

См. также: ssl.min_version / ssl.max_version


28.8. {server,client}.connection.min_ssl_version / max_ssl_version — версии TLS

Устанавливают минимальную и максимальную допустимую версию TLS. Есть две независимые пары — для каждой стороны MITM-перехвата (имена как в BlueCoat):

  • server.connection.* — соединение с upstream-сервером (применяется к upstream-коннектору).
  • client.connection.* — соединение с клиентом (применяется к MITM-acceptor'у).

Синтаксис:

server.connection.min_ssl_version(version)
server.connection.max_ssl_version(version)
client.connection.min_ssl_version(version)
client.connection.max_ssl_version(version)

where: versionpreserve, sslv3, tlsv1, tlsv1.1, tlsv1.2, tlsv1.3. Значение preserve (по умолчанию в BlueCoat) означает «не ограничивать, сохранить версию, предложенную пиром» — в Redcoat при preserve ограничение НЕ применяется.

Тип: Нетерминальное.

Слои и транзакции: <SSL-Intercept> / <SSL>. Серверная пара — к upstream TLS; клиентская — к клиентскому (MITM) TLS.

Пример:

<SSL-Intercept>
    ; upstream: только TLS 1.2–1.3
    server.connection.min_ssl_version(tlsv1.2)
    server.connection.max_ssl_version(tlsv1.3)
    ; клиент: не принимать ниже TLS 1.2 для конкретного хоста
    client.connection.ssl_server_name=www.example.com client.connection.min_ssl_version(tlsv1.2)

Реализация в Redcoat: обе стороны реализованы. Серверная → CplDecision::SslMin/MaxVersion → upstream-коннектор; клиентская → CplDecision::ClientSslMin/MaxVersion → MITM-acceptor (apply_client_version, preserve пропускается).

См. также: ssl.cipher_suites


28.9. http2.client.accept(yes|no|preserve) — управление HTTP/2 на клиентском соединении

Определяет, принимает ли прокси HTTP/2 от клиента. no → клиент работает по HTTP/1.1. Имя и значения как в BlueCoat.

Синтаксис:

http2.client.accept(yes|no|preserve)

where:

  • yes — принимать HTTP/2 от клиента (поведение по умолчанию);
  • no — HTTP/2-запросы клиента обрабатываются как HTTP/1.1 (понижение версии);
  • preserve — если сервер поддерживает HTTP/2, клиентское соединение апгрейдится до HTTP/2; эквивалентно yes, когда поддержку сервера определить нельзя.

Что реально делает: при no прокси убирает h2 из ALPN (и на стороне клиента, и на upstream-соединении) → согласуется HTTP/1.1 (понижение версии). yes/preserve оставляют h2 в ALPN.

Тип: Нетерминальное.

Слой <SSL-Intercept>, решение по SNI; применяется там, где прокси терминирует клиентский TLS

ALPN согласуется в TLS-рукопожатии (до того как известен URL), поэтому правило размещается в слое <SSL-Intercept> и гейтится по client.connection.ssl_server_name= (SNI) или client.address=. В слое <SSL> (после рукопожатия) — не повлияет. Где работает: при SSL-инспекции (MITM) — да; на сквозном CONNECT-туннеле без инспекции — нет (прокси не в TLS, ALPN согласуется между клиентом и сервером назначения напрямую). Поддержка терминирующих слушающих сокетов без MITM (reverse / forward-HTTPS) пока в работе.

Пример:

<SSL-Intercept>
    ; Принудить клиентов example.com к HTTP/1.1
    client.connection.ssl_server_name=example.com http2.client.accept(no)

См. также: ssl.quic, ssl.ech


28.10. ssl.quic(action) — управление QUIC/HTTP3

Статус: в процессе реализации. Принимается парсером, но пока не применяется — управление QUIC для отдельных правил ещё не подключено.

Управляет QUIC-соединениями (HTTP/3).

Синтаксис:

ssl.quic(inspect|allow|bypass|block|deny|log)

where: значения: inspect|allow|bypass|block|deny|log.

Тип: Нетерминальное.

Слои и транзакции: SSL. Применяется к UDP-транзакциям QUIC.

Пример:

<SSL>
    ssl.quic(deny)                         ; заблокировать QUIC, принудить к HTTP/2

См. также: http2.client.accept


28.11. ssl.ech(action) — управление Encrypted Client Hello

Статус: в процессе реализации. Принимается парсером, но пока не применяется — управление ECH для отдельных правил ещё не подключено.

Управляет ECH (Encrypted Client Hello), расширением TLS, скрывающим SNI.

Синтаксис:

ssl.ech(inspect|allow|bypass|block|deny|log)

where: значения: inspect|allow|bypass|block|deny|log.

Тип: Нетерминальное.

Слои и транзакции: SSL, SSL-Intercept. Применяется к TLS-соединениям с расширением ECH.

Пример:

<SSL>
    ssl.ech(block)                         ; заблокировать ECH (принудить к открытому SNI)

См. также: ssl.dns_over_tls


28.12. ssl.dns_over_tls(action) — управление DNS over TLS

Управляет DNS over TLS (DoT) соединениями на порту 853.

Синтаксис:

ssl.dns_over_tls(inspect|allow|bypass|block|deny|log)

where: значения: inspect|allow|bypass|block|deny|log.

Тип: Нетерминальное.

Слои и транзакции: SSL. Применяется к TLS-соединениям на порту 853.

Пример:

<SSL>
    ssl.dns_over_tls(deny)                 ; заблокировать DoT (принудить к обычному DNS)

См. также: Глава 31. Управление DNS


28.13. Гранулярный контроль сертификатов (расширения Redcoat)

Расширения Redcoat. В BlueCoat валидация сертификата сервера — это один коарс-переключатель server.certificate.validate(yes|no) (он покрывает trust chain, срок действия, well-formedness разом). Redcoat добавляет гранулярные переключатели — блокировать только по конкретной причине. Нетерминальные, слой <SSL-Intercept>/<SSL>.

Действие Описание
ssl.block_expired_cert(yes\|no) блокировать серверы с истёкшим сертификатом
ssl.block_invalid_cert(yes\|no) блокировать серверы с невалидным сертификатом
ssl.block_weak_cert(yes\|no) блокировать серверы со слабым сертификатом
ssl.min_key_size(bits) минимальный размер ключа сертификата (BlueCoat регулирует силу через cipher-suite strength)

Ещё в процессе (пока не поддерживаются в политике): ssl.subordinate_ca(ca_name) (BlueCoat: ssl.forward_proxy.issuer_keyring()), ssl.allowed_sig_algorithms=(...), ssl.client_certificate(...).

Пример комплексной SSL-политики:

<SSL-Intercept>
    ; По умолчанию — перехватывать
    ssl.forward_proxy(yes)

    ; Исключения: финансы, медицина, pinned-сертификаты
    url.domain=*.bank.com ssl.forward_proxy(no)
    category="Financial Services" ssl.forward_proxy(no)
    category="Health and Medicine" ssl.forward_proxy(no)

<SSL>
    ; Запрет устаревших версий TLS
    client.connection.negotiated_ssl_version=TLSv1.0 deny
    client.connection.negotiated_ssl_version=TLSv1.1 deny

    ; Минимальная версия TLS
    ssl.min_version(TLSv1.2)

    ; Блокировка QUIC, принуждение к HTTP/2
    ssl.quic(deny)
    http2.client.accept(yes)

    ; Блокировка ECH и DoT
    ssl.ech(block)
    ssl.dns_over_tls(deny)