Перейти к основному содержимому

ADR: Доверенная среда передачи данных между компонентами Netgap

  • Status: Accepted by Andrei Ganiushkin andrey@ganyushkin.ru
  • Date: 2026-08-07
  • Authors: Andrei Ganiushkin andrey@ganyushkin.ru
  • Related issues: -

Context

Netgap реализует паттерн logical airgap и устанавливается на границе доверенного и недоверенного контуров (ADR 0008). Компоненты продукта (шлюзы и сервисы) взаимодействуют между собой по сети, в том числе через сегменты, которые по определению нельзя считать доверенными: трафик между сторонами логического разрыва проходит именно через ту границу, которую продукт призван защищать.

Требуется зафиксировать метод организации доверенной среды передачи данных внутри Netgap — между его собственными компонентами. Доверенная среда означает одновременное выполнение четырёх свойств для каждого внутреннего соединения:

  • конфиденциальность — трафик недоступен для чтения третьей стороне;
  • целостность — модификация трафика в канале обнаруживается и приводит к разрыву соединения;
  • взаимная аутентификация — каждая сторона криптографически доказывает свою подлинность до передачи первого байта прикладных данных; недостаточно аутентифицировать только сервер;
  • авторизация отправителя — принимающая сторона проверяет не только «сертификат подписан доверенным CA», но и «этот конкретный компонент имеет право обращаться к этому конкретному сервису».

Существенные ограничения:

  • модель угроз включает активного атакующего в сети между компонентами (MITM, спуфинг, несанкционированное подключение постороннего клиента к внутреннему API);
  • компоненты могут взаимодействовать по разным прикладным протоколам; свойства доверенной среды не должны зависеть от прикладного протокола;
  • поставка — статические бинарные файлы с минимальными зависимостями в целевой среде (ADR 0008): решения, требующие внешнего runtime, sidecar-прокси или агентов в ОС, противоречат модели поставки;
  • TLS-стек проекта — rustls (следствие ADR 0000: чистый Rust, без OpenSSL в поставке (ADR 0008));
  • источник сертификатов не ограничивается: заказчик вправе использовать публичный CA или собственный корпоративный УЦ; для разработки и тестовых стендов используется private PKI — см. методичку Работа с сертификатами.

Decision

Все коммуникации между компонентами Netgap выполняются исключительно по mTLS (TLS 1.3) независимо от прикладного протокола, со строгой проверкой идентичности отправителя по SPIFFE ID. Незащищённого внутреннего транспорта в продукте не существует — ни как режима по умолчанию, ни как опции конфигурации.

Правила, вытекающие из решения:

  1. mTLS обязателен и неотключаем. Каждое внутреннее соединение — TLS 1.3 с обязательным клиентским сертификатом. В коде отсутствует plaintext-режим и отсутствует опция «отключить проверку сертификата» (аналог insecure-skip-verify). Отсутствие валидного клиентского сертификата — разрыв соединения на этапе рукопожатия, до передачи прикладных данных.
  2. Версия протокола — только TLS 1.3. TLS 1.2 и ниже для внутренних соединений не поддерживаются. Наборы шифров — AEAD из TLS 1.3: TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256.
  3. Идентичность — SPIFFE ID. Каждый компонент получает сертификат, в SAN которого записан URI вида spiffe://<trust-domain>/<component>/<instance>. Принимающая сторона после успешной проверки цепочки извлекает SPIFFE ID из клиентского сертификата и сверяет его с явным списком разрешённых идентичностей для данного сервиса (deny by default). Проверка по CN и DNS-именам для авторизации не используется — см. Идентичность клиента.
  4. Проверка симметрична. Не только сервер авторизует клиента: клиент проверяет SPIFFE ID сервера и отказывается работать с сервером, чья идентичность не соответствует ожидаемой. Это исключает перенаправление трафика на подставной узел с любым «валидным» сертификатом того же CA.
  5. Алгоритмы ключей. Leaf-сертификаты компонентов — ECDSA P-256 или Ed25519; CA (корневой и промежуточный) — ECDSA P-384 или RSA 4096. RSA для leaf-сертификатов допускается (минимум 3072 бита) для совместимости с УЦ заказчика, но не рекомендуется. Компоненты обязаны принимать все перечисленные алгоритмы — выбор конкретного остаётся за PKI заказчика. Обоснование — в Ключах и алгоритмах.
  6. Требования к сертификатам. Обязательные расширения: SAN c URI (SPIFFE ID), EKU clientAuth и/или serverAuth по роли соединения, Basic Constraints: CA:FALSE. rustls строго проверяет EKU; сертификаты без нужного назначения отклоняются.
  7. Доверие — только явный Truststore. Корни доверия задаются в конфигурации компонента (ADR 0002); системное хранилище ОС не используется. Trust-домены разных инсталляций изолированы: компонент доверяет только корню своей PKI.
  8. Автоматизация выдачи и ротация. Компоненты поддерживают автоматизированный жизненный цикл сертификатов:
    • выпуск через внешние механизмы автоматизации (HashiCorp Vault PKI, cert-manager, корпоративный УЦ заказчика) — см. Автоматизацию выпуска;
    • горячую ротацию: компонент получает сигнал перезагрузки сертификата и подменяет их в памяти rustls без рестарта процесса и без разрыва уже установленных соединений — см. mTLS в Rust на rustls;
    • рекомендуемый срок жизни leaf-сертификатов — короткий (часы–дни), что делает ротацию штатной регулярной операцией, а не аварийной процедурой.
  9. Отзыв сертификатов. Компоненты поддерживают проверку отзыва по CRL: файл CRL задаётся в конфигурации, перечитывается при обновлении и применяется при проверке каждого рукопожатия. Основной эксплуатационной стратегией при автоматизированной выдаче являются короткоживущие сертификаты (компрометация истекает сама), CRL — обязательный механизм для немедленного отзыва и для инсталляций с длинными сроками жизни сертификатов. Классический OCSP-запрос «в момент рукопожатия» во внутренних контурах не применяется (требует доступности OCSP- респондера с узлов периметра) — см. Отзыв сертификатов.
  10. Единый транспортный слой. mTLS реализуется один раз, в общем транспортном слое, и используется всеми внутренними протоколами. Прикладной код получает от транспорта уже проверенную идентичность (SPIFFE ID) и не реализует собственных проверок TLS.
  11. События безопасности журналируются. Отклонённые рукопожатия (невалидная цепочка, отозванный сертификат, неразрешённый SPIFFE ID, истёкший срок) фиксируются в журнале с указанием причины и предъявленной идентичности — это события безопасности, а не отладочный шум.

Решение сознательно принимает рост сложности разработки и тестирования: любой стенд, включая локальный, требует развёрнутой PKI и выпущенных сертификатов; «быстро поднять два компонента без сертификатов» невозможно by design. Это цена бескомпромиссной защиты внутреннего трафика — подробно в разделе Consequences.

Considered Alternatives

Alternative 1: mTLS (TLS 1.3) со строгой проверкой SPIFFE ID для всех внутренних соединений

  • Pros:
    • Все четыре свойства доверенной среды обеспечиваются на транспортном уровне одним механизмом, одинаково для любого прикладного протокола.
    • Аутентификация криптографическая и взаимная: предъявление чужого сертификата бесполезно без приватного ключа (CertificateVerify в рукопожатии), подключение постороннего клиента отсекается до прикладного уровня.
    • SPIFFE ID даёт стандартизированную, независимую от сети идентичность: авторизация не привязана к IP-адресам и DNS-именам, переезд компонента не меняет политику доступа.
    • Опирается на стандартную PKI: заказчик использует собственный УЦ, продукт не навязывает инфраструктуру.
    • Реализуется средствами rustls без внешних зависимостей — совместимо с моделью поставки (ADR 0008).
    • Неотключаемость исключает класс инцидентов «защиту забыли включить» и упрощает аудит: не существует конфигурации продукта с незащищённым внутренним трафиком.
  • Cons:
    • Усложнение разработки и тестирования: каждый стенд требует PKI, выпуска и обновления сертификатов; отладка трафика требует ключей сессии вместо простого перехвата.
    • Эксплуатационная нагрузка: жизненный цикл сертификатов (выдача, ротация, отзыв) становится обязательной частью эксплуатации продукта.
    • Накладные расходы рукопожатия и шифрования (для TLS 1.3 с AEAD на современном CPU — незначительные).
  • Reason for selection: единственная альтернатива, обеспечивающая максимальный уровень защиты внутренних коммуникаций при активном атакующем в сети, без внешних зависимостей в целевой среде и без привязки доверия к топологии сети.

Alternative 2: TLS без клиентских сертификатов + аутентификация на прикладном уровне (токены, HMAC)

Сервер предъявляет сертификат, клиент аутентифицируется прикладным секретом (bearer-токен, подписанные запросы).

  • Pros:
    • Проще стенды разработки: клиентские сертификаты не нужны.
    • Привычная модель для HTTP API; секреты просто выпускать и передавать.
  • Cons:
    • Аутентификация несимметрична и происходит после установления канала: посторонний клиент получает TLS-соединение и поверхность прикладного парсера до проверки прав.
    • Bearer-токен передаётся в канале и воспроизводим при утечке; не привязан к соединению, в отличие от приватного ключа, который канал не покидает.
    • Каждый прикладной протокол реализует аутентификацию заново — множественные реализации одной критичной функции вместо одной проверенной.
    • Появляется отдельная система распространения и ротации секретов, эквивалентная по сложности PKI, но без её стандартизации.
  • Reason for rejection: более слабая модель (односторонняя криптографическая аутентификация, воспроизводимые секреты, поздняя проверка) при сопоставимой эксплуатационной сложности.

Alternative 3: Защита периметром — plaintext или обычный TLS внутри «доверенной» сети

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

  • Pros:
    • Минимальная сложность разработки, тестирования и эксплуатации.
    • Нулевые накладные расходы на криптографию внутри контура.
  • Cons:
    • Прямо противоречит модели угроз: компоненты Netgap стоят на границе недоверенного контура, их взаимный трафик проходит через защищаемую границу — «доверенной сети» между ними не существует по определению.
    • Один скомпрометированный узел сегмента получает неограниченный доступ ко всем внутренним API (lateral movement).
    • Безопасность продукта становится зависимой от корректности чужой сетевой конфигурации, которую продукт не контролирует и не может проверить.
  • Reason for rejection: несовместимо с назначением продукта; периметровая модель отвергнута в пользу zero-trust: каждое соединение аутентифицируется само, независимо от сети.

Alternative 4: Сетевой туннель уровня ОС (IPsec, WireGuard) между узлами

Шифрование и аутентификация выносятся на уровень ОС/сети; приложение работает поверх туннеля.

  • Pros:
    • Прозрачно для приложения: код компонентов не меняется.
    • WireGuard даёт простую и быструю криптографию; IPsec — сертифицированные реализации.
  • Cons:
    • Аутентифицируется узел, а не компонент: любой процесс на узле, включая скомпрометированный, получает доступ в туннель; авторизация «какой компонент к какому сервису» невозможна.
    • Требует настройки и сопровождения на уровне ОС вне продукта: права администратора, модули ядра/интерфейсы, разная конфигурация под Linux и Windows — ломается принцип самодостаточной поставки (ADR 0008).
    • Продукт не может проверить наличие туннеля: незащищённая инсталляция неотличима от защищённой с точки зрения самих компонентов.
  • Reason for rejection: идентичность на уровне узла вместо компонента и перенос гарантии безопасности из продукта во внешнюю инфраструктуру, которую продукт не контролирует.

Alternative 5: Service Mesh (Istio/Linkerd) — mTLS через sidecar-прокси

mTLS между компонентами терминируется sidecar-прокси, управление сертификатами — control plane меша.

  • Pros:
    • mTLS, SPIFFE-идентичности и ротация «из коробки», без кода в приложении.
    • Зрелые реализации, готовые политики авторизации.
  • Cons:
    • Требует Kubernetes/container runtime в целевой среде — недопустимая зависимость для узлов периметра и аттестованных контуров (ADR 0008).
    • Участок «приложение → локальный sidecar» остаётся незащищённым и не аутентифицированным.
    • Control plane меша становится критическим компонентом чужой поставки внутри границы доверия продукта; расширяется поверхность атаки и область аудита.
  • Reason for rejection: несовместимо с моделью поставки (статические бинарные файлы на «голой» машине); для продукта из фиксированного числа компонентов инфраструктура меша избыточна. Архитектурные идеи меша (SPIFFE ID, короткие TTL, горячая ротация) заимствованы в принятое решение без самого меша.

Alternative 6: mTLS как опция конфигурации (включаемый режим)

Тот же mTLS, но с возможностью отключения для разработки, отладки или «простых» инсталляций.

  • Pros:
    • Быстрые стенды разработки и демонстрации без PKI.
    • Упрощается первичное развёртывание у заказчика.
  • Cons:
    • Появляется класс инцидентов «в продакшене забыли включить»: незащищённая конфигурация становится валидным состоянием продукта.
    • Кодовая база и тесты обязаны поддерживать два транспортных режима; plaintext-ветка кода — постоянная цель для ошибок и даунгрейд-атак.
    • Гарантия «внутренний трафик всегда защищён» превращается из свойства продукта в свойство конкретной инсталляции — её нельзя заявлять в документации и при аудите.
  • Reason for rejection: безопасность по умолчанию с возможностью отключения на практике означает отсутствие гарантии. Стоимость PKI на стендах разработки принимается осознанно и компенсируется автоматизацией (скрипты private PKI из методички).

Consequences

Positive

  • Внутренний трафик Netgap защищён всегда и везде: конфиденциальность, целостность и взаимная аутентификация гарантируются самим продуктом, а не конфигурацией сети заказчика.
  • Посторонний процесс не может ни подключиться к внутреннему API, ни выдать себя за компонент: соединение отклоняется на рукопожатии, до прикладного парсера.
  • Авторизация по SPIFFE ID не зависит от IP-адресов и DNS: политика доступа переживает переезды, NAT и смену адресации; идентичность компонента едина во всех журналах и политиках.
  • Одна реализация транспортной безопасности на все прикладные протоколы: аудируется и тестируется один слой, а не каждый протокол отдельно.
  • Заказчик сохраняет контроль над PKI: публичный CA, корпоративный УЦ или Vault — продукт лишь предъявляет требования к содержимому сертификата.
  • Короткие TTL + горячая ротация делают компрометацию ключа ограниченной по времени, а замену сертификатов — фоновой операцией без остановки сервиса.
  • Отсутствие plaintext-режима упрощает аудит и сертификацию: незащищённой конфигурации не существует, проверять «включён ли TLS» не нужно.

Negative

  • Сложность разработки и тестирования — осознанная цена решения. Любой стенд, включая локальный запуск двух компонентов на машине разработчика, требует развёрнутой PKI и полного комплекта сертификатов с корректными SAN/EKU/SPIFFE ID. Интеграционные и e2e-тесты обязаны выпускать сертификаты в рамках подготовки окружения; появляется отдельный класс тестов на негативные сценарии PKI (истёкший, отозванный, чужой trust-домен, неверный EKU).
  • Отладка сетевых взаимодействий усложняется: трафик не читается перехватом; для анализа нужны ключи сессии (SSLKEYLOGFILE) или журналирование на концах соединения.
  • Эксплуатация получает обязательный процесс: мониторинг сроков действия, автоматизация выдачи, доставка CRL. Ошибки процесса (просроченный сертификат) приводят к отказу связи между компонентами — отказ безопасный, но это отказ.
  • Первичное развёртывание у заказчика дороже: до запуска нужно спланировать trust-домен, схему SPIFFE ID и источник сертификатов.
  • Рукопожатие TLS добавляет задержку установления соединения; смягчается пулами долгоживущих соединений.

Risks and Mitigations

  • Risk: просроченный сертификат останавливает связь между компонентами — эксплуатационный инцидент из-за процесса, а не атаки.
    • Mitigation: короткие TTL только вместе с автоматизированной выдачей; горячая ротация без рестарта; метрика/журналирование остатка срока действия и алёрт заведомо раньше истечения.
  • Risk: компрометация корневого или промежуточного CA делает возможным выпуск сертификатов для подставных компонентов.
    • Mitigation: авторизация по точному SPIFFE ID (deny by default) ограничивает ценность произвольного сертификата; поддержка CRL для немедленного отзыва; рекомендация заказчику — офлайн-корень и выделенный промежуточный CA для trust-домена Netgap.
  • Risk: кража приватного ключа компонента с узла.
    • Mitigation: короткий TTL ограничивает окно использования; отзыв через CRL; права доступа к файлу ключа — только процесс компонента; журналирование аномалий (один SPIFFE ID с неожиданных адресов).
  • Risk: разработчики обходят сложность стендов небезопасными самоделками (общий «вечный» сертификат, скопированный по проектам).
    • Mitigation: штатные скрипты развёртывания тестовой private PKI из методички (Собственная PKI) как путь наименьшего сопротивления; выпуск полного комплекта сертификатов — часть подготовки тестового окружения в CI.
  • Risk: УЦ заказчика не может выпустить сертификат с URI SAN (SPIFFE ID).
    • Mitigation: требования к сертификатам зафиксированы в документации поставки и проверяются компонентом при старте с внятной диагностикой; для таких случаев заказчику предлагается выделенный промежуточный CA под trust-домен Netgap.
  • Risk: ошибка в единственной реализации транспортного слоя затрагивает все внутренние коммуникации сразу.
    • Mitigation: транспортный слой опирается на rustls (без собственной криптографии); негативные mTLS-сценарии входят в обязательный набор тестов; обновления rustls отслеживаются через cargo audit.

References